From d1887280d99823a55be887d635c1c4b2a9b89785 Mon Sep 17 00:00:00 2001 From: patroswastik Date: Thu, 27 Feb 2025 16:19:31 -0600 Subject: [PATCH 01/56] add specific check for @deprecated tag in router files Signed-off-by: patroswastik --- .../lint-legacy-backend-exports.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts b/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts index 6025defe31..0ebc7088e3 100644 --- a/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts +++ b/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts @@ -46,10 +46,15 @@ function verifyIndex(pkg: string, packageJson?: BackstagePackageJson) { console.log(`Verifying ${pkg}`); const tsPath = path.join(pkg, 'src/index.ts'); const sourceFile = project.getSourceFile(tsPath); + + const tsRouterPath = path.join(pkg, 'src/service/router.ts'); + const routerFile = project.getSourceFile(tsRouterPath); + if (!sourceFile) { console.log(`Could not find ${tsPath}`); process.exit(1); } + const symbols = sourceFile?.getExportSymbols(); const exportCount = symbols?.length || 0; @@ -75,9 +80,23 @@ function verifyIndex(pkg: string, packageJson?: BackstagePackageJson) { .find(tag => tag.getName() === 'deprecated'); } + let routerCreateRouterDeprecated = undefined; + if (routerFile) { + const routerSymbols = routerFile?.getExportSymbols(); + const routerCreateRouterExport = routerSymbols?.find( + symbol => symbol.getName() === 'createRouter', + ); + + if (routerCreateRouterExport) { + routerCreateRouterDeprecated = routerCreateRouterExport + .getJsDocTags() + .find(tag => tag.getName() === 'deprecated'); + } + } + if (createRouterExport) { console.log(' ❌ createRouter is exported'); - if (!createRouterDeprecated) + if (!createRouterDeprecated && !routerCreateRouterDeprecated) console.log(' ❌ createRouter is NOT deprecated'); } From 18ce51c5b6d88720d131dbdbb022ce6bec4506cb Mon Sep 17 00:00:00 2001 From: patroswastik Date: Thu, 27 Feb 2025 17:20:24 -0600 Subject: [PATCH 02/56] Changeset added as per guide Signed-off-by: patroswastik --- .changeset/rich-phones-whisper.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rich-phones-whisper.md diff --git a/.changeset/rich-phones-whisper.md b/.changeset/rich-phones-whisper.md new file mode 100644 index 0000000000..c27b6de2f8 --- /dev/null +++ b/.changeset/rich-phones-whisper.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': minor +--- + +Checking through router.ts files of the packages if they exist and looking for deprecated tag inside it. If exists then only the message will appear From 9f2170f9e33bfb4527c3f37b678722461641f45f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Mar 2025 18:34:01 +0000 Subject: [PATCH 03/56] chore(deps): update docker/build-push-action action to v6.15.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/deploy_docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index f2ff2f3017..05d44f8079 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -62,7 +62,7 @@ jobs: uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0 - name: Build and push - uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0 + uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 with: context: './example-app' file: ./example-app/packages/backend/Dockerfile From df32cef7ffed42a8218c22c123fbf96621fc9957 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:15:49 +0000 Subject: [PATCH 04/56] chore(deps): update actions/upload-artifact action to v4.6.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/pr-review-comment-trigger.yaml | 2 +- .github/workflows/scorecard.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-review-comment-trigger.yaml b/.github/workflows/pr-review-comment-trigger.yaml index 61252e7567..e2bdb4c1cb 100644 --- a/.github/workflows/pr-review-comment-trigger.yaml +++ b/.github/workflows/pr-review-comment-trigger.yaml @@ -30,7 +30,7 @@ jobs: run: | mkdir -p ./pr echo $PR_NUMBER > ./pr/pr_number - - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: pr_number-${{ github.event.pull_request.number }} path: pr/ diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 4a5707087e..929bfbab75 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -58,7 +58,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: 'Upload artifact' - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: SARIF file path: results.sarif From 68b49e34330132daf44f1aeb97b4bb7deba349f4 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 4 Apr 2025 08:02:41 +0100 Subject: [PATCH 05/56] First pass at pagination Signed-off-by: Charles de Dreuille --- packages/canon/package.json | 1 + .../src/components/DataTable/DataTable.tsx | 20 + .../canon/src/components/DataTable/index.ts | 17 + .../src/components/Table/Table.stories.tsx | 184 ++-- packages/canon/src/components/Table/Table.tsx | 34 +- packages/canon/src/components/Table/index.ts | 12 +- .../components/Table/mocked-data/columns.tsx | 71 ++ .../Table/mocked-data/components.ts | 818 ++++++++++++++++++ .../canon/src/components/Table/styles.css | 10 + packages/canon/src/components/Table/types.ts | 50 ++ .../TablePagination/TablePagination.tsx | 81 ++ .../src/components/TablePagination/index.ts | 18 + .../src/components/TablePagination/types.ts | 50 ++ yarn.lock | 20 + 14 files changed, 1265 insertions(+), 121 deletions(-) create mode 100644 packages/canon/src/components/DataTable/DataTable.tsx create mode 100644 packages/canon/src/components/DataTable/index.ts create mode 100644 packages/canon/src/components/Table/mocked-data/columns.tsx create mode 100644 packages/canon/src/components/Table/mocked-data/components.ts create mode 100644 packages/canon/src/components/Table/types.ts create mode 100644 packages/canon/src/components/TablePagination/TablePagination.tsx create mode 100644 packages/canon/src/components/TablePagination/index.ts create mode 100644 packages/canon/src/components/TablePagination/types.ts diff --git a/packages/canon/package.json b/packages/canon/package.json index b5d2d715f6..fc47bd89d4 100644 --- a/packages/canon/package.json +++ b/packages/canon/package.json @@ -56,6 +56,7 @@ "@storybook/react": "^8.6.8", "@storybook/react-webpack5": "^8.6.8", "@storybook/test": "^8.6.8", + "@tanstack/react-table": "^8.21.2", "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", "chalk": "^5.4.1", diff --git a/packages/canon/src/components/DataTable/DataTable.tsx b/packages/canon/src/components/DataTable/DataTable.tsx new file mode 100644 index 0000000000..335e4d73e4 --- /dev/null +++ b/packages/canon/src/components/DataTable/DataTable.tsx @@ -0,0 +1,20 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; + +export const DataTable = () => { + return
DataTable
; +}; diff --git a/packages/canon/src/components/DataTable/index.ts b/packages/canon/src/components/DataTable/index.ts new file mode 100644 index 0000000000..cbbb2babe5 --- /dev/null +++ b/packages/canon/src/components/DataTable/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './DataTable'; diff --git a/packages/canon/src/components/Table/Table.stories.tsx b/packages/canon/src/components/Table/Table.stories.tsx index c4c08be527..8c94249a79 100644 --- a/packages/canon/src/components/Table/Table.stories.tsx +++ b/packages/canon/src/components/Table/Table.stories.tsx @@ -14,73 +14,34 @@ * limitations under the License. */ -const invoices = [ - { - invoice: 'INV001', - paymentStatus: 'Paid', - totalAmount: '$250.00', - paymentMethod: 'Credit Card', - }, - { - invoice: 'INV002', - paymentStatus: 'Pending', - totalAmount: '$150.00', - paymentMethod: 'PayPal', - }, - { - invoice: 'INV003', - paymentStatus: 'Unpaid', - totalAmount: '$350.00', - paymentMethod: 'Bank Transfer', - }, - { - invoice: 'INV004', - paymentStatus: 'Paid', - totalAmount: '$450.00', - paymentMethod: 'Credit Card', - }, - { - invoice: 'INV005', - paymentStatus: 'Paid', - totalAmount: '$550.00', - paymentMethod: 'PayPal', - }, - { - invoice: 'INV006', - paymentStatus: 'Pending', - totalAmount: '$200.00', - paymentMethod: 'Bank Transfer', - }, - { - invoice: 'INV007', - paymentStatus: 'Unpaid', - totalAmount: '$300.00', - paymentMethod: 'Credit Card', - }, -]; - import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; +import { Table } from '../Table'; +import { components } from './mocked-data/components'; import { - Table, - TableBody, - TableCell, - TableFooter, - TableHead, - TableHeader, - TableRow, -} from '../Table'; + // ColumnFiltersState, + // SortingState, + // VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from '@tanstack/react-table'; +import { columns } from './mocked-data/columns'; +import { TablePagination } from '../TablePagination'; const meta = { title: 'Components/Table', - component: Table, + component: Table.Root, subcomponents: { - TableBody: TableBody as React.ComponentType, - TableCell: TableCell as React.ComponentType, - TableFooter: TableFooter as React.ComponentType, - TableHead: TableHead as React.ComponentType, - TableHeader: TableHeader as React.ComponentType, - TableRow: TableRow as React.ComponentType, + Body: Table.Body as React.ComponentType, + Cell: Table.Cell as React.ComponentType, + Pagination: TablePagination as React.ComponentType, + Head: Table.Head as React.ComponentType, + Header: Table.Header as React.ComponentType, + Row: Table.Row as React.ComponentType, }, } satisfies Meta; @@ -88,32 +49,79 @@ export default meta; type Story = StoryObj; export const Default: Story = { - render: () => ( - - - - Invoice - Status - Method - Amount - - - - {invoices.map(invoice => ( - - {invoice.invoice} - {invoice.paymentStatus} - {invoice.paymentMethod} - {invoice.totalAmount} - - ))} - - - - Total - $2,500.00 - - -
- ), + render: () => { + const table = useReactTable({ + data: components, + columns, + // onSortingChange: setSorting, + // onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + // onColumnVisibilityChange: setColumnVisibility, + // onRowSelectionChange: setRowSelection, + // state: { + // sorting, + // columnFilters, + // columnVisibility, + // rowSelection, + // }, + }); + + return ( + + + {table.getHeaderGroups().map(headerGroup => ( + + {headerGroup.headers.map(header => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map(row => ( + + {row.getVisibleCells().map(cell => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + )) + ) : ( + + + No results. + + + )} + + table.previousPage()} + onClickNext={() => table.nextPage()} + canPrevious={table.getCanPreviousPage()} + canNext={table.getCanNextPage()} + setPageSize={pageSize => table.setPageSize(pageSize)} + /> + + ); + }, }; diff --git a/packages/canon/src/components/Table/Table.tsx b/packages/canon/src/components/Table/Table.tsx index efd5181036..f67f74883d 100644 --- a/packages/canon/src/components/Table/Table.tsx +++ b/packages/canon/src/components/Table/Table.tsx @@ -16,7 +16,7 @@ import * as React from 'react'; /** @public */ -const Table = React.forwardRef< +const TableRoot = React.forwardRef< HTMLTableElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( @@ -24,7 +24,7 @@ const Table = React.forwardRef< )); -Table.displayName = 'Table'; +TableRoot.displayName = 'TableRoot'; /** @public */ const TableHeader = React.forwardRef< @@ -48,19 +48,6 @@ const TableBody = React.forwardRef< )); TableBody.displayName = 'TableBody'; -/** @public */ -const TableFooter = React.forwardRef< - HTMLTableSectionElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( - -)); -TableFooter.displayName = 'TableFooter'; - /** @public */ const TableRow = React.forwardRef< HTMLTableRowElement, @@ -103,13 +90,12 @@ const TableCaption = React.forwardRef< )); TableCaption.displayName = 'TableCaption'; -export { - Table, - TableHeader, - TableBody, - TableFooter, - TableHead, - TableRow, - TableCell, - TableCaption, +export const Table = { + Root: TableRoot, + Header: TableHeader, + Body: TableBody, + Head: TableHead, + Row: TableRow, + Cell: TableCell, + Caption: TableCaption, }; diff --git a/packages/canon/src/components/Table/index.ts b/packages/canon/src/components/Table/index.ts index cf88de2b1a..1b13d8b85e 100644 --- a/packages/canon/src/components/Table/index.ts +++ b/packages/canon/src/components/Table/index.ts @@ -13,12 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { - Table, - TableHead, - TableRow, - TableHeader, - TableCell, - TableBody, - TableFooter, -} from './Table'; + +export * from './Table'; +export * from './types'; diff --git a/packages/canon/src/components/Table/mocked-data/columns.tsx b/packages/canon/src/components/Table/mocked-data/columns.tsx new file mode 100644 index 0000000000..3ef63c59bb --- /dev/null +++ b/packages/canon/src/components/Table/mocked-data/columns.tsx @@ -0,0 +1,71 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { ColumnDef } from '@tanstack/react-table'; +import { Component } from './components'; +import { Checkbox } from '../../Checkbox'; +import { Text } from '../../Text'; + +export const columns: ColumnDef[] = [ + { + id: 'select', + header: ({ table }) => ( + + table.toggleAllPageRowsSelected(checked) + } + aria-label="Select all" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(checked)} + aria-label="Select row" + /> + ), + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: 'name', + header: 'Name', + cell: ({ row }) => ( +
+ {row.getValue('name')} + + {row.original.description} + +
+ ), + }, + { + accessorKey: 'owner', + header: 'Owner', + cell: ({ row }) => {row.getValue('owner')}, + }, + { + accessorKey: 'type', + header: 'Type', + cell: ({ row }) => {row.getValue('type')}, + }, + { + accessorKey: 'tags', + header: 'Tags', + cell: ({ row }) => {row.getValue('tags')}, + }, +]; diff --git a/packages/canon/src/components/Table/mocked-data/components.ts b/packages/canon/src/components/Table/mocked-data/components.ts new file mode 100644 index 0000000000..f7861023d3 --- /dev/null +++ b/packages/canon/src/components/Table/mocked-data/components.ts @@ -0,0 +1,818 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export interface Component { + name: string; + owner: string; + type: 'documentation' | 'library' | 'service' | 'website' | 'other'; + description?: string; + tags?: string[]; +} + +export const components: Component[] = [ + { + name: 'authentication-and-authorization-service', + owner: 'security-team', + type: 'service', + description: + 'A comprehensive service handling user authentication and role-based access control across all applications.', + tags: ['security', 'authentication', 'authorization'], + }, + { + name: 'user-interface-dashboard-and-analytics-platform', + owner: 'frontend-team', + type: 'website', + description: + 'Interactive dashboard providing real-time analytics and data visualization for business metrics.', + tags: ['analytics', 'visualization', 'dashboard'], + }, + { + name: 'payment-gateway', + owner: 'finance-team', + type: 'service', + description: + 'Secure payment processing system supporting multiple payment methods and currencies.', + tags: ['payments', 'security', 'finance'], + }, + { + name: 'real-time-analytics-processing-and-visualization-engine', + owner: 'data-team', + type: 'service', + description: + 'High-performance engine for processing and visualizing streaming data analytics.', + tags: ['analytics', 'real-time', 'data-processing'], + }, + { + name: 'notification-center', + owner: 'platform-team', + type: 'service', + description: + 'Centralized system for managing and delivering notifications across multiple channels.', + tags: ['notifications', 'messaging'], + }, + { + name: 'administrative-control-panel-and-user-management-interface', + owner: 'frontend-team', + type: 'website', + description: + 'Admin interface for managing users, permissions, and system configurations.', + tags: ['admin', 'user-management', 'configuration'], + }, + { + name: 'search-indexer', + owner: 'search-team', + type: 'service', + description: + 'Service responsible for indexing and updating searchable content across the platform.', + tags: ['search', 'indexing'], + }, + { + name: 'cross-platform-mobile-application-framework', + owner: 'mobile-team', + type: 'website', + description: + 'Framework enabling development of cross-platform mobile applications with shared codebase.', + tags: ['mobile', 'framework', 'cross-platform'], + }, + { + name: 'database-migration', + owner: 'devops-team', + type: 'other', + description: + 'Tools and scripts for managing database schema migrations and data transformations.', + tags: ['database', 'migration', 'devops'], + }, + { + name: 'api-gateway', + owner: 'platform-team', + type: 'service', + description: + 'Central entry point for all API requests, handling routing, authentication, and rate limiting.', + tags: ['api', 'gateway', 'security', 'routing'], + }, + { + name: 'content-management', + owner: 'content-team', + type: 'service', + description: + 'System for managing and delivering digital content across multiple channels.', + tags: ['content', 'management', 'delivery'], + }, + { + name: 'enterprise-reporting-and-analytics-dashboard', + owner: 'analytics-team', + type: 'website', + description: + 'Comprehensive business intelligence platform for enterprise-wide reporting and analytics.', + tags: ['analytics', 'reporting', 'business-intelligence'], + }, + { + name: 'image-processing-and-optimization-service', + owner: 'media-team', + type: 'service', + description: + 'Service for processing, optimizing, and delivering images across different devices and networks.', + tags: ['media', 'optimization', 'processing'], + }, + { + name: 'customer-portal', + owner: 'frontend-team', + type: 'website', + description: + 'Self-service portal for customers to manage their accounts and access services.', + tags: ['customer', 'self-service'], + }, + { + name: 'log-aggregator', + owner: 'devops-team', + type: 'service', + description: + 'Centralized logging system for collecting, processing, and analyzing application logs.', + tags: ['logging', 'monitoring', 'devops'], + }, + { + name: 'identity-provider', + owner: 'security-team', + type: 'service', + description: + 'Service managing user identities and authentication across the organization.', + tags: ['identity', 'security', 'authentication'], + }, + { + name: 'document-storage', + owner: 'storage-team', + type: 'service', + description: + 'Secure and scalable document storage system with version control and access management.', + tags: ['storage', 'documents', 'version-control'], + }, + { + name: 'workflow-engine', + owner: 'platform-team', + type: 'service', + description: + 'Engine for defining and executing business processes and workflows.', + tags: ['workflow', 'automation'], + }, + { + name: 'mobile-backend', + owner: 'mobile-team', + type: 'service', + description: + 'Backend services supporting mobile applications with optimized APIs and data synchronization.', + tags: ['mobile', 'backend', 'api'], + }, + { + name: 'system-monitoring-and-alerting-dashboard', + owner: 'devops-team', + type: 'website', + description: + 'Real-time monitoring and alerting system for infrastructure and application health.', + tags: ['monitoring', 'alerting', 'devops', 'infrastructure'], + }, + { + name: 'email-service', + owner: 'communication-team', + type: 'service', + description: + 'Reliable email delivery service with templates and tracking capabilities.', + tags: ['email', 'communication'], + }, + { + name: 'data-pipeline', + owner: 'data-team', + type: 'service', + description: + 'ETL pipeline for processing and transforming large volumes of data.', + tags: ['data', 'etl', 'pipeline'], + }, + { + name: 'configuration-manager', + owner: 'platform-team', + type: 'service', + description: + 'Centralized system for managing application configurations across environments.', + tags: ['configuration', 'management'], + }, + { + name: 'testing-framework', + owner: 'qa-team', + type: 'library', + description: + 'Comprehensive testing framework supporting various types of automated tests.', + tags: ['testing', 'automation', 'qa'], + }, + { + name: 'cache-service', + owner: 'platform-team', + type: 'service', + description: + 'Distributed caching service for improving application performance.', + tags: ['caching', 'performance'], + }, + { + name: 'billing-system', + owner: 'finance-team', + type: 'service', + description: + 'System for managing customer billing, invoicing, and payment processing.', + tags: ['billing', 'finance', 'payments'], + }, + { + name: 'comprehensive-product-documentation-and-api-reference', + owner: 'docs-team', + type: 'documentation', + description: + 'Complete documentation covering product features, APIs, and integration guides.', + tags: ['documentation', 'api', 'reference'], + }, + { + name: 'queue-manager', + owner: 'platform-team', + type: 'service', + description: + 'Message queue system for asynchronous processing and event handling.', + tags: ['queue', 'messaging', 'async'], + }, + { + name: 'security-scanner', + owner: 'security-team', + type: 'other', + description: + 'Automated security scanning tool for identifying vulnerabilities in code and infrastructure.', + tags: ['security', 'scanning', 'vulnerability'], + }, + { + name: 'user-profile', + owner: 'frontend-team', + type: 'website', + description: + 'User profile management interface with personalization features.', + tags: ['user', 'profile', 'personalization'], + }, + { + name: 'data-warehouse', + owner: 'data-team', + type: 'service', + description: + 'Centralized data repository for business intelligence and analytics.', + tags: ['data', 'warehouse', 'analytics'], + }, + { + name: 'deployment-automation', + owner: 'devops-team', + type: 'other', + description: + 'Automated deployment pipeline for continuous integration and delivery.', + tags: ['deployment', 'automation', 'ci-cd', 'devops'], + }, + { + name: 'chat-service', + owner: 'communication-team', + type: 'service', + description: + 'Real-time chat service supporting text, file sharing, and group conversations.', + tags: ['chat', 'communication', 'real-time'], + }, + { + name: 'analytics-dashboard', + owner: 'analytics-team', + type: 'website', + description: + 'Interactive dashboard for visualizing and analyzing business metrics.', + tags: ['analytics', 'dashboard', 'visualization'], + }, + { + name: 'file-uploader', + owner: 'storage-team', + type: 'service', + description: + 'Service for handling secure file uploads with progress tracking and validation.', + tags: ['storage', 'upload', 'files'], + }, + { + name: 'search-service', + owner: 'search-team', + type: 'service', + description: + 'Full-text search service with advanced filtering and ranking capabilities.', + tags: ['search', 'full-text'], + }, + { + name: 'mobile-sdk', + owner: 'mobile-team', + type: 'library', + description: + 'Software development kit for building mobile applications with native features.', + tags: ['mobile', 'sdk', 'development'], + }, + { + name: 'performance-monitor', + owner: 'devops-team', + type: 'service', + description: + 'System for monitoring and analyzing application performance metrics.', + tags: ['performance', 'monitoring', 'metrics'], + }, + { + name: 'content-delivery', + owner: 'media-team', + type: 'service', + description: + 'CDN service for optimized content delivery across global networks.', + tags: ['cdn', 'content', 'delivery'], + }, + { + name: 'user-authentication', + owner: 'security-team', + type: 'service', + description: + 'Service handling user login, session management, and authentication flows.', + tags: ['authentication', 'security', 'user'], + }, + { + name: 'data-export', + owner: 'data-team', + type: 'service', + description: + 'Service for exporting data in various formats with scheduling capabilities.', + tags: ['data', 'export', 'scheduling'], + }, + { + name: 'admin-api', + owner: 'platform-team', + type: 'service', + description: + 'API endpoints for administrative functions and system management.', + tags: ['api', 'admin', 'management'], + }, + { + name: 'testing-dashboard', + owner: 'qa-team', + type: 'website', + description: 'Dashboard for monitoring test results and quality metrics.', + tags: ['testing', 'dashboard', 'qa'], + }, + { + name: 'message-broker', + owner: 'platform-team', + type: 'service', + description: + 'Message broker service for reliable event-driven communication between services.', + tags: ['messaging', 'broker', 'event-driven'], + }, + { + name: 'payment-processor', + owner: 'finance-team', + type: 'service', + description: + 'Service for processing financial transactions and payment methods.', + tags: ['payments', 'finance', 'processing'], + }, + { + name: 'document-viewer', + owner: 'frontend-team', + type: 'website', + description: 'Web-based document viewer supporting multiple file formats.', + tags: ['documents', 'viewer'], + }, + { + name: 'load-balancer', + owner: 'devops-team', + type: 'service', + description: + 'Service for distributing network traffic across multiple servers.', + tags: ['load-balancing', 'networking', 'infrastructure'], + }, + { + name: 'security-audit', + owner: 'security-team', + type: 'other', + description: + 'Tools and processes for conducting security audits and compliance checks.', + tags: ['security', 'audit', 'compliance'], + }, + { + name: 'user-settings', + owner: 'frontend-team', + type: 'website', + description: + 'Interface for users to manage their preferences and account settings.', + tags: ['user', 'settings', 'preferences'], + }, + { + name: 'data-import', + owner: 'data-team', + type: 'service', + description: + 'Service for importing and validating data from external sources.', + tags: ['data', 'import', 'validation'], + }, + { + name: 'infrastructure-monitor', + owner: 'devops-team', + type: 'service', + description: + 'Monitoring system for infrastructure components and resources.', + tags: ['monitoring', 'infrastructure', 'devops'], + }, + { + name: 'notification-manager', + owner: 'communication-team', + type: 'service', + description: + 'Service for managing and delivering notifications across multiple channels.', + tags: ['notifications', 'management'], + }, + { + name: 'analytics-processor', + owner: 'analytics-team', + type: 'service', + description: + 'Service for processing and analyzing business data and metrics.', + tags: ['analytics', 'processing', 'metrics'], + }, + { + name: 'file-manager', + owner: 'storage-team', + type: 'website', + description: 'Web interface for managing files and storage resources.', + tags: ['files', 'storage', 'management'], + }, + { + name: 'search-index', + owner: 'search-team', + type: 'service', + description: 'Service for maintaining and updating search indices.', + tags: ['search', 'indexing'], + }, + { + name: 'mobile-authentication', + owner: 'mobile-team', + type: 'service', + description: + 'Authentication service specifically designed for mobile applications.', + tags: ['mobile', 'authentication', 'security'], + }, + { + name: 'system-monitor', + owner: 'devops-team', + type: 'service', + description: + 'Monitoring service for system health and performance metrics.', + tags: ['monitoring', 'system', 'metrics'], + }, + { + name: 'media-processor', + owner: 'media-team', + type: 'service', + description: 'Service for processing and optimizing media files.', + tags: ['media', 'processing', 'optimization'], + }, + { + name: 'user-management', + owner: 'security-team', + type: 'service', + description: 'Service for managing user accounts and permissions.', + tags: ['user', 'management', 'security'], + }, + { + name: 'data-transformer', + owner: 'data-team', + type: 'service', + description: + 'Service for transforming data between different formats and structures.', + tags: ['data', 'transformation'], + }, + { + name: 'admin-dashboard', + owner: 'platform-team', + type: 'website', + description: + 'Administrative interface for system management and monitoring.', + tags: ['admin', 'dashboard', 'management'], + }, + { + name: 'test-automation', + owner: 'qa-team', + type: 'other', + description: 'Tools and frameworks for automating testing processes.', + tags: ['testing', 'automation', 'qa'], + }, + { + name: 'event-bus', + owner: 'platform-team', + type: 'service', + description: 'Event-driven communication system between services.', + tags: ['events', 'messaging', 'communication'], + }, + { + name: 'invoice-generator', + owner: 'finance-team', + type: 'service', + description: 'Service for generating and managing invoices.', + tags: ['invoices', 'finance'], + }, + { + name: 'document-editor', + owner: 'frontend-team', + type: 'website', + description: 'Web-based document editing interface.', + tags: ['documents', 'editor'], + }, + { + name: 'service-discovery', + owner: 'devops-team', + type: 'service', + description: 'Service for discovering and registering available services.', + tags: ['discovery', 'services', 'devops'], + }, + { + name: 'security-monitor', + owner: 'security-team', + type: 'service', + description: 'Service for monitoring security events and threats.', + tags: ['security', 'monitoring', 'threats'], + }, + { + name: 'user-preferences', + owner: 'frontend-team', + type: 'website', + description: 'Interface for managing user preferences and settings.', + tags: ['user', 'preferences'], + }, + { + name: 'data-validator', + owner: 'data-team', + type: 'service', + description: 'Service for validating data integrity and format.', + tags: ['data', 'validation'], + }, + { + name: 'infrastructure-automation', + owner: 'devops-team', + type: 'other', + description: + 'Tools for automating infrastructure provisioning and management.', + tags: ['infrastructure', 'automation', 'devops'], + }, + { + name: 'notification-dispatcher', + owner: 'communication-team', + type: 'service', + description: + 'Service for dispatching notifications to appropriate channels.', + tags: ['notifications', 'dispatch'], + }, + { + name: 'analytics-collector', + owner: 'analytics-team', + type: 'service', + description: 'Service for collecting and aggregating analytics data.', + tags: ['analytics', 'collection', 'aggregation'], + }, + { + name: 'file-processor', + owner: 'storage-team', + type: 'service', + description: 'Service for processing and managing files.', + tags: ['files', 'processing'], + }, + { + name: 'search-analyzer', + owner: 'search-team', + type: 'service', + description: 'Service for analyzing search queries and results.', + tags: ['search', 'analysis'], + }, + { + name: 'mobile-notifications', + owner: 'mobile-team', + type: 'service', + description: 'Service for sending notifications to mobile devices.', + tags: ['mobile', 'notifications'], + }, + { + name: 'system-alerts', + owner: 'devops-team', + type: 'service', + description: 'Service for managing and dispatching system alerts.', + tags: ['alerts', 'system', 'monitoring'], + }, + { + name: 'media-encoder', + owner: 'media-team', + type: 'service', + description: 'Service for encoding and processing media files.', + tags: ['media', 'encoding'], + }, + { + name: 'user-authorization', + owner: 'security-team', + type: 'service', + description: 'Service for managing user permissions and access control.', + tags: ['authorization', 'security', 'user'], + }, + { + name: 'data-aggregator', + owner: 'data-team', + type: 'service', + description: 'Service for aggregating data from multiple sources.', + tags: ['data', 'aggregation'], + }, + { + name: 'admin-authentication', + owner: 'platform-team', + type: 'service', + description: 'Authentication service for administrative access.', + tags: ['admin', 'authentication', 'security'], + }, + { + name: 'test-coverage', + owner: 'qa-team', + type: 'other', + description: 'Tools for measuring and reporting test coverage.', + tags: ['testing', 'coverage', 'qa'], + }, + { + name: 'event-processor', + owner: 'platform-team', + type: 'service', + description: 'Service for processing and handling events.', + tags: ['events', 'processing'], + }, + { + name: 'payment-validator', + owner: 'finance-team', + type: 'service', + description: 'Service for validating payment transactions.', + tags: ['payments', 'validation', 'finance'], + }, + { + name: 'document-converter', + owner: 'frontend-team', + type: 'service', + description: 'Service for converting documents between different formats.', + tags: ['documents', 'conversion'], + }, + { + name: 'service-health', + owner: 'devops-team', + type: 'service', + description: 'Service for monitoring and reporting service health status.', + tags: ['health', 'monitoring', 'services'], + }, + { + name: 'security-logger', + owner: 'security-team', + type: 'service', + description: 'Service for logging security-related events and activities.', + tags: ['security', 'logging'], + }, + { + name: 'user-analytics', + owner: 'frontend-team', + type: 'website', + description: + 'Analytics dashboard for user behavior and engagement metrics.', + tags: ['analytics', 'user', 'metrics'], + }, + { + name: 'data-cleaner', + owner: 'data-team', + type: 'service', + description: 'Service for cleaning and standardizing data.', + tags: ['data', 'cleaning'], + }, + { + name: 'infrastructure-deployer', + owner: 'devops-team', + type: 'other', + description: 'Tools for deploying and managing infrastructure resources.', + tags: ['infrastructure', 'deployment', 'devops'], + }, + { + name: 'notification-queue', + owner: 'communication-team', + type: 'service', + description: 'Queue system for managing notification delivery.', + tags: ['notifications', 'queue'], + }, + { + name: 'analytics-exporter', + owner: 'analytics-team', + type: 'service', + description: 'Service for exporting analytics data in various formats.', + tags: ['analytics', 'export'], + }, + { + name: 'file-validator', + owner: 'storage-team', + type: 'service', + description: 'Service for validating file integrity and format.', + tags: ['files', 'validation'], + }, + { + name: 'search-optimizer', + owner: 'search-team', + type: 'service', + description: 'Service for optimizing search performance and relevance.', + tags: ['search', 'optimization'], + }, + { + name: 'mobile-analytics', + owner: 'mobile-team', + type: 'service', + description: 'Analytics service specifically for mobile applications.', + tags: ['mobile', 'analytics'], + }, + { + name: 'system-logger', + owner: 'devops-team', + type: 'service', + description: 'Service for logging system events and activities.', + tags: ['logging', 'system'], + }, + { + name: 'media-validator', + owner: 'media-team', + type: 'service', + description: 'Service for validating media files and formats.', + tags: ['media', 'validation'], + }, + { + name: 'user-audit', + owner: 'security-team', + type: 'service', + description: 'Service for auditing user activities and access.', + tags: ['audit', 'user', 'security'], + }, + { + name: 'data-normalizer', + owner: 'data-team', + type: 'service', + description: 'Service for normalizing data formats and structures.', + tags: ['data', 'normalization'], + }, + { + name: 'admin-authorization', + owner: 'platform-team', + type: 'service', + description: 'Authorization service for administrative functions.', + tags: ['admin', 'authorization', 'security'], + }, + { + name: 'test-reporting', + owner: 'qa-team', + type: 'other', + description: 'Tools for generating and managing test reports.', + tags: ['testing', 'reporting', 'qa'], + }, + { + name: 'event-aggregator', + owner: 'platform-team', + type: 'service', + description: 'Service for aggregating and processing events.', + tags: ['events', 'aggregation'], + }, + { + name: 'payment-reconciler', + owner: 'finance-team', + type: 'service', + description: 'Service for reconciling payment transactions.', + tags: ['payments', 'reconciliation', 'finance'], + }, + { + name: 'document-validator', + owner: 'frontend-team', + type: 'service', + description: 'Service for validating document formats and content.', + tags: ['documents', 'validation'], + }, + { + name: 'service-monitor', + owner: 'devops-team', + type: 'service', + description: 'Service for monitoring service health and performance.', + tags: ['monitoring', 'services', 'health'], + }, + { + name: 'security-validator', + owner: 'security-team', + type: 'service', + description: 'Service for validating security configurations and policies.', + tags: ['security', 'validation'], + }, +]; diff --git a/packages/canon/src/components/Table/styles.css b/packages/canon/src/components/Table/styles.css index 7d3cb35e3d..93a7de3259 100644 --- a/packages/canon/src/components/Table/styles.css +++ b/packages/canon/src/components/Table/styles.css @@ -59,3 +59,13 @@ box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1), inset 0px -2px 0 0 var(--canon-bg-surface-1); } + +.canon-TablePagination { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--canon-space-3); + background-color: var(--canon-bg-surface-1); + border-top: 1px solid var(--canon-border); +} diff --git a/packages/canon/src/components/Table/types.ts b/packages/canon/src/components/Table/types.ts new file mode 100644 index 0000000000..79628de2bd --- /dev/null +++ b/packages/canon/src/components/Table/types.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export interface TablePaginationProps + extends React.HTMLAttributes { + /** + * The current page index. + */ + pageIndex: number; + /** + * The current page size. + */ + pageSize: number; + /** + * The total number of rows. + */ + totalRows: number; + /** + * The function to call when the previous button is clicked. + */ + onClickPrevious: () => void; + /** + * The function to call when the next button is clicked. + */ + onClickNext: () => void; + /** + * Whether the previous button is disabled. + */ + canPrevious: boolean; + /** + * Whether the next button is disabled. + */ + canNext: boolean; + /** + * The function to call when the page size is changed. + */ + setPageSize: (pageSize: number) => void; +} diff --git a/packages/canon/src/components/TablePagination/TablePagination.tsx b/packages/canon/src/components/TablePagination/TablePagination.tsx new file mode 100644 index 0000000000..b04fd69f28 --- /dev/null +++ b/packages/canon/src/components/TablePagination/TablePagination.tsx @@ -0,0 +1,81 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as React from 'react'; +import { Text } from '../Text'; +import { TablePaginationProps } from './types'; +import { Button } from '../Button'; + +/** @public */ +const TablePagination = React.forwardRef( + ({ className, ...props }, ref) => { + const { + pageIndex, + pageSize, + onClickPrevious, + onClickNext, + canPrevious, + canNext, + totalRows, + setPageSize, + } = props; + return ( +
+ +
+ {`${pageIndex * pageSize + 1} - ${ + (pageIndex + 1) * pageSize + } of ${totalRows}`} +
+
+ + +
+
+ ); + }, +); +TablePagination.displayName = 'TablePagination'; + +export { TablePagination }; diff --git a/packages/canon/src/components/TablePagination/index.ts b/packages/canon/src/components/TablePagination/index.ts new file mode 100644 index 0000000000..2a2d462f84 --- /dev/null +++ b/packages/canon/src/components/TablePagination/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './TablePagination'; +export * from './types'; diff --git a/packages/canon/src/components/TablePagination/types.ts b/packages/canon/src/components/TablePagination/types.ts new file mode 100644 index 0000000000..79628de2bd --- /dev/null +++ b/packages/canon/src/components/TablePagination/types.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export interface TablePaginationProps + extends React.HTMLAttributes { + /** + * The current page index. + */ + pageIndex: number; + /** + * The current page size. + */ + pageSize: number; + /** + * The total number of rows. + */ + totalRows: number; + /** + * The function to call when the previous button is clicked. + */ + onClickPrevious: () => void; + /** + * The function to call when the next button is clicked. + */ + onClickNext: () => void; + /** + * Whether the previous button is disabled. + */ + canPrevious: boolean; + /** + * Whether the next button is disabled. + */ + canNext: boolean; + /** + * The function to call when the page size is changed. + */ + setPageSize: (pageSize: number) => void; +} diff --git a/yarn.lock b/yarn.lock index baf0dd3925..9e19cc722f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3789,6 +3789,7 @@ __metadata: "@storybook/react": ^8.6.8 "@storybook/react-webpack5": ^8.6.8 "@storybook/test": ^8.6.8 + "@tanstack/react-table": ^8.21.2 "@types/react": ^18.0.0 "@types/react-dom": ^18.0.0 chalk: ^5.4.1 @@ -19117,6 +19118,25 @@ __metadata: languageName: node linkType: hard +"@tanstack/react-table@npm:^8.21.2": + version: 8.21.2 + resolution: "@tanstack/react-table@npm:8.21.2" + dependencies: + "@tanstack/table-core": 8.21.2 + peerDependencies: + react: ">=16.8" + react-dom: ">=16.8" + checksum: 3cda97794c10777d48d01de29d087a33edd8af153b3096ac9798c7ebddd31bd34f2b7838ecf4bddaf469d661a777556bd8021e15a41f692eb2e3dd79dd0e5c3b + languageName: node + linkType: hard + +"@tanstack/table-core@npm:8.21.2": + version: 8.21.2 + resolution: "@tanstack/table-core@npm:8.21.2" + checksum: 21573388b26cef9c6fcabe3785640c470ea072a6d6ec34543e7eaf54ee742bf5fb12f8a04b172d7e08bd828a35afd7133add5adb4729dd4f94eaf4745b1cab14 + languageName: node + linkType: hard + "@techdocs/cli@workspace:*, @techdocs/cli@workspace:packages/techdocs-cli": version: 0.0.0-use.local resolution: "@techdocs/cli@workspace:packages/techdocs-cli" From 2b74f7be4b4dad6932b665a7e22605b9eaca1135 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 4 Apr 2025 21:32:59 +0100 Subject: [PATCH 06/56] Add DataTable Signed-off-by: Charles de Dreuille --- .../DataTablePagination.stories.tsx | 39 ++++++++ .../Pagination/DataTablePagination.styles.css | 21 +++++ .../Pagination/DataTablePagination.tsx | 86 ++++++++++++++++++ .../Pagination}/types.ts | 20 +++-- .../DataTable/Root/DataTableRoot.stories.tsx | 35 ++++++++ .../DataTable/Root/DataTableRoot.styles.css | 5 ++ .../Root/DataTableRoot.tsx} | 21 ++++- .../{DataTable.tsx => Root/types.ts} | 7 +- .../canon/src/components/DataTable/index.ts | 11 ++- .../src/components/Table/Table.stories.tsx | 90 ++++++++++--------- .../canon/src/components/Table/styles.css | 17 ---- .../TablePagination/TablePagination.tsx | 81 ----------------- packages/canon/src/css/components.css | 2 + packages/canon/src/index.ts | 1 + 14 files changed, 281 insertions(+), 155 deletions(-) create mode 100644 packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx create mode 100644 packages/canon/src/components/DataTable/Pagination/DataTablePagination.styles.css create mode 100644 packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx rename packages/canon/src/components/{TablePagination => DataTable/Pagination}/types.ts (80%) create mode 100644 packages/canon/src/components/DataTable/Root/DataTableRoot.stories.tsx create mode 100644 packages/canon/src/components/DataTable/Root/DataTableRoot.styles.css rename packages/canon/src/components/{TablePagination/index.ts => DataTable/Root/DataTableRoot.tsx} (57%) rename packages/canon/src/components/DataTable/{DataTable.tsx => Root/types.ts} (85%) delete mode 100644 packages/canon/src/components/TablePagination/TablePagination.tsx diff --git a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx new file mode 100644 index 0000000000..54563cd98a --- /dev/null +++ b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx @@ -0,0 +1,39 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Meta, StoryObj } from '@storybook/react'; +import { DataTablePagination } from './DataTablePagination'; + +const meta = { + title: 'Components/DataTable/Pagination', + component: DataTablePagination, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + pageIndex: 0, + pageSize: 10, + totalRows: 100, + onClickPrevious: () => {}, + onClickNext: () => {}, + canPrevious: true, + canNext: true, + setPageSize: () => {}, + }, +}; diff --git a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.styles.css b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.styles.css new file mode 100644 index 0000000000..460a6a5874 --- /dev/null +++ b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.styles.css @@ -0,0 +1,21 @@ +.canon-TablePagination { + display: flex; + align-items: center; + justify-content: space-between; + padding-top: var(--canon-space-3); + border-top: 1px solid var(--canon-border); + margin-top: var(--canon-space-3); +} + +.canon-TablePagination--left { + display: flex; + align-items: center; + justify-content: space-between; +} + +.canon-TablePagination--right { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--canon-space-2); +} diff --git a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx new file mode 100644 index 0000000000..3a80ba3270 --- /dev/null +++ b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx @@ -0,0 +1,86 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as React from 'react'; +import { Text } from '../../Text'; +import { DataTablePaginationProps } from './types'; +import { IconButton } from '../../IconButton'; +import clsx from 'clsx'; +import { Select } from '../../Select'; + +/** @public */ +const DataTablePagination = React.forwardRef< + HTMLDivElement, + DataTablePaginationProps +>(({ className, ...props }, ref) => { + const { + pageIndex, + pageSize, + onClickPrevious, + onClickNext, + canPrevious, + canNext, + totalRows, + setPageSize, + } = props; + return ( +
+
+ { - setPageSize(Number(e.target.value)); - }} - > - {[10, 20, 30, 40, 50].map(pageSize => ( - - ))} - -
- {`${pageIndex * pageSize + 1} - ${ - (pageIndex + 1) * pageSize - } of ${totalRows}`} -
-
- - -
-
- ); - }, -); -TablePagination.displayName = 'TablePagination'; - -export { TablePagination }; diff --git a/packages/canon/src/css/components.css b/packages/canon/src/css/components.css index 2480baab14..f6b1db16c5 100644 --- a/packages/canon/src/css/components.css +++ b/packages/canon/src/css/components.css @@ -16,6 +16,8 @@ @import '../components/Box/styles.css'; @import '../components/Button/styles.css'; +@import '../components/DataTable/Root/DataTableRoot.styles.css'; +@import '../components/DataTable/Pagination/DataTablePagination.styles.css'; @import '../components/Flex/styles.css'; @import '../components/Grid/styles.css'; @import '../components/Container/styles.css'; diff --git a/packages/canon/src/index.ts b/packages/canon/src/index.ts index 7435b6dd60..a458ac0f4d 100644 --- a/packages/canon/src/index.ts +++ b/packages/canon/src/index.ts @@ -33,6 +33,7 @@ export * from './components/Heading'; // UI components export * from './components/Button'; +export * from './components/DataTable'; export * from './components/Icon'; export * from './components/IconButton'; export * from './components/Checkbox'; From 4c9a6c94ee0d41a38d7b791618e296ebd64528b5 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 4 Apr 2025 22:15:45 +0100 Subject: [PATCH 07/56] Improve DataTable Signed-off-by: Charles de Dreuille --- packages/canon/css/components.css | 35 +++- packages/canon/css/datatable.css | 21 +++ packages/canon/css/styles.css | 35 +++- packages/canon/css/table.css | 7 - packages/canon/report.api.md | 99 ++++++----- .../DataTable/DataTable.stories.tsx | 113 ++++++++++++ .../canon/src/components/DataTable/index.ts | 4 + .../mocked-columns.tsx} | 6 +- .../mocked-components.ts} | 0 .../src/components/Table/Table.stories.tsx | 167 ++++++++---------- packages/canon/src/components/Table/Table.tsx | 11 +- packages/canon/src/components/Table/index.ts | 1 - packages/canon/src/components/Table/types.ts | 50 ------ 13 files changed, 328 insertions(+), 221 deletions(-) create mode 100644 packages/canon/css/datatable.css create mode 100644 packages/canon/src/components/DataTable/DataTable.stories.tsx rename packages/canon/src/components/{Table/mocked-data/columns.tsx => DataTable/mocked-columns.tsx} (94%) rename packages/canon/src/components/{Table/mocked-data/components.ts => DataTable/mocked-components.ts} (100%) delete mode 100644 packages/canon/src/components/Table/types.ts diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index bc473d2006..9314abed7c 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -94,6 +94,34 @@ height: 1.5rem; } +.canon-DataTableRoot { + gap: var(--canon-space-3); + flex-direction: column; + display: flex; +} + +.canon-TablePagination { + padding-top: var(--canon-space-3); + border-top: 1px solid var(--canon-border); + margin-top: var(--canon-space-3); + justify-content: space-between; + align-items: center; + display: flex; +} + +.canon-TablePagination--left { + justify-content: space-between; + align-items: center; + display: flex; +} + +.canon-TablePagination--right { + justify-content: space-between; + align-items: center; + gap: var(--canon-space-2); + display: flex; +} + .canon-Flex { display: flex; } @@ -163,14 +191,7 @@ } .table { - background-color: var(--canon-bg-surface-1); - border-radius: var(--canon-radius-2); width: 100%; - padding-bottom: var(--canon-space-0_5); - padding-top: var(--canon-space-0_5); - font-size: var(--canon-font-size-3); - font-family: var(--canon-font-regular); - font-weight: var(--canon-font-weight-regular); position: relative; overflow: auto; diff --git a/packages/canon/css/datatable.css b/packages/canon/css/datatable.css new file mode 100644 index 0000000000..c33ecce578 --- /dev/null +++ b/packages/canon/css/datatable.css @@ -0,0 +1,21 @@ +.canon-TablePagination { + padding-top: var(--canon-space-3); + border-top: 1px solid var(--canon-border); + margin-top: var(--canon-space-3); + justify-content: space-between; + align-items: center; + display: flex; +} + +.canon-TablePagination--left { + justify-content: space-between; + align-items: center; + display: flex; +} + +.canon-TablePagination--right { + justify-content: space-between; + align-items: center; + gap: var(--canon-space-2); + display: flex; +} diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index b5093e217c..db668eb1b9 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9300,6 +9300,34 @@ height: 1.5rem; } +.canon-DataTableRoot { + gap: var(--canon-space-3); + flex-direction: column; + display: flex; +} + +.canon-TablePagination { + padding-top: var(--canon-space-3); + border-top: 1px solid var(--canon-border); + margin-top: var(--canon-space-3); + justify-content: space-between; + align-items: center; + display: flex; +} + +.canon-TablePagination--left { + justify-content: space-between; + align-items: center; + display: flex; +} + +.canon-TablePagination--right { + justify-content: space-between; + align-items: center; + gap: var(--canon-space-2); + display: flex; +} + .canon-Flex { display: flex; } @@ -9369,14 +9397,7 @@ } .table { - background-color: var(--canon-bg-surface-1); - border-radius: var(--canon-radius-2); width: 100%; - padding-bottom: var(--canon-space-0_5); - padding-top: var(--canon-space-0_5); - font-size: var(--canon-font-size-3); - font-family: var(--canon-font-regular); - font-weight: var(--canon-font-weight-regular); position: relative; overflow: auto; diff --git a/packages/canon/css/table.css b/packages/canon/css/table.css index a8e904d7ce..a146010779 100644 --- a/packages/canon/css/table.css +++ b/packages/canon/css/table.css @@ -1,12 +1,5 @@ .table { - background-color: var(--canon-bg-surface-1); - border-radius: var(--canon-radius-2); width: 100%; - padding-bottom: var(--canon-space-0_5); - padding-top: var(--canon-space-0_5); - font-size: var(--canon-font-size-3); - font-family: var(--canon-font-regular); - font-weight: var(--canon-font-weight-regular); position: relative; overflow: auto; diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index 766b40febd..29bf979040 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -213,6 +213,33 @@ export interface ContainerProps { style?: React.CSSProperties; } +// @public +export const DataTable: { + Root: ForwardRefExoticComponent< + DataTableRootProps & RefAttributes + >; + Pagination: ForwardRefExoticComponent< + DataTablePaginationProps & RefAttributes + >; +}; + +// @public (undocumented) +export interface DataTablePaginationProps + extends React.HTMLAttributes { + canNext?: boolean; + canPrevious?: boolean; + onClickNext?: () => void; + onClickPrevious?: () => void; + pageIndex?: number; + pageSize?: number; + setPageSize?: (pageSize: number) => void; + totalRows?: number; +} + +// @public (undocumented) +export interface DataTableRootProps + extends React.HTMLAttributes {} + // @public (undocumented) export type Display = 'none' | 'flex' | 'block' | 'inline'; @@ -1005,47 +1032,37 @@ export type StylingPropDef = { parseValue?: (value: string) => string | undefined; }; -// @public (undocumented) -export const Table: React_3.ForwardRefExoticComponent< - React_3.HTMLAttributes & - React_3.RefAttributes ->; - -// @public (undocumented) -export const TableBody: React_3.ForwardRefExoticComponent< - React_3.HTMLAttributes & - React_3.RefAttributes ->; - -// @public (undocumented) -export const TableCell: React_3.ForwardRefExoticComponent< - React_3.TdHTMLAttributes & - React_3.RefAttributes ->; - -// @public (undocumented) -export const TableFooter: React_3.ForwardRefExoticComponent< - React_3.HTMLAttributes & - React_3.RefAttributes ->; - -// @public (undocumented) -export const TableHead: React_3.ForwardRefExoticComponent< - React_3.ThHTMLAttributes & - React_3.RefAttributes ->; - -// @public (undocumented) -export const TableHeader: React_3.ForwardRefExoticComponent< - React_3.HTMLAttributes & - React_3.RefAttributes ->; - -// @public (undocumented) -export const TableRow: React_3.ForwardRefExoticComponent< - React_3.HTMLAttributes & - React_3.RefAttributes ->; +// @public +export const Table: { + Root: React_3.ForwardRefExoticComponent< + React_3.HTMLAttributes & + React_3.RefAttributes + >; + Header: React_3.ForwardRefExoticComponent< + React_3.HTMLAttributes & + React_3.RefAttributes + >; + Body: React_3.ForwardRefExoticComponent< + React_3.HTMLAttributes & + React_3.RefAttributes + >; + Head: React_3.ForwardRefExoticComponent< + React_3.ThHTMLAttributes & + React_3.RefAttributes + >; + Row: React_3.ForwardRefExoticComponent< + React_3.HTMLAttributes & + React_3.RefAttributes + >; + Cell: React_3.ForwardRefExoticComponent< + React_3.TdHTMLAttributes & + React_3.RefAttributes + >; + Caption: React_3.ForwardRefExoticComponent< + React_3.HTMLAttributes & + React_3.RefAttributes + >; +}; // @public (undocumented) const Text_2: React_2.ForwardRefExoticComponent< diff --git a/packages/canon/src/components/DataTable/DataTable.stories.tsx b/packages/canon/src/components/DataTable/DataTable.stories.tsx new file mode 100644 index 0000000000..46bc81d08c --- /dev/null +++ b/packages/canon/src/components/DataTable/DataTable.stories.tsx @@ -0,0 +1,113 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { Table } from '../Table'; +import { DataTable } from '.'; +import { components } from './mocked-components'; +import { columns } from './mocked-columns'; +import { + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from '@tanstack/react-table'; + +const meta = { + title: 'Components/DataTable', +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => { + const table = useReactTable({ + data: components, + columns, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + }); + + return ( + + + + {table.getHeaderGroups().map(headerGroup => ( + + {headerGroup.headers.map(header => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map(row => ( + + {row.getVisibleCells().map(cell => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + + + table.previousPage()} + onClickNext={() => table.nextPage()} + canPrevious={table.getCanPreviousPage()} + canNext={table.getCanNextPage()} + setPageSize={pageSize => table.setPageSize(pageSize)} + /> + + ); + }, +}; diff --git a/packages/canon/src/components/DataTable/index.ts b/packages/canon/src/components/DataTable/index.ts index dc377403a8..688df19b90 100644 --- a/packages/canon/src/components/DataTable/index.ts +++ b/packages/canon/src/components/DataTable/index.ts @@ -17,6 +17,10 @@ import { DataTableRoot } from './Root/DataTableRoot'; import { DataTablePagination } from './Pagination/DataTablePagination'; +/** + * DataTable component for displaying tabular data with pagination + * @public + */ export const DataTable = { Root: DataTableRoot, Pagination: DataTablePagination, diff --git a/packages/canon/src/components/Table/mocked-data/columns.tsx b/packages/canon/src/components/DataTable/mocked-columns.tsx similarity index 94% rename from packages/canon/src/components/Table/mocked-data/columns.tsx rename to packages/canon/src/components/DataTable/mocked-columns.tsx index 3ef63c59bb..0c730af3ab 100644 --- a/packages/canon/src/components/Table/mocked-data/columns.tsx +++ b/packages/canon/src/components/DataTable/mocked-columns.tsx @@ -15,9 +15,9 @@ */ import React from 'react'; import { ColumnDef } from '@tanstack/react-table'; -import { Component } from './components'; -import { Checkbox } from '../../Checkbox'; -import { Text } from '../../Text'; +import { Component } from './mocked-components'; +import { Checkbox } from '../Checkbox'; +import { Text } from '../Text'; export const columns: ColumnDef[] = [ { diff --git a/packages/canon/src/components/Table/mocked-data/components.ts b/packages/canon/src/components/DataTable/mocked-components.ts similarity index 100% rename from packages/canon/src/components/Table/mocked-data/components.ts rename to packages/canon/src/components/DataTable/mocked-components.ts diff --git a/packages/canon/src/components/Table/Table.stories.tsx b/packages/canon/src/components/Table/Table.stories.tsx index 6dcdf748a1..ff1993235a 100644 --- a/packages/canon/src/components/Table/Table.stories.tsx +++ b/packages/canon/src/components/Table/Table.stories.tsx @@ -17,20 +17,51 @@ import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { Table } from '../Table'; -import { components } from './mocked-data/components'; -import { - // ColumnFiltersState, - // SortingState, - // VisibilityState, - flexRender, - getCoreRowModel, - getFilteredRowModel, - getPaginationRowModel, - getSortedRowModel, - useReactTable, -} from '@tanstack/react-table'; -import { columns } from './mocked-data/columns'; -import { TablePagination } from '../TablePagination'; + +const invoices = [ + { + invoice: 'INV001', + paymentStatus: 'Paid', + totalAmount: '$250.00', + paymentMethod: 'Credit Card', + }, + { + invoice: 'INV002', + paymentStatus: 'Pending', + totalAmount: '$150.00', + paymentMethod: 'PayPal', + }, + { + invoice: 'INV003', + paymentStatus: 'Unpaid', + totalAmount: '$350.00', + paymentMethod: 'Bank Transfer', + }, + { + invoice: 'INV004', + paymentStatus: 'Paid', + totalAmount: '$450.00', + paymentMethod: 'Credit Card', + }, + { + invoice: 'INV005', + paymentStatus: 'Paid', + totalAmount: '$550.00', + paymentMethod: 'PayPal', + }, + { + invoice: 'INV006', + paymentStatus: 'Pending', + totalAmount: '$200.00', + paymentMethod: 'Bank Transfer', + }, + { + invoice: 'INV007', + paymentStatus: 'Unpaid', + totalAmount: '$300.00', + paymentMethod: 'Credit Card', + }, +]; const meta = { title: 'Components/Table', @@ -38,7 +69,6 @@ const meta = { subcomponents: { Body: Table.Body as React.ComponentType, Cell: Table.Cell as React.ComponentType, - Pagination: TablePagination as React.ComponentType, Head: Table.Head as React.ComponentType, Header: Table.Header as React.ComponentType, Row: Table.Row as React.ComponentType, @@ -49,87 +79,28 @@ export default meta; type Story = StoryObj; export const Default: Story = { - render: () => { - const table = useReactTable({ - data: components, - columns, - // onSortingChange: setSorting, - // onColumnFiltersChange: setColumnFilters, - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), - getSortedRowModel: getSortedRowModel(), - getFilteredRowModel: getFilteredRowModel(), - // onColumnVisibilityChange: setColumnVisibility, - // onRowSelectionChange: setRowSelection, - // state: { - // sorting, - // columnFilters, - // columnVisibility, - // rowSelection, - // }, - }); - - return ( - <> - - - {table.getHeaderGroups().map(headerGroup => ( - - {headerGroup.headers.map(header => { - return ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext(), - )} - - ); - })} - - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map(row => ( - - {row.getVisibleCells().map(cell => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} - - ))} - - )) - ) : ( - - - No results. - - - )} - - - table.previousPage()} - onClickNext={() => table.nextPage()} - canPrevious={table.getCanPreviousPage()} - canNext={table.getCanNextPage()} - setPageSize={pageSize => table.setPageSize(pageSize)} - /> - - ); - }, + render: () => ( + + + + Invoice + Status + Method + Amount + + + + {invoices.map(invoice => ( + + {invoice.invoice} + {invoice.paymentStatus} + {invoice.paymentMethod} + + {invoice.totalAmount} + + + ))} + + + ), }; diff --git a/packages/canon/src/components/Table/Table.tsx b/packages/canon/src/components/Table/Table.tsx index f67f74883d..99fb181e3d 100644 --- a/packages/canon/src/components/Table/Table.tsx +++ b/packages/canon/src/components/Table/Table.tsx @@ -15,7 +15,6 @@ */ import * as React from 'react'; -/** @public */ const TableRoot = React.forwardRef< HTMLTableElement, React.HTMLAttributes @@ -26,7 +25,6 @@ const TableRoot = React.forwardRef< )); TableRoot.displayName = 'TableRoot'; -/** @public */ const TableHeader = React.forwardRef< HTMLTableSectionElement, React.HTMLAttributes @@ -39,7 +37,6 @@ const TableHeader = React.forwardRef< )); TableHeader.displayName = 'TableHeader'; -/** @public */ const TableBody = React.forwardRef< HTMLTableSectionElement, React.HTMLAttributes @@ -48,7 +45,6 @@ const TableBody = React.forwardRef< )); TableBody.displayName = 'TableBody'; -/** @public */ const TableRow = React.forwardRef< HTMLTableRowElement, React.HTMLAttributes @@ -59,7 +55,6 @@ const TableRow = React.forwardRef< )); TableRow.displayName = 'TableRow'; -/** @public */ const TableHead = React.forwardRef< HTMLTableCellElement, React.ThHTMLAttributes @@ -68,7 +63,6 @@ const TableHead = React.forwardRef< )); TableHead.displayName = 'TableHead'; -/** @public */ const TableCell = React.forwardRef< HTMLTableCellElement, React.TdHTMLAttributes @@ -77,7 +71,6 @@ const TableCell = React.forwardRef< )); TableCell.displayName = 'TableCell'; -/** @public */ const TableCaption = React.forwardRef< HTMLTableCaptionElement, React.HTMLAttributes @@ -90,6 +83,10 @@ const TableCaption = React.forwardRef< )); TableCaption.displayName = 'TableCaption'; +/** + * Table component for displaying tabular data + * @public + */ export const Table = { Root: TableRoot, Header: TableHeader, diff --git a/packages/canon/src/components/Table/index.ts b/packages/canon/src/components/Table/index.ts index 1b13d8b85e..4929b2d36a 100644 --- a/packages/canon/src/components/Table/index.ts +++ b/packages/canon/src/components/Table/index.ts @@ -15,4 +15,3 @@ */ export * from './Table'; -export * from './types'; diff --git a/packages/canon/src/components/Table/types.ts b/packages/canon/src/components/Table/types.ts deleted file mode 100644 index 79628de2bd..0000000000 --- a/packages/canon/src/components/Table/types.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2025 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export interface TablePaginationProps - extends React.HTMLAttributes { - /** - * The current page index. - */ - pageIndex: number; - /** - * The current page size. - */ - pageSize: number; - /** - * The total number of rows. - */ - totalRows: number; - /** - * The function to call when the previous button is clicked. - */ - onClickPrevious: () => void; - /** - * The function to call when the next button is clicked. - */ - onClickNext: () => void; - /** - * Whether the previous button is disabled. - */ - canPrevious: boolean; - /** - * Whether the next button is disabled. - */ - canNext: boolean; - /** - * The function to call when the page size is changed. - */ - setPageSize: (pageSize: number) => void; -} From 8dc32947ec1bf440ffabd30be91de650e55fc7c8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:23:17 +0000 Subject: [PATCH 08/56] chore(deps): update dependency @types/node to v20.17.30 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- canon-docs/yarn.lock | 6 +++--- yarn.lock | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/canon-docs/yarn.lock b/canon-docs/yarn.lock index 00438a8658..2cf157e25a 100644 --- a/canon-docs/yarn.lock +++ b/canon-docs/yarn.lock @@ -916,11 +916,11 @@ __metadata: linkType: hard "@types/node@npm:^20": - version: 20.17.24 - resolution: "@types/node@npm:20.17.24" + version: 20.17.30 + resolution: "@types/node@npm:20.17.30" dependencies: undici-types: "npm:~6.19.2" - checksum: 10/0c5fe294b003656b0f95ffce295c08e7ed3fa814ba9bf891e146dced90322d186e63837db9d9bb14d4f294fbe1a6fa1c89ced0b173380c8aaeb90de6c3eddb3b + checksum: 10/69fd3b177417be77b459e8f1dd4e78c85c686167086920fbf35a9fda301709bbeee6a87ad2591fb1ddd96c65e725ec6bb527a06496626a1c94367d1361048f8d languageName: node linkType: hard diff --git a/yarn.lock b/yarn.lock index 215e6ce4bf..0a603748a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20367,11 +20367,11 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:>=12, @types/node@npm:>=12.0.0, @types/node@npm:>=13.7.0, @types/node@npm:>=18.0.0, @types/node@npm:^22.0.0": - version: 22.13.10 - resolution: "@types/node@npm:22.13.10" + version: 22.14.0 + resolution: "@types/node@npm:22.14.0" dependencies: - undici-types: "npm:~6.20.0" - checksum: 10/57dc6a5e0110ca9edea8d7047082e649fa7fa813f79e4a901653b9174141c622f4336435648baced5b38d9f39843f404fa2d8d7a10981610da26066bc8caab48 + undici-types: "npm:~6.21.0" + checksum: 10/d0669a8a37a18532c886ccfa51eb3fe1e46088deb4d3d27ebcd5d7d68bd6343ad1c7a3fcb85164780a57629359c33a6c917ecff748ea232bceac7692acc96537 languageName: node linkType: hard @@ -20397,20 +20397,20 @@ __metadata: linkType: hard "@types/node@npm:^18.11.18, @types/node@npm:^18.11.9, @types/node@npm:^18.17.15": - version: 18.19.80 - resolution: "@types/node@npm:18.19.80" + version: 18.19.86 + resolution: "@types/node@npm:18.19.86" dependencies: undici-types: "npm:~5.26.4" - checksum: 10/05a54af019aae1fa7d7f8e6475071a7486cb8554f638894b5697ef1cad3b5ac6cbdd5fdfe3d8d70b3af7e56e0245b0033a986c00e1c09e80cb584db06e40c0ba + checksum: 10/83e8f07305b102776c1970c2fbe2347eba15f6cca3d50cf8991d5f4e463ee7e204fbb3dec599892d25e613ed3b93f7fb5794b0de874ea6ba884affbe99670cc2 languageName: node linkType: hard "@types/node@npm:^20.1.1, @types/node@npm:^20.16.0": - version: 20.17.24 - resolution: "@types/node@npm:20.17.24" + version: 20.17.30 + resolution: "@types/node@npm:20.17.30" dependencies: undici-types: "npm:~6.19.2" - checksum: 10/0c5fe294b003656b0f95ffce295c08e7ed3fa814ba9bf891e146dced90322d186e63837db9d9bb14d4f294fbe1a6fa1c89ced0b173380c8aaeb90de6c3eddb3b + checksum: 10/69fd3b177417be77b459e8f1dd4e78c85c686167086920fbf35a9fda301709bbeee6a87ad2591fb1ddd96c65e725ec6bb527a06496626a1c94367d1361048f8d languageName: node linkType: hard @@ -46557,10 +46557,10 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~6.20.0": - version: 6.20.0 - resolution: "undici-types@npm:6.20.0" - checksum: 10/583ac7bbf4ff69931d3985f4762cde2690bb607844c16a5e2fbb92ed312fe4fa1b365e953032d469fa28ba8b224e88a595f0b10a449332f83fa77c695e567dbe +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10/ec8f41aa4359d50f9b59fa61fe3efce3477cc681908c8f84354d8567bb3701fafdddf36ef6bff307024d3feb42c837cf6f670314ba37fc8145e219560e473d14 languageName: node linkType: hard From f0ea3a7f26407fabddd8af3304b708716156e099 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:30:43 +0000 Subject: [PATCH 09/56] fix(deps): update typescript-eslint monorepo to v8.29.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 96 +++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/yarn.lock b/yarn.lock index 215e6ce4bf..496e8d455e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21263,14 +21263,14 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^8.17.0": - version: 8.26.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.26.0" + version: 8.29.1 + resolution: "@typescript-eslint/eslint-plugin@npm:8.29.1" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.26.0" - "@typescript-eslint/type-utils": "npm:8.26.0" - "@typescript-eslint/utils": "npm:8.26.0" - "@typescript-eslint/visitor-keys": "npm:8.26.0" + "@typescript-eslint/scope-manager": "npm:8.29.1" + "@typescript-eslint/type-utils": "npm:8.29.1" + "@typescript-eslint/utils": "npm:8.29.1" + "@typescript-eslint/visitor-keys": "npm:8.29.1" graphemer: "npm:^1.4.0" ignore: "npm:^5.3.1" natural-compare: "npm:^1.4.0" @@ -21279,23 +21279,23 @@ __metadata: "@typescript-eslint/parser": ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/5a3d2445178b815398aa9707e112492ce15c1709e7760fc2d68e64fce609901f4145de923007f50c3bbd6d11ef9f6c7843f1df40ab93c99f8a6610bcf34aa5c2 + checksum: 10/0568894f0ea50e67622605eb347d4e26a41571ad06234478ac695a097e9b3ff6252d6d60034853d7a6b8ec644b5c1d354be21075d691173dd7f2fbecb1102674 languageName: node linkType: hard "@typescript-eslint/parser@npm:^8.16.0": - version: 8.26.0 - resolution: "@typescript-eslint/parser@npm:8.26.0" + version: 8.29.1 + resolution: "@typescript-eslint/parser@npm:8.29.1" dependencies: - "@typescript-eslint/scope-manager": "npm:8.26.0" - "@typescript-eslint/types": "npm:8.26.0" - "@typescript-eslint/typescript-estree": "npm:8.26.0" - "@typescript-eslint/visitor-keys": "npm:8.26.0" + "@typescript-eslint/scope-manager": "npm:8.29.1" + "@typescript-eslint/types": "npm:8.29.1" + "@typescript-eslint/typescript-estree": "npm:8.29.1" + "@typescript-eslint/visitor-keys": "npm:8.29.1" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/86843d488b58d47d4bd45fed25a5afbb033bd844b4517f6401ae2f9af0fdeaedaf5c9dd30e74a7bf5b6029cff10fec0e33ca073b1ffe4795df7403b58aaac58c + checksum: 10/effb4cc24e375e4229e711b3ea8611a205bf81964cb487f518dd4a7d6cecde7324201ce4e2c3cd420e0ce7649899294307da2cd77f145af4efef3a0292189f20 languageName: node linkType: hard @@ -21319,28 +21319,28 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.26.0": - version: 8.26.0 - resolution: "@typescript-eslint/scope-manager@npm:8.26.0" +"@typescript-eslint/scope-manager@npm:8.29.1": + version: 8.29.1 + resolution: "@typescript-eslint/scope-manager@npm:8.29.1" dependencies: - "@typescript-eslint/types": "npm:8.26.0" - "@typescript-eslint/visitor-keys": "npm:8.26.0" - checksum: 10/beccc5c0a815f20d8ccd5f8c4365175df39b62d0eeaf4893ef9b25e2fd96d26ac20e667b91d258584d33b970a471240b1b5bee73b14dac6630a63b5ce0b9ecd4 + "@typescript-eslint/types": "npm:8.29.1" + "@typescript-eslint/visitor-keys": "npm:8.29.1" + checksum: 10/33a02f490b53436729f5ca2e6e0c5b8db72adb455274e5de43bdaada21033e7941aed1d92653321991e186af77f7794dc0ac35d2fce891cdf65a6d3fb192249e languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.26.0": - version: 8.26.0 - resolution: "@typescript-eslint/type-utils@npm:8.26.0" +"@typescript-eslint/type-utils@npm:8.29.1": + version: 8.29.1 + resolution: "@typescript-eslint/type-utils@npm:8.29.1" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.26.0" - "@typescript-eslint/utils": "npm:8.26.0" + "@typescript-eslint/typescript-estree": "npm:8.29.1" + "@typescript-eslint/utils": "npm:8.29.1" debug: "npm:^4.3.4" ts-api-utils: "npm:^2.0.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/cc383418bd208b5787ec93923a5ecb46f424b5f9a5aeb81f51382aa440671b6c85d1fe27527f2e0d711dfaff593d42ca6b57c10c839db800aa4d965d01ac8461 + checksum: 10/3774d6fb32c058b3fa607480e5603fdd2919e07d8babbc00aa703f31c6fd6f7fbc25c25ff246e7e6ac6b77ad0e0b66838a7136aebc31503a58fbe509f68ab2f4 languageName: node linkType: hard @@ -21358,10 +21358,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:8.26.0": - version: 8.26.0 - resolution: "@typescript-eslint/types@npm:8.26.0" - checksum: 10/2fcd2eed0550bc7f95ccf54cf44aae50a38b531deae92c6a616890fff7f335eb2c030553062518fa1bde9e29009b2c92ed59489c2ef9d4e35e9df55f95a6992b +"@typescript-eslint/types@npm:8.29.1": + version: 8.29.1 + resolution: "@typescript-eslint/types@npm:8.29.1" + checksum: 10/99ff59e7af3728858af9b7fdc0165955bcf5cd2eefeaafabfbdd7951fbc8ad869cbb7bed7bcbed6c3c50a8661333b33364dc1de0a0c8f3c64d5882339a5d30dd languageName: node linkType: hard @@ -21402,12 +21402,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.26.0": - version: 8.26.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.26.0" +"@typescript-eslint/typescript-estree@npm:8.29.1": + version: 8.29.1 + resolution: "@typescript-eslint/typescript-estree@npm:8.29.1" dependencies: - "@typescript-eslint/types": "npm:8.26.0" - "@typescript-eslint/visitor-keys": "npm:8.26.0" + "@typescript-eslint/types": "npm:8.29.1" + "@typescript-eslint/visitor-keys": "npm:8.29.1" debug: "npm:^4.3.4" fast-glob: "npm:^3.3.2" is-glob: "npm:^4.0.3" @@ -21416,22 +21416,22 @@ __metadata: ts-api-utils: "npm:^2.0.1" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10/f50101c138a545d0286b4c20be6e873c380cd2f3abac0bef7ce120e8c8297bad2e7ccb4ed4152ab455f2fb2761089d5d75e9d2ba277c3beef3019c99a9067c24 + checksum: 10/dded2ebe4c3287443000e3b825e673d0eddb7c48eb2d373c5b7059ea7dbbeba488d7f1de2e42ed7a9299ccff926a65821f2b5594022b49564026ba01c0cc07ab languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.26.0, @typescript-eslint/utils@npm:^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/utils@npm:^8.8.1": - version: 8.26.0 - resolution: "@typescript-eslint/utils@npm:8.26.0" +"@typescript-eslint/utils@npm:8.29.1, @typescript-eslint/utils@npm:^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/utils@npm:^8.8.1": + version: 8.29.1 + resolution: "@typescript-eslint/utils@npm:8.29.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:8.26.0" - "@typescript-eslint/types": "npm:8.26.0" - "@typescript-eslint/typescript-estree": "npm:8.26.0" + "@typescript-eslint/scope-manager": "npm:8.29.1" + "@typescript-eslint/types": "npm:8.29.1" + "@typescript-eslint/typescript-estree": "npm:8.29.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/69b5ace76c27db4c6d9ce5e4d76aa17c712d90cbe61f3e8603c16a75d8ea38d27c54c4f70937bcd16f6352b26be79ee200f62af60d52b4fc6fe7e88fcaf93fe5 + checksum: 10/1d2c85c97a39e063fe490c0cdb6513716e4735bda0bc937475b44d9224074a5070f888dfed1e75f4a91c8f4d825b44fce90b685e07bda391dfeb2553b9b39a5a languageName: node linkType: hard @@ -21487,13 +21487,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.26.0": - version: 8.26.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.26.0" +"@typescript-eslint/visitor-keys@npm:8.29.1": + version: 8.29.1 + resolution: "@typescript-eslint/visitor-keys@npm:8.29.1" dependencies: - "@typescript-eslint/types": "npm:8.26.0" + "@typescript-eslint/types": "npm:8.29.1" eslint-visitor-keys: "npm:^4.2.0" - checksum: 10/8800c84d711682949e27d72e65b501dbdc5de0009b7d74e289f5f9125aa21107dc55c6ef3dc970431acebe92e19e907d1622de2d2092a79eb8d29ac96670ea75 + checksum: 10/788290c369c13403692d857e0a464b5c2223e1fef28c717b7dbc61140d33697ce43c7d1a7cb7ac585f2012fc702ce2ec489363d34bf566303d3c48fa494803c5 languageName: node linkType: hard From 9721266532fd00af6a63451d2eeaf38aebd76d95 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Wed, 9 Apr 2025 10:00:30 +0200 Subject: [PATCH 10/56] Improve pagination + styles Signed-off-by: Charles de Dreuille --- packages/canon/report.api.md | 95 +++++++++++-------- .../DataTable/DataTable.stories.tsx | 16 +--- .../DataTablePagination.stories.tsx | 29 ++++-- .../Pagination/DataTablePagination.styles.css | 1 - .../Pagination/DataTablePagination.tsx | 50 +++++----- .../components/DataTable/Pagination/types.ts | 36 +------ .../DataTable/Root/DataTableRoot.stories.tsx | 1 - .../components/DataTable/mocked-columns.tsx | 2 +- packages/canon/src/components/Table/Table.tsx | 17 ++-- .../canon/src/components/Table/styles.css | 47 +++------ 10 files changed, 129 insertions(+), 165 deletions(-) diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index 0a16c2abcf..a52a83c308 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -10,15 +10,14 @@ import type { CSSProperties } from 'react'; import { FC } from 'react'; import { FocusEvent as FocusEvent_2 } from 'react'; import { ForwardRefExoticComponent } from 'react'; -import { HTMLAttributes } from 'react'; import { JSX as JSX_2 } from 'react/jsx-runtime'; import { Menu as Menu_2 } from '@base-ui-components/react/menu'; +import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { RefAttributes } from 'react'; import type { RemixiconComponentType } from '@remixicon/react'; import { ScrollArea as ScrollArea_2 } from '@base-ui-components/react/scroll-area'; -import { TdHTMLAttributes } from 'react'; -import { ThHTMLAttributes } from 'react'; +import { Table as Table_2 } from '@tanstack/react-table'; import { Tooltip as Tooltip_2 } from '@base-ui-components/react/tooltip'; import type { useRender } from '@base-ui-components/react/use-render'; @@ -219,6 +218,28 @@ export interface ContainerProps { style?: React.CSSProperties; } +// @public +export const DataTable: { + Root: ForwardRefExoticComponent< + DataTableRootProps & RefAttributes + >; + Pagination: ( + props: DataTablePaginationProps & { + ref?: React.ForwardedRef; + }, + ) => React.ReactElement; +}; + +// @public (undocumented) +export interface DataTablePaginationProps + extends React.HTMLAttributes { + table?: Table_2; +} + +// @public (undocumented) +export interface DataTableRootProps + extends React.HTMLAttributes {} + // @public (undocumented) export type Display = 'none' | 'flex' | 'block' | 'inline'; @@ -1028,43 +1049,37 @@ export type StylingPropDef = { parseValue?: (value: string) => string | undefined; }; -// @public (undocumented) -export const Table: ForwardRefExoticComponent< - HTMLAttributes & RefAttributes ->; - -// @public (undocumented) -export const TableBody: ForwardRefExoticComponent< - HTMLAttributes & - RefAttributes ->; - -// @public (undocumented) -export const TableCell: ForwardRefExoticComponent< - TdHTMLAttributes & RefAttributes ->; - -// @public (undocumented) -export const TableFooter: ForwardRefExoticComponent< - HTMLAttributes & - RefAttributes ->; - -// @public (undocumented) -export const TableHead: ForwardRefExoticComponent< - ThHTMLAttributes & RefAttributes ->; - -// @public (undocumented) -export const TableHeader: ForwardRefExoticComponent< - HTMLAttributes & - RefAttributes ->; - -// @public (undocumented) -export const TableRow: ForwardRefExoticComponent< - HTMLAttributes & RefAttributes ->; +// @public +export const Table: { + Root: React_2.ForwardRefExoticComponent< + React_2.HTMLAttributes & + React_2.RefAttributes + >; + Header: React_2.ForwardRefExoticComponent< + React_2.HTMLAttributes & + React_2.RefAttributes + >; + Body: React_2.ForwardRefExoticComponent< + React_2.HTMLAttributes & + React_2.RefAttributes + >; + Head: React_2.ForwardRefExoticComponent< + React_2.ThHTMLAttributes & + React_2.RefAttributes + >; + Row: React_2.ForwardRefExoticComponent< + React_2.HTMLAttributes & + React_2.RefAttributes + >; + Cell: React_2.ForwardRefExoticComponent< + React_2.TdHTMLAttributes & + React_2.RefAttributes + >; + Caption: React_2.ForwardRefExoticComponent< + React_2.HTMLAttributes & + React_2.RefAttributes + >; +}; // @public (undocumented) const Text_2: ForwardRefExoticComponent< diff --git a/packages/canon/src/components/DataTable/DataTable.stories.tsx b/packages/canon/src/components/DataTable/DataTable.stories.tsx index 46bc81d08c..5a7c5dbfbc 100644 --- a/packages/canon/src/components/DataTable/DataTable.stories.tsx +++ b/packages/canon/src/components/DataTable/DataTable.stories.tsx @@ -14,11 +14,10 @@ * limitations under the License. */ -import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { Table } from '../Table'; import { DataTable } from '.'; -import { components } from './mocked-components'; +import { components, Component } from './mocked-components'; import { columns } from './mocked-columns'; import { flexRender, @@ -38,7 +37,7 @@ type Story = StoryObj; export const Default: Story = { render: () => { - const table = useReactTable({ + const table = useReactTable({ data: components, columns, getCoreRowModel: getCoreRowModel(), @@ -97,16 +96,7 @@ export const Default: Story = { )} - table.previousPage()} - onClickNext={() => table.nextPage()} - canPrevious={table.getCanPreviousPage()} - canNext={table.getCanNextPage()} - setPageSize={pageSize => table.setPageSize(pageSize)} - /> + ); }, diff --git a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx index 54563cd98a..ce3c8288a7 100644 --- a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx +++ b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx @@ -16,6 +16,15 @@ import type { Meta, StoryObj } from '@storybook/react'; import { DataTablePagination } from './DataTablePagination'; +import { + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from '@tanstack/react-table'; +import { components, Component } from '../mocked-components'; +import { columns } from '../mocked-columns'; const meta = { title: 'Components/DataTable/Pagination', @@ -26,14 +35,16 @@ export default meta; type Story = StoryObj; export const Default: Story = { - args: { - pageIndex: 0, - pageSize: 10, - totalRows: 100, - onClickPrevious: () => {}, - onClickNext: () => {}, - canPrevious: true, - canNext: true, - setPageSize: () => {}, + render: () => { + const table = useReactTable({ + data: components, + columns, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + }); + + return ; }, }; diff --git a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.styles.css b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.styles.css index 460a6a5874..0117552401 100644 --- a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.styles.css +++ b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.styles.css @@ -4,7 +4,6 @@ justify-content: space-between; padding-top: var(--canon-space-3); border-top: 1px solid var(--canon-border); - margin-top: var(--canon-space-3); } .canon-TablePagination--left { diff --git a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx index 3a80ba3270..51144aa690 100644 --- a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx +++ b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as React from 'react'; + +import { forwardRef } from 'react'; import { Text } from '../../Text'; import { DataTablePaginationProps } from './types'; import { IconButton } from '../../IconButton'; @@ -21,25 +22,19 @@ import clsx from 'clsx'; import { Select } from '../../Select'; /** @public */ -const DataTablePagination = React.forwardRef< - HTMLDivElement, - DataTablePaginationProps ->(({ className, ...props }, ref) => { - const { - pageIndex, - pageSize, - onClickPrevious, - onClickNext, - canPrevious, - canNext, - totalRows, - setPageSize, - } = props; +function DataTablePagination( + props: DataTablePaginationProps, + ref: React.ForwardedRef, +) { + const { className, table, ...rest } = props; + const pageIndex = table?.getState().pagination.pageIndex; + const pageSize = table?.getState().pagination.pageSize; + return (
- +
)); TableRoot.displayName = 'TableRoot'; @@ -31,7 +30,7 @@ const TableHeader = forwardRef< >(({ className, ...props }, ref) => ( )); @@ -41,7 +40,7 @@ const TableBody = forwardRef< HTMLTableSectionElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( - + )); TableBody.displayName = 'TableBody'; @@ -49,7 +48,7 @@ const TableRow = forwardRef< HTMLTableRowElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( - + {props.children} )); @@ -59,7 +58,7 @@ const TableHead = forwardRef< HTMLTableCellElement, React.ThHTMLAttributes >(({ className, ...props }, ref) => ( - ), enableSorting: false, enableHiding: false, @@ -45,27 +47,28 @@ export const columns: ColumnDef[] = [ accessorKey: 'name', header: 'Name', cell: ({ row }) => ( -
- {row.getValue('name')} - - {row.original.description} - -
+ ), }, { accessorKey: 'owner', header: 'Owner', - cell: ({ row }) => {row.getValue('owner')}, + cell: ({ row }) => ( + + ), }, { accessorKey: 'type', header: 'Type', - cell: ({ row }) => {row.getValue('type')}, + cell: ({ row }) => , }, { accessorKey: 'tags', header: 'Tags', - cell: ({ row }) => {row.getValue('tags')}, + cell: ({ row }) => , }, ]; diff --git a/packages/canon/src/components/Table/Table.tsx b/packages/canon/src/components/Table/Table.tsx index e3d8a4e859..185c110654 100644 --- a/packages/canon/src/components/Table/Table.tsx +++ b/packages/canon/src/components/Table/Table.tsx @@ -13,8 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { forwardRef } from 'react'; import clsx from 'clsx'; +import { TableCell } from './TableCell/TableCell'; +import { TableCellText } from './TableCellText/TableCellText'; +import { TableCellLink } from './TableCellLink/TableCellLink'; const TableRoot = forwardRef< HTMLTableElement, @@ -62,14 +66,6 @@ const TableHead = forwardRef< )); TableHead.displayName = 'TableHead'; -const TableCell = forwardRef< - HTMLTableCellElement, - React.TdHTMLAttributes ->(({ className, ...props }, ref) => ( -
+ )); TableHead.displayName = 'TableHead'; @@ -67,7 +66,7 @@ const TableCell = forwardRef< HTMLTableCellElement, React.TdHTMLAttributes >(({ className, ...props }, ref) => ( - + )); TableCell.displayName = 'TableCell'; @@ -77,7 +76,7 @@ const TableCaption = forwardRef< >(({ className, ...props }, ref) => (
)); diff --git a/packages/canon/src/components/Table/styles.css b/packages/canon/src/components/Table/styles.css index 203a0a57b0..325873f690 100644 --- a/packages/canon/src/components/Table/styles.css +++ b/packages/canon/src/components/Table/styles.css @@ -1,54 +1,31 @@ -.table { - position: relative; - overflow: auto; +.canon-TableRoot { width: 100%; - - table { - width: 100%; - caption-side: bottom; - font-size: var(--canon-font-size-sm); - border-collapse: collapse; - } + caption-side: bottom; + border-collapse: collapse; } -.table-head { +.canon-TableHead { text-align: left; padding: var(--canon-space-3); + font-size: var(--canon-font-size-3); } -.table-body { +.canon-TableBody { tr:last-child { border-bottom: none; } } -.table-row { +.canon-TableRow { + border-bottom: 1px solid var(--canon-border); transition: color 0.2s ease-in-out; - &:hover td { - background-color: var(--canon-bg-tint); - } - - & .table-cell:first-child { - border-top-left-radius: var(--canon-radius-2); - border-bottom-left-radius: var(--canon-radius-2); - box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1), - inset 4px -2px 0 0 var(--canon-bg-surface-1); - padding-left: var(--canon-space-3); - } - - & .table-cell:last-child { - border-top-right-radius: var(--canon-radius-2); - border-bottom-right-radius: var(--canon-radius-2); - box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1), - inset -4px -2px 0 0 var(--canon-bg-surface-1); - padding-right: var(--canon-space-3); + &:hover { + background-color: var(--canon-bg-tint-hover); } } -.table-cell { +.canon-TableCell { padding: var(--canon-space-3); - background-color: var(--canon-bg); - box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1), - inset 0px -2px 0 0 var(--canon-bg-surface-1); + font-size: var(--canon-font-size-3); } From 968e0ae3ac2984fcc8c8a651eb8fc5d8b243a634 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 21:09:10 +0000 Subject: [PATCH 11/56] chore(deps): update dependency typescript to v5.8.3 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- canon-docs/yarn.lock | 12 ++++++------ storybook/yarn.lock | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/canon-docs/yarn.lock b/canon-docs/yarn.lock index 00438a8658..998d4b7665 100644 --- a/canon-docs/yarn.lock +++ b/canon-docs/yarn.lock @@ -5410,22 +5410,22 @@ __metadata: linkType: hard "typescript@npm:^5": - version: 5.8.2 - resolution: "typescript@npm:5.8.2" + version: 5.8.3 + resolution: "typescript@npm:5.8.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/dbc2168a55d56771f4d581997be52bab5cbc09734fec976cfbaabd787e61fb4c6cf9125fd48c6f98054ce549c77ecedefc7f64252a830dd8e9c3381f61fbeb78 + checksum: 10/65c40944c51b513b0172c6710ee62e951b70af6f75d5a5da745cb7fab132c09ae27ffdf7838996e3ed603bb015dadd099006658046941bd0ba30340cc563ae92 languageName: node linkType: hard "typescript@patch:typescript@npm%3A^5#optional!builtin": - version: 5.8.2 - resolution: "typescript@patch:typescript@npm%3A5.8.2#optional!builtin::version=5.8.2&hash=5786d5" + version: 5.8.3 + resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin::version=5.8.3&hash=5786d5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/97920a082ffc57583b1cb6bc4faa502acc156358e03f54c7fc7fdf0b61c439a717f4c9070c449ee9ee683d4cfc3bb203127c2b9794b2950f66d9d307a4ff262c + checksum: 10/b9b1e73dabac5dc730c041325dbd9c99467c1b0d239f1b74ec3b90d831384af3e2ba973946232df670519147eb51a2c20f6f96163cea2b359f03de1e2091cc4f languageName: node linkType: hard diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 472bc07cfb..2a7026e1ae 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -4706,22 +4706,22 @@ __metadata: linkType: hard "typescript@npm:^5.5.3": - version: 5.8.2 - resolution: "typescript@npm:5.8.2" + version: 5.8.3 + resolution: "typescript@npm:5.8.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/dbc2168a55d56771f4d581997be52bab5cbc09734fec976cfbaabd787e61fb4c6cf9125fd48c6f98054ce549c77ecedefc7f64252a830dd8e9c3381f61fbeb78 + checksum: 10/65c40944c51b513b0172c6710ee62e951b70af6f75d5a5da745cb7fab132c09ae27ffdf7838996e3ed603bb015dadd099006658046941bd0ba30340cc563ae92 languageName: node linkType: hard "typescript@patch:typescript@npm%3A^5.5.3#optional!builtin": - version: 5.8.2 - resolution: "typescript@patch:typescript@npm%3A5.8.2#optional!builtin::version=5.8.2&hash=5786d5" + version: 5.8.3 + resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin::version=5.8.3&hash=5786d5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/97920a082ffc57583b1cb6bc4faa502acc156358e03f54c7fc7fdf0b61c439a717f4c9070c449ee9ee683d4cfc3bb203127c2b9794b2950f66d9d307a4ff262c + checksum: 10/b9b1e73dabac5dc730c041325dbd9c99467c1b0d239f1b74ec3b90d831384af3e2ba973946232df670519147eb51a2c20f6f96163cea2b359f03de1e2091cc4f languageName: node linkType: hard From 7756cf73fe257b8f6a3d4b6a7aef59fedfa901c5 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 10 Apr 2025 10:48:03 +0200 Subject: [PATCH 12/56] Fix a few things on DataTable Signed-off-by: Charles de Dreuille --- packages/canon/report.api.md | 42 +++++++++---------- .../DataTable/Root/DataTableRoot.tsx | 4 +- .../src/components/Select/Select.stories.tsx | 2 +- .../src/components/Table/Table.stories.tsx | 12 +++--- .../canon/src/components/Table/styles.css | 6 +-- .../TextField/TextField.stories.tsx | 4 +- 6 files changed, 34 insertions(+), 36 deletions(-) diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index a52a83c308..e03b2dfe1e 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -10,14 +10,16 @@ import type { CSSProperties } from 'react'; import { FC } from 'react'; import { FocusEvent as FocusEvent_2 } from 'react'; import { ForwardRefExoticComponent } from 'react'; +import { HTMLAttributes } from 'react'; import { JSX as JSX_2 } from 'react/jsx-runtime'; import { Menu as Menu_2 } from '@base-ui-components/react/menu'; -import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { RefAttributes } from 'react'; import type { RemixiconComponentType } from '@remixicon/react'; import { ScrollArea as ScrollArea_2 } from '@base-ui-components/react/scroll-area'; import { Table as Table_2 } from '@tanstack/react-table'; +import { TdHTMLAttributes } from 'react'; +import { ThHTMLAttributes } from 'react'; import { Tooltip as Tooltip_2 } from '@base-ui-components/react/tooltip'; import type { useRender } from '@base-ui-components/react/use-render'; @@ -1051,33 +1053,29 @@ export type StylingPropDef = { // @public export const Table: { - Root: React_2.ForwardRefExoticComponent< - React_2.HTMLAttributes & - React_2.RefAttributes + Root: ForwardRefExoticComponent< + HTMLAttributes & RefAttributes >; - Header: React_2.ForwardRefExoticComponent< - React_2.HTMLAttributes & - React_2.RefAttributes + Header: ForwardRefExoticComponent< + HTMLAttributes & + RefAttributes >; - Body: React_2.ForwardRefExoticComponent< - React_2.HTMLAttributes & - React_2.RefAttributes + Body: ForwardRefExoticComponent< + HTMLAttributes & + RefAttributes >; - Head: React_2.ForwardRefExoticComponent< - React_2.ThHTMLAttributes & - React_2.RefAttributes + Head: ForwardRefExoticComponent< + ThHTMLAttributes & RefAttributes >; - Row: React_2.ForwardRefExoticComponent< - React_2.HTMLAttributes & - React_2.RefAttributes + Row: ForwardRefExoticComponent< + HTMLAttributes & RefAttributes >; - Cell: React_2.ForwardRefExoticComponent< - React_2.TdHTMLAttributes & - React_2.RefAttributes + Cell: ForwardRefExoticComponent< + TdHTMLAttributes & RefAttributes >; - Caption: React_2.ForwardRefExoticComponent< - React_2.HTMLAttributes & - React_2.RefAttributes + Caption: ForwardRefExoticComponent< + HTMLAttributes & + RefAttributes >; }; diff --git a/packages/canon/src/components/DataTable/Root/DataTableRoot.tsx b/packages/canon/src/components/DataTable/Root/DataTableRoot.tsx index 50cc81b7ab..8ca6db736c 100644 --- a/packages/canon/src/components/DataTable/Root/DataTableRoot.tsx +++ b/packages/canon/src/components/DataTable/Root/DataTableRoot.tsx @@ -14,12 +14,12 @@ * limitations under the License. */ -import * as React from 'react'; +import { forwardRef } from 'react'; import clsx from 'clsx'; import { DataTableRootProps } from './types'; /** @public */ -const DataTableRoot = React.forwardRef( +const DataTableRoot = forwardRef( ({ className, ...props }, ref) => { return (
, - Cell: Table.Cell as React.ComponentType, - Head: Table.Head as React.ComponentType, - Header: Table.Header as React.ComponentType, - Row: Table.Row as React.ComponentType, + Body: Table.Body as ComponentType, + Cell: Table.Cell as ComponentType, + Head: Table.Head as ComponentType, + Header: Table.Header as ComponentType, + Row: Table.Row as ComponentType, }, } satisfies Meta; diff --git a/packages/canon/src/components/Table/styles.css b/packages/canon/src/components/Table/styles.css index 325873f690..982b2d1c69 100644 --- a/packages/canon/src/components/Table/styles.css +++ b/packages/canon/src/components/Table/styles.css @@ -19,10 +19,10 @@ .canon-TableRow { border-bottom: 1px solid var(--canon-border); transition: color 0.2s ease-in-out; +} - &:hover { - background-color: var(--canon-bg-tint-hover); - } +.canon-TableBody .canon-TableRow:hover { + background-color: var(--canon-gray-2); } .canon-TableCell { diff --git a/packages/canon/src/components/TextField/TextField.stories.tsx b/packages/canon/src/components/TextField/TextField.stories.tsx index 34f81af4f4..8594a8c650 100644 --- a/packages/canon/src/components/TextField/TextField.stories.tsx +++ b/packages/canon/src/components/TextField/TextField.stories.tsx @@ -95,14 +95,14 @@ export const Responsive: Story = { }, }; -export const withError: Story = { +export const WithError: Story = { args: { ...WithLabel.args, error: 'Invalid URL', }, }; -export const withErrorAndDescription: Story = { +export const WithErrorAndDescription: Story = { args: { ...WithLabel.args, error: 'Invalid URL', From febc78ebd3ec296d163d22cbf55be9f466231f1b Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 10 Apr 2025 11:03:11 +0200 Subject: [PATCH 13/56] Build CSS Signed-off-by: Charles de Dreuille --- packages/canon/css/components.css | 49 +++++++++---------------------- packages/canon/css/datatable.css | 1 - packages/canon/css/styles.css | 49 +++++++++---------------------- packages/canon/css/table.css | 48 +++++++++--------------------- 4 files changed, 42 insertions(+), 105 deletions(-) diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index b5a81a9586..38a03e9dd1 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -103,7 +103,6 @@ .canon-TablePagination { padding-top: var(--canon-space-3); border-top: 1px solid var(--canon-border); - margin-top: var(--canon-space-3); justify-content: space-between; align-items: center; display: flex; @@ -203,56 +202,36 @@ display: flex; } -.table { +.canon-TableRoot { + caption-side: bottom; + border-collapse: collapse; width: 100%; - position: relative; - overflow: auto; - - & table { - caption-side: bottom; - width: 100%; - font-size: var(--canon-font-size-sm); - border-collapse: collapse; - } } -.table-head { +.canon-TableHead { text-align: left; padding: var(--canon-space-3); + font-size: var(--canon-font-size-3); } -.table-body { +.canon-TableBody { & tr:last-child { border-bottom: none; } } -.table-row { +.canon-TableRow { + border-bottom: 1px solid var(--canon-border); transition: color .2s ease-in-out; - - &:hover td { - background-color: var(--canon-bg-tint); - } - - & .table-cell:first-child { - border-top-left-radius: var(--canon-radius-2); - border-bottom-left-radius: var(--canon-radius-2); - box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1), inset 4px -2px 0 0 var(--canon-bg-surface-1); - padding-left: var(--canon-space-3); - } - - & .table-cell:last-child { - border-top-right-radius: var(--canon-radius-2); - border-bottom-right-radius: var(--canon-radius-2); - box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1), inset -4px -2px 0 0 var(--canon-bg-surface-1); - padding-right: var(--canon-space-3); - } } -.table-cell { +.canon-TableBody .canon-TableRow:hover { + background-color: var(--canon-gray-2); +} + +.canon-TableCell { padding: var(--canon-space-3); - background-color: var(--canon-bg); - box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1), inset 0px -2px 0 0 var(--canon-bg-surface-1); + font-size: var(--canon-font-size-3); } .canon-Text { diff --git a/packages/canon/css/datatable.css b/packages/canon/css/datatable.css index c33ecce578..1773f6b980 100644 --- a/packages/canon/css/datatable.css +++ b/packages/canon/css/datatable.css @@ -1,7 +1,6 @@ .canon-TablePagination { padding-top: var(--canon-space-3); border-top: 1px solid var(--canon-border); - margin-top: var(--canon-space-3); justify-content: space-between; align-items: center; display: flex; diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index 12097151da..57b4eb4d02 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9327,7 +9327,6 @@ .canon-TablePagination { padding-top: var(--canon-space-3); border-top: 1px solid var(--canon-border); - margin-top: var(--canon-space-3); justify-content: space-between; align-items: center; display: flex; @@ -9427,56 +9426,36 @@ display: flex; } -.table { +.canon-TableRoot { + caption-side: bottom; + border-collapse: collapse; width: 100%; - position: relative; - overflow: auto; - - & table { - caption-side: bottom; - width: 100%; - font-size: var(--canon-font-size-sm); - border-collapse: collapse; - } } -.table-head { +.canon-TableHead { text-align: left; padding: var(--canon-space-3); + font-size: var(--canon-font-size-3); } -.table-body { +.canon-TableBody { & tr:last-child { border-bottom: none; } } -.table-row { +.canon-TableRow { + border-bottom: 1px solid var(--canon-border); transition: color .2s ease-in-out; - - &:hover td { - background-color: var(--canon-bg-tint); - } - - & .table-cell:first-child { - border-top-left-radius: var(--canon-radius-2); - border-bottom-left-radius: var(--canon-radius-2); - box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1), inset 4px -2px 0 0 var(--canon-bg-surface-1); - padding-left: var(--canon-space-3); - } - - & .table-cell:last-child { - border-top-right-radius: var(--canon-radius-2); - border-bottom-right-radius: var(--canon-radius-2); - box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1), inset -4px -2px 0 0 var(--canon-bg-surface-1); - padding-right: var(--canon-space-3); - } } -.table-cell { +.canon-TableBody .canon-TableRow:hover { + background-color: var(--canon-gray-2); +} + +.canon-TableCell { padding: var(--canon-space-3); - background-color: var(--canon-bg); - box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1), inset 0px -2px 0 0 var(--canon-bg-surface-1); + font-size: var(--canon-font-size-3); } .canon-Text { diff --git a/packages/canon/css/table.css b/packages/canon/css/table.css index a146010779..dd25410a0e 100644 --- a/packages/canon/css/table.css +++ b/packages/canon/css/table.css @@ -1,51 +1,31 @@ -.table { +.canon-TableRoot { + caption-side: bottom; + border-collapse: collapse; width: 100%; - position: relative; - overflow: auto; - - & table { - caption-side: bottom; - width: 100%; - font-size: var(--canon-font-size-sm); - border-collapse: collapse; - } } -.table-head { +.canon-TableHead { text-align: left; padding: var(--canon-space-3); + font-size: var(--canon-font-size-3); } -.table-body { +.canon-TableBody { & tr:last-child { border-bottom: none; } } -.table-row { +.canon-TableRow { + border-bottom: 1px solid var(--canon-border); transition: color .2s ease-in-out; - - &:hover td { - background-color: var(--canon-bg-tint); - } - - & .table-cell:first-child { - border-top-left-radius: var(--canon-radius-2); - border-bottom-left-radius: var(--canon-radius-2); - box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1), inset 4px -2px 0 0 var(--canon-bg-surface-1); - padding-left: var(--canon-space-3); - } - - & .table-cell:last-child { - border-top-right-radius: var(--canon-radius-2); - border-bottom-right-radius: var(--canon-radius-2); - box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1), inset -4px -2px 0 0 var(--canon-bg-surface-1); - padding-right: var(--canon-space-3); - } } -.table-cell { +.canon-TableBody .canon-TableRow:hover { + background-color: var(--canon-gray-2); +} + +.canon-TableCell { padding: var(--canon-space-3); - background-color: var(--canon-bg); - box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1), inset 0px -2px 0 0 var(--canon-bg-surface-1); + font-size: var(--canon-font-size-3); } From 9806fd8adf4047db465a89819532350e65d5face Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 11 Apr 2025 09:12:35 +0200 Subject: [PATCH 14/56] Update DataTable Signed-off-by: Charles de Dreuille --- packages/canon/report.api.md | 72 +++++++++-- .../DataTable/DataTable.stories.tsx | 49 ++++---- .../src/components/DataTable/DataTable.tsx | 76 ++++++++++++ .../DataTablePagination.stories.tsx | 11 +- .../Pagination/DataTablePagination.tsx | 116 +++++++++--------- .../components/DataTable/Pagination/types.ts | 11 +- .../DataTable/Root/DataTableRoot.stories.tsx | 34 ----- .../DataTable/Root/DataTableRoot.tsx | 43 +++++-- .../src/components/DataTable/Root/types.ts | 11 +- .../canon/src/components/DataTable/index.ts | 13 +- .../components/DataTable/mocked-components.ts | 2 +- .../src/components/Select/Select.stories.tsx | 26 ++++ 12 files changed, 297 insertions(+), 167 deletions(-) create mode 100644 packages/canon/src/components/DataTable/DataTable.tsx delete mode 100644 packages/canon/src/components/DataTable/Root/DataTableRoot.stories.tsx diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index e03b2dfe1e..ec859f34e6 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -222,25 +222,71 @@ export interface ContainerProps { // @public export const DataTable: { - Root: ForwardRefExoticComponent< - DataTableRootProps & RefAttributes + Root: ( + props: { + table: Table_2; + } & React.HTMLAttributes, + ) => JSX.Element; + Pagination: ForwardRefExoticComponent< + DataTablePaginationProps & RefAttributes + >; + Table: ForwardRefExoticComponent< + Omit< + HTMLAttributes & RefAttributes, + 'ref' + > & + RefAttributes + >; + Header: ForwardRefExoticComponent< + Omit< + HTMLAttributes & + RefAttributes, + 'ref' + > & + RefAttributes + >; + Body: ForwardRefExoticComponent< + Omit< + HTMLAttributes & + RefAttributes, + 'ref' + > & + RefAttributes + >; + Row: ForwardRefExoticComponent< + Omit< + HTMLAttributes & RefAttributes, + 'ref' + > & + RefAttributes + >; + Cell: ForwardRefExoticComponent< + Omit< + TdHTMLAttributes & + RefAttributes, + 'ref' + > & + RefAttributes + >; + Head: ForwardRefExoticComponent< + Omit< + ThHTMLAttributes & + RefAttributes, + 'ref' + > & + RefAttributes >; - Pagination: ( - props: DataTablePaginationProps & { - ref?: React.ForwardedRef; - }, - ) => React.ReactElement; }; // @public (undocumented) -export interface DataTablePaginationProps - extends React.HTMLAttributes { - table?: Table_2; -} +export interface DataTablePaginationProps + extends React.HTMLAttributes {} // @public (undocumented) -export interface DataTableRootProps - extends React.HTMLAttributes {} +export interface DataTableRootProps + extends React.HTMLAttributes { + table: Table_2; +} // @public (undocumented) export type Display = 'none' | 'flex' | 'block' | 'inline'; diff --git a/packages/canon/src/components/DataTable/DataTable.stories.tsx b/packages/canon/src/components/DataTable/DataTable.stories.tsx index 5a7c5dbfbc..9bcdc23ad3 100644 --- a/packages/canon/src/components/DataTable/DataTable.stories.tsx +++ b/packages/canon/src/components/DataTable/DataTable.stories.tsx @@ -15,16 +15,13 @@ */ import type { Meta, StoryObj } from '@storybook/react'; -import { Table } from '../Table'; import { DataTable } from '.'; -import { components, Component } from './mocked-components'; +import { data, Component } from './mocked-components'; import { columns } from './mocked-columns'; import { flexRender, getCoreRowModel, - getFilteredRowModel, getPaginationRowModel, - getSortedRowModel, useReactTable, } from '@tanstack/react-table'; @@ -38,65 +35,63 @@ type Story = StoryObj; export const Default: Story = { render: () => { const table = useReactTable({ - data: components, + data, columns, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), - getSortedRowModel: getSortedRowModel(), - getFilteredRowModel: getFilteredRowModel(), }); return ( - - - + + + {table.getHeaderGroups().map(headerGroup => ( - + {headerGroup.headers.map(header => { return ( - + {header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext(), )} - + ); })} - + ))} - - + + {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map(row => ( - {row.getVisibleCells().map(cell => ( - + {flexRender( cell.column.columnDef.cell, cell.getContext(), )} - + ))} - + )) ) : ( - - + No results. - - + + )} - - - + + + ); }, diff --git a/packages/canon/src/components/DataTable/DataTable.tsx b/packages/canon/src/components/DataTable/DataTable.tsx new file mode 100644 index 0000000000..83251b473b --- /dev/null +++ b/packages/canon/src/components/DataTable/DataTable.tsx @@ -0,0 +1,76 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { forwardRef } from 'react'; +import { Table } from '../Table'; +import { DataTableRoot } from './Root/DataTableRoot'; +import { DataTablePagination } from './Pagination/DataTablePagination'; +import { Table as TanstackTable } from '@tanstack/react-table'; + +const TableRoot = forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ); +TableRoot.displayName = Table.Root.displayName; + +const TableHeader = forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ); +TableHeader.displayName = Table.Header.displayName; + +const TableBody = forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ); +TableBody.displayName = Table.Body.displayName; + +const TableRow = forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ); +TableRow.displayName = Table.Row.displayName; + +const TableCell = forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ); +TableCell.displayName = Table.Cell.displayName; + +const TableHead = forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ); +TableHead.displayName = Table.Head.displayName; + +/** + * DataTable component for displaying tabular data with pagination + * @public + */ +export const DataTable = { + Root: DataTableRoot as ( + props: { + table: TanstackTable; + } & React.HTMLAttributes, + ) => JSX.Element, + Pagination: DataTablePagination, + Table: TableRoot, + Header: TableHeader, + Body: TableBody, + Row: TableRow, + Cell: TableCell, + Head: TableHead, +}; diff --git a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx index ce3c8288a7..7a45b111b9 100644 --- a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx +++ b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx @@ -23,8 +23,9 @@ import { getSortedRowModel, useReactTable, } from '@tanstack/react-table'; -import { components, Component } from '../mocked-components'; +import { data, Component } from '../mocked-components'; import { columns } from '../mocked-columns'; +import { DataTable } from '../DataTable'; const meta = { title: 'Components/DataTable/Pagination', @@ -37,7 +38,7 @@ type Story = StoryObj; export const Default: Story = { render: () => { const table = useReactTable({ - data: components, + data, columns, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), @@ -45,6 +46,10 @@ export const Default: Story = { getFilteredRowModel: getFilteredRowModel(), }); - return ; + return ( + + + + ); }, }; diff --git a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx index 51144aa690..c85b90f743 100644 --- a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx +++ b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx @@ -20,67 +20,67 @@ import { DataTablePaginationProps } from './types'; import { IconButton } from '../../IconButton'; import clsx from 'clsx'; import { Select } from '../../Select'; +import { useDataTable } from '../Root/DataTableRoot'; /** @public */ -function DataTablePagination( - props: DataTablePaginationProps, - ref: React.ForwardedRef, -) { - const { className, table, ...rest } = props; - const pageIndex = table?.getState().pagination.pageIndex; - const pageSize = table?.getState().pagination.pageSize; +const DataTablePagination = forwardRef( + ( + props: DataTablePaginationProps, + ref: React.ForwardedRef, + ) => { + const { className, ...rest } = props; + const { table } = useDataTable(); + const pageIndex = table?.getState().pagination.pageIndex; + const pageSize = table?.getState().pagination.pageSize; - return ( -
-
- { + table?.setPageSize(Number(value)); + }} + /> +
+
+ {`${(pageIndex ?? 0) * (pageSize ?? 10) + 1} - ${ + ((pageIndex ?? 0) + 1) * (pageSize ?? 10) + } of ${table?.getRowCount()}`} + table?.previousPage()} + disabled={!table?.getCanPreviousPage()} + icon="chevron-left" + /> + table?.nextPage()} + disabled={!table?.getCanNextPage()} + icon="chevron-right" + /> +
-
- {`${(pageIndex ?? 0) * (pageSize ?? 10) + 1} - ${ - ((pageIndex ?? 0) + 1) * (pageSize ?? 10) - } of ${table?.getRowCount()}`} - table?.previousPage()} - disabled={!table?.getCanPreviousPage()} - icon="chevron-left" - /> - table?.nextPage()} - disabled={!table?.getCanNextPage()} - icon="chevron-right" - /> -
-
- ); -} - -const ForwardedDataTablePagination = forwardRef(DataTablePagination) as ( - props: DataTablePaginationProps & { - ref?: React.ForwardedRef; + ); }, -) => React.ReactElement; +); -export { ForwardedDataTablePagination as DataTablePagination }; +DataTablePagination.displayName = 'DataTablePagination'; + +export { DataTablePagination }; diff --git a/packages/canon/src/components/DataTable/Pagination/types.ts b/packages/canon/src/components/DataTable/Pagination/types.ts index d8dbe76582..b2a895c465 100644 --- a/packages/canon/src/components/DataTable/Pagination/types.ts +++ b/packages/canon/src/components/DataTable/Pagination/types.ts @@ -14,13 +14,6 @@ * limitations under the License. */ -import { Table } from '@tanstack/react-table'; - /** @public */ -export interface DataTablePaginationProps - extends React.HTMLAttributes { - /** - * The table instance. - */ - table?: Table; -} +export interface DataTablePaginationProps + extends React.HTMLAttributes {} diff --git a/packages/canon/src/components/DataTable/Root/DataTableRoot.stories.tsx b/packages/canon/src/components/DataTable/Root/DataTableRoot.stories.tsx deleted file mode 100644 index 371183aa7c..0000000000 --- a/packages/canon/src/components/DataTable/Root/DataTableRoot.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Meta, StoryObj } from '@storybook/react'; -import { DataTableRoot } from './DataTableRoot'; -import { DataTablePagination } from '../Pagination/DataTablePagination'; -import { Default as DataTablePaginationDefault } from '../Pagination/DataTablePagination.stories'; - -const meta = { - title: 'Components/DataTable/Root', - component: DataTableRoot, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - children: , - }, -}; diff --git a/packages/canon/src/components/DataTable/Root/DataTableRoot.tsx b/packages/canon/src/components/DataTable/Root/DataTableRoot.tsx index 8ca6db736c..8207fe4412 100644 --- a/packages/canon/src/components/DataTable/Root/DataTableRoot.tsx +++ b/packages/canon/src/components/DataTable/Root/DataTableRoot.tsx @@ -14,22 +14,49 @@ * limitations under the License. */ -import { forwardRef } from 'react'; +import { forwardRef, createContext, useContext } from 'react'; import clsx from 'clsx'; import { DataTableRootProps } from './types'; +import { Table } from '@tanstack/react-table'; + +type DataTableContextType = { + table: Table; +}; /** @public */ -const DataTableRoot = forwardRef( - ({ className, ...props }, ref) => { +export const DataTableContext = createContext | null>( + null, +); + +/** @public */ +const DataTableRoot = forwardRef( + ( + props: DataTableRootProps, + ref: React.ForwardedRef, + ) => { + const { className, table, ...rest } = props; + return ( -
+ +
+ ); }, ); + DataTableRoot.displayName = 'DataTableRoot'; export { DataTableRoot }; + +/** @public */ +export function useDataTable() { + const context = useContext(DataTableContext); + if (!context) { + throw new Error('useDataTable must be used within a DataTableRoot'); + } + return context as DataTableContextType; +} diff --git a/packages/canon/src/components/DataTable/Root/types.ts b/packages/canon/src/components/DataTable/Root/types.ts index 420181feaf..0a14f81fb3 100644 --- a/packages/canon/src/components/DataTable/Root/types.ts +++ b/packages/canon/src/components/DataTable/Root/types.ts @@ -14,6 +14,13 @@ * limitations under the License. */ +import { Table } from '@tanstack/react-table'; + /** @public */ -export interface DataTableRootProps - extends React.HTMLAttributes {} +export interface DataTableRootProps + extends React.HTMLAttributes { + /** + * The table instance. + */ + table: Table; +} diff --git a/packages/canon/src/components/DataTable/index.ts b/packages/canon/src/components/DataTable/index.ts index 688df19b90..d684c2b48c 100644 --- a/packages/canon/src/components/DataTable/index.ts +++ b/packages/canon/src/components/DataTable/index.ts @@ -14,17 +14,6 @@ * limitations under the License. */ -import { DataTableRoot } from './Root/DataTableRoot'; -import { DataTablePagination } from './Pagination/DataTablePagination'; - -/** - * DataTable component for displaying tabular data with pagination - * @public - */ -export const DataTable = { - Root: DataTableRoot, - Pagination: DataTablePagination, -}; - +export * from './DataTable'; export * from './Root/types'; export * from './Pagination/types'; diff --git a/packages/canon/src/components/DataTable/mocked-components.ts b/packages/canon/src/components/DataTable/mocked-components.ts index f7861023d3..35b43d887f 100644 --- a/packages/canon/src/components/DataTable/mocked-components.ts +++ b/packages/canon/src/components/DataTable/mocked-components.ts @@ -21,7 +21,7 @@ export interface Component { tags?: string[]; } -export const components: Component[] = [ +export const data: Component[] = [ { name: 'authentication-and-authorization-service', owner: 'security-team', diff --git a/packages/canon/src/components/Select/Select.stories.tsx b/packages/canon/src/components/Select/Select.stories.tsx index dc951a390f..0a621162b5 100644 --- a/packages/canon/src/components/Select/Select.stories.tsx +++ b/packages/canon/src/components/Select/Select.stories.tsx @@ -252,3 +252,29 @@ export const WithErrorAndDescription: Story = { error: 'Invalid font family', }, }; + +export const WithLongLabels: Story = { + args: { + label: 'Document Template', + options: [ + { + value: 'annual-report-2024', + label: + 'Annual Financial Report and Strategic Planning Document for Fiscal Year 2024 with Comprehensive Analysis of Market Trends, Competitive Landscape, Financial Performance Metrics, Revenue Projections, Cost Optimization Strategies, Risk Assessment, and Long-term Growth Initiatives Across All Business Units and Geographical Regions', + }, + { + value: 'product-roadmap', + label: + 'Comprehensive Product Development Roadmap and Feature Implementation Timeline Including Detailed Technical Specifications, Resource Allocation Plans, Cross-functional Team Dependencies, Milestone Tracking, Quality Assurance Procedures, User Acceptance Testing Protocols, and Post-launch Support Strategy for All Product Lines and Service Offerings', + }, + { + value: 'user-guide', + label: + 'Detailed User Guide and Technical Documentation for Advanced System Features Covering Installation Procedures, Configuration Settings, Security Protocols, Troubleshooting Guidelines, Best Practices, Common Use Cases, Performance Optimization Tips, Integration Methods, API Documentation, and Frequently Asked Questions with Step-by-Step Solutions', + }, + ], + placeholder: 'Select a document template', + name: 'template', + style: { maxWidth: 400 }, + }, +}; From 0e654bf4b62e3b7bce793313cb56d92d5bcdc8ef Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 11 Apr 2025 09:37:20 +0200 Subject: [PATCH 15/56] Improve DataTable Signed-off-by: Charles de Dreuille --- .changeset/quiet-rockets-throw.md | 5 +++++ packages/canon/css/components.css | 10 +++++++--- packages/canon/css/datatable.css | 10 +++++++--- packages/canon/css/styles.css | 10 +++++++--- .../src/components/DataTable/DataTable.stories.tsx | 4 ++-- .../Pagination/DataTablePagination.stories.tsx | 4 ++-- .../Pagination/DataTablePagination.styles.css | 10 +++++++--- .../DataTable/Pagination/DataTablePagination.tsx | 7 ++++--- .../canon/src/components/DataTable/mocked-columns.tsx | 4 ++-- .../src/components/DataTable/mocked-components.ts | 4 ++-- 10 files changed, 45 insertions(+), 23 deletions(-) create mode 100644 .changeset/quiet-rockets-throw.md diff --git a/.changeset/quiet-rockets-throw.md b/.changeset/quiet-rockets-throw.md new file mode 100644 index 0000000000..a73dbeb51b --- /dev/null +++ b/.changeset/quiet-rockets-throw.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Add new DataTable component. diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index d8f289d019..e138c9bdd6 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -100,7 +100,7 @@ display: flex; } -.canon-TablePagination { +.canon-DataTablePagination { padding-top: var(--canon-space-3); border-top: 1px solid var(--canon-border); justify-content: space-between; @@ -108,19 +108,23 @@ display: flex; } -.canon-TablePagination--left { +.canon-DataTablePagination--left { justify-content: space-between; align-items: center; display: flex; } -.canon-TablePagination--right { +.canon-DataTablePagination--right { justify-content: space-between; align-items: center; gap: var(--canon-space-2); display: flex; } +.canon-DataTablePagination--select { + min-width: 168px; +} + .canon-Flex { display: flex; } diff --git a/packages/canon/css/datatable.css b/packages/canon/css/datatable.css index 1773f6b980..081bc11ce9 100644 --- a/packages/canon/css/datatable.css +++ b/packages/canon/css/datatable.css @@ -1,4 +1,4 @@ -.canon-TablePagination { +.canon-DataTablePagination { padding-top: var(--canon-space-3); border-top: 1px solid var(--canon-border); justify-content: space-between; @@ -6,15 +6,19 @@ display: flex; } -.canon-TablePagination--left { +.canon-DataTablePagination--left { justify-content: space-between; align-items: center; display: flex; } -.canon-TablePagination--right { +.canon-DataTablePagination--right { justify-content: space-between; align-items: center; gap: var(--canon-space-2); display: flex; } + +.canon-DataTablePagination--select { + min-width: 168px; +} diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index 395b8ccde8..46fd6f05d1 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9324,7 +9324,7 @@ display: flex; } -.canon-TablePagination { +.canon-DataTablePagination { padding-top: var(--canon-space-3); border-top: 1px solid var(--canon-border); justify-content: space-between; @@ -9332,19 +9332,23 @@ display: flex; } -.canon-TablePagination--left { +.canon-DataTablePagination--left { justify-content: space-between; align-items: center; display: flex; } -.canon-TablePagination--right { +.canon-DataTablePagination--right { justify-content: space-between; align-items: center; gap: var(--canon-space-2); display: flex; } +.canon-DataTablePagination--select { + min-width: 168px; +} + .canon-Flex { display: flex; } diff --git a/packages/canon/src/components/DataTable/DataTable.stories.tsx b/packages/canon/src/components/DataTable/DataTable.stories.tsx index 9bcdc23ad3..3119821d92 100644 --- a/packages/canon/src/components/DataTable/DataTable.stories.tsx +++ b/packages/canon/src/components/DataTable/DataTable.stories.tsx @@ -16,7 +16,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { DataTable } from '.'; -import { data, Component } from './mocked-components'; +import { data, DataProps } from './mocked-components'; import { columns } from './mocked-columns'; import { flexRender, @@ -34,7 +34,7 @@ type Story = StoryObj; export const Default: Story = { render: () => { - const table = useReactTable({ + const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), diff --git a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx index 7a45b111b9..a2f931aed6 100644 --- a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx +++ b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.stories.tsx @@ -23,7 +23,7 @@ import { getSortedRowModel, useReactTable, } from '@tanstack/react-table'; -import { data, Component } from '../mocked-components'; +import { data, DataProps } from '../mocked-components'; import { columns } from '../mocked-columns'; import { DataTable } from '../DataTable'; @@ -37,7 +37,7 @@ type Story = StoryObj; export const Default: Story = { render: () => { - const table = useReactTable({ + const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), diff --git a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.styles.css b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.styles.css index 0117552401..ff2e8e232c 100644 --- a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.styles.css +++ b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.styles.css @@ -1,4 +1,4 @@ -.canon-TablePagination { +.canon-DataTablePagination { display: flex; align-items: center; justify-content: space-between; @@ -6,15 +6,19 @@ border-top: 1px solid var(--canon-border); } -.canon-TablePagination--left { +.canon-DataTablePagination--left { display: flex; align-items: center; justify-content: space-between; } -.canon-TablePagination--right { +.canon-DataTablePagination--right { display: flex; align-items: center; justify-content: space-between; gap: var(--canon-space-2); } + +.canon-DataTablePagination--select { + min-width: 168px; +} diff --git a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx index c85b90f743..ef74d12db6 100644 --- a/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx +++ b/packages/canon/src/components/DataTable/Pagination/DataTablePagination.tsx @@ -36,10 +36,10 @@ const DataTablePagination = forwardRef( return (
-
+
{ - table?.setPageSize(Number(value)); - }} - className="canon-DataTablePagination--select" - /> + {!table.options.manualPagination && ( + Date: Sat, 12 Apr 2025 15:21:36 +0200 Subject: [PATCH 31/56] Add new breakpoint-helpers Signed-off-by: Charles de Dreuille --- .changeset/cyan-radios-stand.md | 5 ++++ packages/canon/report.api.md | 7 +++++ packages/canon/src/hooks/useBreakpoint.ts | 28 ++++++++++++++++--- .../canon/src/hooks/useResponsiveValue.ts | 6 ++-- packages/canon/src/index.ts | 3 ++ 5 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 .changeset/cyan-radios-stand.md diff --git a/.changeset/cyan-radios-stand.md b/.changeset/cyan-radios-stand.md new file mode 100644 index 0000000000..c7b204ac47 --- /dev/null +++ b/.changeset/cyan-radios-stand.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Add new breakpoint helpers up(), down() and current breakpoint to help you use our breakpoints in your React components. diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index 0a16c2abcf..241b315220 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -1143,6 +1143,13 @@ export const Tooltip: { >; }; +// @public (undocumented) +export const useBreakpoint: () => { + breakpoint: Breakpoint; + up: (key: Breakpoint) => boolean; + down: (key: Breakpoint) => boolean; +}; + // @public (undocumented) export const useIcons: () => IconContextProps; diff --git a/packages/canon/src/hooks/useBreakpoint.ts b/packages/canon/src/hooks/useBreakpoint.ts index a6dacde525..a529f5ef06 100644 --- a/packages/canon/src/hooks/useBreakpoint.ts +++ b/packages/canon/src/hooks/useBreakpoint.ts @@ -25,18 +25,38 @@ export const breakpoints: { name: string; id: Breakpoint; value: number }[] = [ { name: 'Extra Large', id: 'xl', value: 1536 }, ]; -export const useBreakpoint = (): Breakpoint => { - // TODO: Perhaps refactor for useMediaQuery to accept an array of queries +const getBreakpointValue = (key: Breakpoint): number => { + const breakpoint = breakpoints.find(bp => bp.id === key); + if (!breakpoint) { + throw new Error(`Invalid breakpoint key: ${key}`); + } + return breakpoint.value; +}; + +/** @public */ +export const useBreakpoint = () => { const matches = breakpoints.map(breakpoint => { const match = useMediaQuery(`(min-width: ${breakpoint.value}px)`); return match; }); + let breakpoint: Breakpoint = breakpoints[0].id; for (let i = matches.length - 1; i >= 0; i--) { if (matches[i]) { - return breakpoints[i].id; + breakpoint = breakpoints[i].id; + break; } } - return breakpoints[0].id; + return { + breakpoint, + up: (key: Breakpoint): boolean => { + const value = getBreakpointValue(key); + return useMediaQuery(`(min-width: ${value}px)`); + }, + down: (key: Breakpoint): boolean => { + const value = getBreakpointValue(key); + return useMediaQuery(`(max-width: ${value - 1}px)`); + }, + }; }; diff --git a/packages/canon/src/hooks/useResponsiveValue.ts b/packages/canon/src/hooks/useResponsiveValue.ts index 3ef5b99d47..206306566f 100644 --- a/packages/canon/src/hooks/useResponsiveValue.ts +++ b/packages/canon/src/hooks/useResponsiveValue.ts @@ -20,12 +20,10 @@ import { useBreakpoint, breakpoints } from './useBreakpoint'; type ResponsiveValue = string | Partial>; export const useResponsiveValue = (value: ResponsiveValue) => { - const currentBreakpoint = useBreakpoint(); + const { breakpoint } = useBreakpoint(); if (typeof value === 'object') { - const index = breakpoints.findIndex( - breakpoint => breakpoint.id === currentBreakpoint, - ); + const index = breakpoints.findIndex(b => b.id === breakpoint); for (let i = index; i >= 0; i--) { if (value[breakpoints[i].id]) { diff --git a/packages/canon/src/index.ts b/packages/canon/src/index.ts index 815e153c19..38759ebed4 100644 --- a/packages/canon/src/index.ts +++ b/packages/canon/src/index.ts @@ -47,3 +47,6 @@ export * from './components/Select'; // Types export * from './types'; export * from './props'; + +// Hooks +export { useBreakpoint } from './hooks/useBreakpoint'; From 85319ac8cfa393f62aa01b0cbc4ba82166e94db8 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Sat, 12 Apr 2025 15:25:58 +0200 Subject: [PATCH 32/56] Build CSS Signed-off-by: Charles de Dreuille --- packages/canon/css/button.css | 12 ++++----- packages/canon/css/components.css | 44 +++++++++++++++---------------- packages/canon/css/iconbutton.css | 12 ++++----- packages/canon/css/link.css | 12 ++++----- packages/canon/css/select.css | 4 +-- packages/canon/css/styles.css | 44 +++++++++++++++---------------- packages/canon/css/textfield.css | 4 +-- 7 files changed, 66 insertions(+), 66 deletions(-) diff --git a/packages/canon/css/button.css b/packages/canon/css/button.css index 312f58c5e6..ccfc3b48f5 100644 --- a/packages/canon/css/button.css +++ b/packages/canon/css/button.css @@ -16,7 +16,7 @@ } } -.canon-Button--variant-primary { +.canon-Button[data-variant="primary"] { background-color: var(--canon-bg-solid); color: var(--canon-fg-solid); transition: background-color .15s, box-shadow .15s; @@ -40,7 +40,7 @@ } } -.canon-Button--variant-secondary { +.canon-Button[data-variant="secondary"] { background-color: var(--canon-bg-surface-1); box-shadow: inset 0 0 0 1px var(--canon-border); color: var(--canon-fg-primary); @@ -66,24 +66,24 @@ } } -.canon-Button--size-medium { +.canon-Button[data-size="medium"] { font-size: var(--canon-font-size-4); padding: 0 var(--canon-space-3); height: 40px; } -.canon-Button--size-small { +.canon-Button[data-size="small"] { font-size: var(--canon-font-size-3); padding: 0 var(--canon-space-2); height: 32px; } -.canon-Button--size-small .canon-Button--icon { +.canon-Button[data-size="small"] .canon-Button--icon { width: 1rem; height: 1rem; } -.canon-Button--size-medium .canon-Button--icon { +.canon-Button[data-size="medium"] .canon-Button--icon { width: 1.5rem; height: 1.5rem; } diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index 9a276a3a0a..216f38c968 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -22,7 +22,7 @@ } } -.canon-Button--variant-primary { +.canon-Button[data-variant="primary"] { background-color: var(--canon-bg-solid); color: var(--canon-fg-solid); transition: background-color .15s, box-shadow .15s; @@ -46,7 +46,7 @@ } } -.canon-Button--variant-secondary { +.canon-Button[data-variant="secondary"] { background-color: var(--canon-bg-surface-1); box-shadow: inset 0 0 0 1px var(--canon-border); color: var(--canon-fg-primary); @@ -72,24 +72,24 @@ } } -.canon-Button--size-medium { +.canon-Button[data-size="medium"] { font-size: var(--canon-font-size-4); padding: 0 var(--canon-space-3); height: 40px; } -.canon-Button--size-small { +.canon-Button[data-size="small"] { font-size: var(--canon-font-size-3); padding: 0 var(--canon-space-2); height: 32px; } -.canon-Button--size-small .canon-Button--icon { +.canon-Button[data-size="small"] .canon-Button--icon { width: 1rem; height: 1rem; } -.canon-Button--size-medium .canon-Button--icon { +.canon-Button[data-size="medium"] .canon-Button--icon { width: 1.5rem; height: 1.5rem; } @@ -348,7 +348,7 @@ } } -.canon-IconButton--variant-primary { +.canon-IconButton[data-variant="primary"] { background-color: var(--canon-bg-solid); color: var(--canon-fg-solid); transition: background-color .15s, box-shadow .15s; @@ -372,7 +372,7 @@ } } -.canon-IconButton--variant-secondary { +.canon-IconButton[data-variant="secondary"] { background-color: var(--canon-bg-surface-1); box-shadow: inset 0 0 0 1px var(--canon-border); color: var(--canon-fg-primary); @@ -398,24 +398,24 @@ } } -.canon-IconButton--size-medium { +.canon-IconButton[data-size="medium"] { font-size: var(--canon-font-size-4); width: 40px; height: 40px; } -.canon-IconButton--size-small { +.canon-IconButton[data-size="small"] { font-size: var(--canon-font-size-3); width: 32px; height: 32px; } -.canon-IconButton--size-small .canon-IconButton--icon { +.canon-IconButton[data-size="small"] .canon-IconButton--icon { width: 1rem; height: 1rem; } -.canon-IconButton--size-medium .canon-IconButton--icon { +.canon-IconButton[data-size="medium"] .canon-IconButton--icon { width: 1.5rem; height: 1.5rem; } @@ -487,11 +487,11 @@ border: 1px solid var(--canon-border-disabled); } -.canon-TextField--input-size-small { +.canon-TextField--input[data-size="small"] { height: 2rem; } -.canon-TextField--input-size-medium { +.canon-TextField--input[data-size="medium"] { height: 2.5rem; } @@ -572,31 +572,31 @@ } } -.canon-Link--variant-body { +.canon-Link[data-variant="body"] { font-size: var(--canon-font-size-3); line-height: 140%; } -.canon-Link--variant-subtitle { +.canon-Link[data-variant="subtitle"] { font-size: var(--canon-font-size-4); line-height: 140%; } -.canon-Link--variant-caption { +.canon-Link[data-variant="caption"] { font-size: var(--canon-font-size-2); line-height: 140%; } -.canon-Link--variant-label { +.canon-Link[data-variant="label"] { font-size: var(--canon-font-size-1); line-height: 140%; } -.canon-Link--weight-regular { +.canon-Link[data-weight="regular"] { font-weight: var(--canon-font-weight-regular); } -.canon-Link--weight-bold { +.canon-Link[data-weight="bold"] { font-weight: var(--canon-font-weight-bold); } @@ -789,11 +789,11 @@ color: var(--canon-fg-disabled); } -.canon-Select--trigger-size-small { +.canon-Select--trigger[data-size="small"] { height: 2rem; } -.canon-Select--trigger-size-medium { +.canon-Select--trigger[data-size="medium"] { height: 3rem; } diff --git a/packages/canon/css/iconbutton.css b/packages/canon/css/iconbutton.css index 7853c59770..da38b40ba1 100644 --- a/packages/canon/css/iconbutton.css +++ b/packages/canon/css/iconbutton.css @@ -16,7 +16,7 @@ } } -.canon-IconButton--variant-primary { +.canon-IconButton[data-variant="primary"] { background-color: var(--canon-bg-solid); color: var(--canon-fg-solid); transition: background-color .15s, box-shadow .15s; @@ -40,7 +40,7 @@ } } -.canon-IconButton--variant-secondary { +.canon-IconButton[data-variant="secondary"] { background-color: var(--canon-bg-surface-1); box-shadow: inset 0 0 0 1px var(--canon-border); color: var(--canon-fg-primary); @@ -66,24 +66,24 @@ } } -.canon-IconButton--size-medium { +.canon-IconButton[data-size="medium"] { font-size: var(--canon-font-size-4); width: 40px; height: 40px; } -.canon-IconButton--size-small { +.canon-IconButton[data-size="small"] { font-size: var(--canon-font-size-3); width: 32px; height: 32px; } -.canon-IconButton--size-small .canon-IconButton--icon { +.canon-IconButton[data-size="small"] .canon-IconButton--icon { width: 1rem; height: 1rem; } -.canon-IconButton--size-medium .canon-IconButton--icon { +.canon-IconButton[data-size="medium"] .canon-IconButton--icon { width: 1.5rem; height: 1.5rem; } diff --git a/packages/canon/css/link.css b/packages/canon/css/link.css index ddc47c5d04..535beb3d9f 100644 --- a/packages/canon/css/link.css +++ b/packages/canon/css/link.css @@ -16,30 +16,30 @@ } } -.canon-Link--variant-body { +.canon-Link[data-variant="body"] { font-size: var(--canon-font-size-3); line-height: 140%; } -.canon-Link--variant-subtitle { +.canon-Link[data-variant="subtitle"] { font-size: var(--canon-font-size-4); line-height: 140%; } -.canon-Link--variant-caption { +.canon-Link[data-variant="caption"] { font-size: var(--canon-font-size-2); line-height: 140%; } -.canon-Link--variant-label { +.canon-Link[data-variant="label"] { font-size: var(--canon-font-size-1); line-height: 140%; } -.canon-Link--weight-regular { +.canon-Link[data-weight="regular"] { font-weight: var(--canon-font-weight-regular); } -.canon-Link--weight-bold { +.canon-Link[data-weight="bold"] { font-weight: var(--canon-font-weight-bold); } diff --git a/packages/canon/css/select.css b/packages/canon/css/select.css index 29494c634a..3c37952afd 100644 --- a/packages/canon/css/select.css +++ b/packages/canon/css/select.css @@ -74,11 +74,11 @@ color: var(--canon-fg-disabled); } -.canon-Select--trigger-size-small { +.canon-Select--trigger[data-size="small"] { height: 2rem; } -.canon-Select--trigger-size-medium { +.canon-Select--trigger[data-size="medium"] { height: 3rem; } diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index d3e61ff556..e4375692f9 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9246,7 +9246,7 @@ } } -.canon-Button--variant-primary { +.canon-Button[data-variant="primary"] { background-color: var(--canon-bg-solid); color: var(--canon-fg-solid); transition: background-color .15s, box-shadow .15s; @@ -9270,7 +9270,7 @@ } } -.canon-Button--variant-secondary { +.canon-Button[data-variant="secondary"] { background-color: var(--canon-bg-surface-1); box-shadow: inset 0 0 0 1px var(--canon-border); color: var(--canon-fg-primary); @@ -9296,24 +9296,24 @@ } } -.canon-Button--size-medium { +.canon-Button[data-size="medium"] { font-size: var(--canon-font-size-4); padding: 0 var(--canon-space-3); height: 40px; } -.canon-Button--size-small { +.canon-Button[data-size="small"] { font-size: var(--canon-font-size-3); padding: 0 var(--canon-space-2); height: 32px; } -.canon-Button--size-small .canon-Button--icon { +.canon-Button[data-size="small"] .canon-Button--icon { width: 1rem; height: 1rem; } -.canon-Button--size-medium .canon-Button--icon { +.canon-Button[data-size="medium"] .canon-Button--icon { width: 1.5rem; height: 1.5rem; } @@ -9572,7 +9572,7 @@ } } -.canon-IconButton--variant-primary { +.canon-IconButton[data-variant="primary"] { background-color: var(--canon-bg-solid); color: var(--canon-fg-solid); transition: background-color .15s, box-shadow .15s; @@ -9596,7 +9596,7 @@ } } -.canon-IconButton--variant-secondary { +.canon-IconButton[data-variant="secondary"] { background-color: var(--canon-bg-surface-1); box-shadow: inset 0 0 0 1px var(--canon-border); color: var(--canon-fg-primary); @@ -9622,24 +9622,24 @@ } } -.canon-IconButton--size-medium { +.canon-IconButton[data-size="medium"] { font-size: var(--canon-font-size-4); width: 40px; height: 40px; } -.canon-IconButton--size-small { +.canon-IconButton[data-size="small"] { font-size: var(--canon-font-size-3); width: 32px; height: 32px; } -.canon-IconButton--size-small .canon-IconButton--icon { +.canon-IconButton[data-size="small"] .canon-IconButton--icon { width: 1rem; height: 1rem; } -.canon-IconButton--size-medium .canon-IconButton--icon { +.canon-IconButton[data-size="medium"] .canon-IconButton--icon { width: 1.5rem; height: 1.5rem; } @@ -9711,11 +9711,11 @@ border: 1px solid var(--canon-border-disabled); } -.canon-TextField--input-size-small { +.canon-TextField--input[data-size="small"] { height: 2rem; } -.canon-TextField--input-size-medium { +.canon-TextField--input[data-size="medium"] { height: 2.5rem; } @@ -9796,31 +9796,31 @@ } } -.canon-Link--variant-body { +.canon-Link[data-variant="body"] { font-size: var(--canon-font-size-3); line-height: 140%; } -.canon-Link--variant-subtitle { +.canon-Link[data-variant="subtitle"] { font-size: var(--canon-font-size-4); line-height: 140%; } -.canon-Link--variant-caption { +.canon-Link[data-variant="caption"] { font-size: var(--canon-font-size-2); line-height: 140%; } -.canon-Link--variant-label { +.canon-Link[data-variant="label"] { font-size: var(--canon-font-size-1); line-height: 140%; } -.canon-Link--weight-regular { +.canon-Link[data-weight="regular"] { font-weight: var(--canon-font-weight-regular); } -.canon-Link--weight-bold { +.canon-Link[data-weight="bold"] { font-weight: var(--canon-font-weight-bold); } @@ -10013,11 +10013,11 @@ color: var(--canon-fg-disabled); } -.canon-Select--trigger-size-small { +.canon-Select--trigger[data-size="small"] { height: 2rem; } -.canon-Select--trigger-size-medium { +.canon-Select--trigger[data-size="medium"] { height: 3rem; } diff --git a/packages/canon/css/textfield.css b/packages/canon/css/textfield.css index a542690580..eb43b1f77f 100644 --- a/packages/canon/css/textfield.css +++ b/packages/canon/css/textfield.css @@ -65,11 +65,11 @@ border: 1px solid var(--canon-border-disabled); } -.canon-TextField--input-size-small { +.canon-TextField--input[data-size="small"] { height: 2rem; } -.canon-TextField--input-size-medium { +.canon-TextField--input[data-size="medium"] { height: 2.5rem; } From 32d5f0e0a512a571f48ac1726189297a0525f54c Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Sun, 13 Apr 2025 08:40:52 +0200 Subject: [PATCH 33/56] Add new TableCells Signed-off-by: Charles de Dreuille --- packages/canon/css/components.css | 6 ++ packages/canon/css/styles.css | 6 ++ packages/canon/css/table.css | 26 -------- packages/canon/report.api.md | 66 +++++++++++-------- .../src/components/DataTable/DataTable.tsx | 42 ++---------- .../components/DataTable/mocked-columns.tsx | 33 +++++----- packages/canon/src/components/Table/Table.tsx | 14 ++-- .../Table/TableCell/TableCell.stories.tsx | 32 +++++++++ .../Table/TableCell/TableCell.styles.css | 20 ++++++ .../components/Table/TableCell/TableCell.tsx | 29 ++++++++ .../TableCellLink/TableCellLink.stories.tsx | 40 +++++++++++ .../TableCellLink/TableCellLink.styles.css | 21 ++++++ .../Table/TableCellLink/TableCellLink.tsx | 46 +++++++++++++ .../components/Table/TableCellLink/types.ts | 26 ++++++++ .../TableCellText/TableCellText.stories.tsx | 39 +++++++++++ .../TableCellText/TableCellText.styles.css | 21 ++++++ .../Table/TableCellText/TableCellText.tsx | 41 ++++++++++++ .../components/Table/TableCellText/types.ts | 22 +++++++ packages/canon/src/components/Table/index.ts | 2 + .../canon/src/components/Table/styles.css | 5 -- packages/canon/src/css/components.css | 3 + 21 files changed, 424 insertions(+), 116 deletions(-) create mode 100644 packages/canon/src/components/Table/TableCell/TableCell.stories.tsx create mode 100644 packages/canon/src/components/Table/TableCell/TableCell.styles.css create mode 100644 packages/canon/src/components/Table/TableCell/TableCell.tsx create mode 100644 packages/canon/src/components/Table/TableCellLink/TableCellLink.stories.tsx create mode 100644 packages/canon/src/components/Table/TableCellLink/TableCellLink.styles.css create mode 100644 packages/canon/src/components/Table/TableCellLink/TableCellLink.tsx create mode 100644 packages/canon/src/components/Table/TableCellLink/types.ts create mode 100644 packages/canon/src/components/Table/TableCellText/TableCellText.stories.tsx create mode 100644 packages/canon/src/components/Table/TableCellText/TableCellText.styles.css create mode 100644 packages/canon/src/components/Table/TableCellText/TableCellText.tsx create mode 100644 packages/canon/src/components/Table/TableCellText/types.ts diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index 4dae0f5d3a..af4ea8c4f0 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -240,6 +240,12 @@ font-size: var(--canon-font-size-3); } +.canon-TableCellText, .canon-TableCellLink { + gap: var(--canon-space-0_5); + flex-direction: column; + display: flex; +} + .canon-Text { font-family: var(--canon-font-regular); color: var(--canon-fg-primary); diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index a166a67263..2230922917 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9464,6 +9464,12 @@ font-size: var(--canon-font-size-3); } +.canon-TableCellText, .canon-TableCellLink { + gap: var(--canon-space-0_5); + flex-direction: column; + display: flex; +} + .canon-Text { font-family: var(--canon-font-regular); color: var(--canon-fg-primary); diff --git a/packages/canon/css/table.css b/packages/canon/css/table.css index c27fe3235d..f14ee3759e 100644 --- a/packages/canon/css/table.css +++ b/packages/canon/css/table.css @@ -1,29 +1,3 @@ -.canon-TableRoot { - caption-side: bottom; - border-collapse: collapse; - width: 100%; -} - -.canon-TableHead { - text-align: left; - padding: var(--canon-space-3); - font-size: var(--canon-font-size-3); - color: var(--canon-fg-primary); -} - -.canon-TableBody { - color: var(--canon-fg-primary); -} - -.canon-TableRow { - border-bottom: 1px solid var(--canon-border); - transition: color .2s ease-in-out; -} - -.canon-TableBody .canon-TableRow:hover { - background-color: var(--canon-gray-2); -} - .canon-TableCell { padding: var(--canon-space-3); font-size: var(--canon-font-size-3); diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index ec53a49a92..44d6a3dd44 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -241,43 +241,27 @@ export const DataTable: { RefAttributes >; TableHeader: ForwardRefExoticComponent< - Omit< - HTMLAttributes & - RefAttributes, - 'ref' - > & + HTMLAttributes & RefAttributes >; TableBody: ForwardRefExoticComponent< - Omit< - HTMLAttributes & - RefAttributes, - 'ref' - > & + HTMLAttributes & RefAttributes >; TableRow: ForwardRefExoticComponent< - Omit< - HTMLAttributes & RefAttributes, - 'ref' - > & - RefAttributes + HTMLAttributes & RefAttributes >; TableCell: ForwardRefExoticComponent< - Omit< - TdHTMLAttributes & - RefAttributes, - 'ref' - > & - RefAttributes + TdHTMLAttributes & RefAttributes + >; + TableCellText: ForwardRefExoticComponent< + TableCellTextProps & RefAttributes + >; + TableCellLink: ForwardRefExoticComponent< + TableCellLinkProps & RefAttributes >; TableHead: ForwardRefExoticComponent< - Omit< - ThHTMLAttributes & - RefAttributes, - 'ref' - > & - RefAttributes + ThHTMLAttributes & RefAttributes >; }; @@ -1126,12 +1110,40 @@ export const Table: { Cell: ForwardRefExoticComponent< TdHTMLAttributes & RefAttributes >; + CellText: ForwardRefExoticComponent< + TableCellTextProps & RefAttributes + >; + CellLink: ForwardRefExoticComponent< + TableCellLinkProps & RefAttributes + >; Caption: ForwardRefExoticComponent< HTMLAttributes & RefAttributes >; }; +// @public (undocumented) +export interface TableCellLinkProps + extends React.TdHTMLAttributes { + // (undocumented) + description?: string; + // (undocumented) + href: string; + // (undocumented) + render?: useRender.ComponentProps<'a'>['render']; + // (undocumented) + title: string; +} + +// @public (undocumented) +export interface TableCellTextProps + extends React.TdHTMLAttributes { + // (undocumented) + description?: string; + // (undocumented) + title: string; +} + // @public (undocumented) const Text_2: ForwardRefExoticComponent< TextProps & RefAttributes diff --git a/packages/canon/src/components/DataTable/DataTable.tsx b/packages/canon/src/components/DataTable/DataTable.tsx index 44e32fa404..8be9ae174e 100644 --- a/packages/canon/src/components/DataTable/DataTable.tsx +++ b/packages/canon/src/components/DataTable/DataTable.tsx @@ -27,36 +27,6 @@ const TableRoot = forwardRef< >(({ className, ...props }, ref) => ); TableRoot.displayName = Table.Root.displayName; -const TableHeader = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ); -TableHeader.displayName = Table.Header.displayName; - -const TableBody = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ); -TableBody.displayName = Table.Body.displayName; - -const TableRow = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ); -TableRow.displayName = Table.Row.displayName; - -const TableCell = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ); -TableCell.displayName = Table.Cell.displayName; - -const TableHead = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ); -TableHead.displayName = Table.Head.displayName; - /** * DataTable component for displaying tabular data with pagination * @public @@ -70,9 +40,11 @@ export const DataTable = { Pagination: DataTablePagination, Table: DataTableTable, TableRoot: TableRoot, - TableHeader: TableHeader, - TableBody: TableBody, - TableRow: TableRow, - TableCell: TableCell, - TableHead: TableHead, + TableHeader: Table.Header, + TableBody: Table.Body, + TableRow: Table.Row, + TableCell: Table.Cell, + TableCellText: Table.CellText, + TableCellLink: Table.CellLink, + TableHead: Table.Head, }; diff --git a/packages/canon/src/components/DataTable/mocked-columns.tsx b/packages/canon/src/components/DataTable/mocked-columns.tsx index e140f41702..9f2ce72378 100644 --- a/packages/canon/src/components/DataTable/mocked-columns.tsx +++ b/packages/canon/src/components/DataTable/mocked-columns.tsx @@ -17,7 +17,7 @@ import { ColumnDef } from '@tanstack/react-table'; import { DataProps } from './mocked-components'; import { Checkbox } from '../Checkbox'; -import { Text } from '../Text'; +import { DataTable } from './DataTable'; export const columns: ColumnDef[] = [ { @@ -32,11 +32,13 @@ export const columns: ColumnDef[] = [ /> ), cell: ({ row }) => ( - row.toggleSelected(checked)} - aria-label="Select row" - /> +
+ row.toggleSelected(checked)} + aria-label="Select row" + /> + -)); -TableCell.displayName = 'TableCell'; - const TableCaption = forwardRef< HTMLTableCaptionElement, React.HTMLAttributes @@ -93,5 +89,7 @@ export const Table = { Head: TableHead, Row: TableRow, Cell: TableCell, + CellText: TableCellText, + CellLink: TableCellLink, Caption: TableCaption, }; diff --git a/packages/canon/src/components/Table/TableCell/TableCell.stories.tsx b/packages/canon/src/components/Table/TableCell/TableCell.stories.tsx new file mode 100644 index 0000000000..123dca6e39 --- /dev/null +++ b/packages/canon/src/components/Table/TableCell/TableCell.stories.tsx @@ -0,0 +1,32 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Meta, StoryObj } from '@storybook/react'; +import { TableCell } from './TableCell'; + +const meta = { + title: 'Components/Table/TableCell', + component: TableCell, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: 'Hello world', + }, +}; diff --git a/packages/canon/src/components/Table/TableCell/TableCell.styles.css b/packages/canon/src/components/Table/TableCell/TableCell.styles.css new file mode 100644 index 0000000000..26d0868b49 --- /dev/null +++ b/packages/canon/src/components/Table/TableCell/TableCell.styles.css @@ -0,0 +1,20 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.canon-TableCell { + padding: var(--canon-space-3); + font-size: var(--canon-font-size-3); +} diff --git a/packages/canon/src/components/Table/TableCell/TableCell.tsx b/packages/canon/src/components/Table/TableCell/TableCell.tsx new file mode 100644 index 0000000000..a8a87ac038 --- /dev/null +++ b/packages/canon/src/components/Table/TableCell/TableCell.tsx @@ -0,0 +1,29 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { forwardRef } from 'react'; +import clsx from 'clsx'; + +/** @public */ +const TableCell = forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableCell.displayName = 'TableCell'; + +export { TableCell }; diff --git a/packages/canon/src/components/Table/TableCellLink/TableCellLink.stories.tsx b/packages/canon/src/components/Table/TableCellLink/TableCellLink.stories.tsx new file mode 100644 index 0000000000..861f2643b2 --- /dev/null +++ b/packages/canon/src/components/Table/TableCellLink/TableCellLink.stories.tsx @@ -0,0 +1,40 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Meta, StoryObj } from '@storybook/react'; +import { TableCellLink } from './TableCellLink'; + +const meta = { + title: 'Components/Table/TableCellLink', + component: TableCellLink, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + title: 'I am a link', + href: 'https://canon.backstage.io', + }, +}; + +export const WithDescription: Story = { + args: { + ...Default.args, + description: 'This is a description', + }, +}; diff --git a/packages/canon/src/components/Table/TableCellLink/TableCellLink.styles.css b/packages/canon/src/components/Table/TableCellLink/TableCellLink.styles.css new file mode 100644 index 0000000000..3064bbb791 --- /dev/null +++ b/packages/canon/src/components/Table/TableCellLink/TableCellLink.styles.css @@ -0,0 +1,21 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.canon-TableCellLink { + display: flex; + flex-direction: column; + gap: var(--canon-space-0_5); +} diff --git a/packages/canon/src/components/Table/TableCellLink/TableCellLink.tsx b/packages/canon/src/components/Table/TableCellLink/TableCellLink.tsx new file mode 100644 index 0000000000..cb9d3e69a8 --- /dev/null +++ b/packages/canon/src/components/Table/TableCellLink/TableCellLink.tsx @@ -0,0 +1,46 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { forwardRef } from 'react'; +import clsx from 'clsx'; +import { TableCellLinkProps } from './types'; +import { Text } from '../../Text/Text'; +import { Link } from '../../Link/Link'; + +/** @public */ +const TableCellLink = forwardRef( + ({ className, title, description, href, render, ...props }, ref) => ( +
+ {title && ( + + {title} + + )} + {description && ( + + {description} + + )} +
+ ), +); +TableCellLink.displayName = 'TableCellLink'; + +export { TableCellLink }; diff --git a/packages/canon/src/components/Table/TableCellLink/types.ts b/packages/canon/src/components/Table/TableCellLink/types.ts new file mode 100644 index 0000000000..50cac9a45a --- /dev/null +++ b/packages/canon/src/components/Table/TableCellLink/types.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { useRender } from '@base-ui-components/react/use-render'; + +/** @public */ +export interface TableCellLinkProps + extends React.TdHTMLAttributes { + title: string; + description?: string; + href: string; + render?: useRender.ComponentProps<'a'>['render']; +} diff --git a/packages/canon/src/components/Table/TableCellText/TableCellText.stories.tsx b/packages/canon/src/components/Table/TableCellText/TableCellText.stories.tsx new file mode 100644 index 0000000000..3756dd2544 --- /dev/null +++ b/packages/canon/src/components/Table/TableCellText/TableCellText.stories.tsx @@ -0,0 +1,39 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Meta, StoryObj } from '@storybook/react'; +import { TableCellText } from './TableCellText'; + +const meta = { + title: 'Components/Table/TableCellText', + component: TableCellText, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + title: 'Hello world', + }, +}; + +export const WithDescription: Story = { + args: { + ...Default.args, + description: 'This is a description', + }, +}; diff --git a/packages/canon/src/components/Table/TableCellText/TableCellText.styles.css b/packages/canon/src/components/Table/TableCellText/TableCellText.styles.css new file mode 100644 index 0000000000..7a645a1fcb --- /dev/null +++ b/packages/canon/src/components/Table/TableCellText/TableCellText.styles.css @@ -0,0 +1,21 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.canon-TableCellText { + display: flex; + flex-direction: column; + gap: var(--canon-space-0_5); +} diff --git a/packages/canon/src/components/Table/TableCellText/TableCellText.tsx b/packages/canon/src/components/Table/TableCellText/TableCellText.tsx new file mode 100644 index 0000000000..d5496d799b --- /dev/null +++ b/packages/canon/src/components/Table/TableCellText/TableCellText.tsx @@ -0,0 +1,41 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { forwardRef } from 'react'; +import clsx from 'clsx'; +import { TableCellTextProps } from './types'; +import { Text } from '../../Text/Text'; + +/** @public */ +const TableCellText = forwardRef( + ({ className, title, description, ...props }, ref) => ( +
+ {title && {title}} + {description && ( + + {description} + + )} +
+ ), +); +TableCellText.displayName = 'TableCellText'; + +export { TableCellText }; diff --git a/packages/canon/src/components/Table/TableCellText/types.ts b/packages/canon/src/components/Table/TableCellText/types.ts new file mode 100644 index 0000000000..c6f1814966 --- /dev/null +++ b/packages/canon/src/components/Table/TableCellText/types.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @public */ +export interface TableCellTextProps + extends React.TdHTMLAttributes { + title: string; + description?: string; +} diff --git a/packages/canon/src/components/Table/index.ts b/packages/canon/src/components/Table/index.ts index 4929b2d36a..0422086b45 100644 --- a/packages/canon/src/components/Table/index.ts +++ b/packages/canon/src/components/Table/index.ts @@ -15,3 +15,5 @@ */ export * from './Table'; +export * from './TableCellText/types'; +export * from './TableCellLink/types'; diff --git a/packages/canon/src/components/Table/styles.css b/packages/canon/src/components/Table/styles.css index 4234095823..723287babe 100644 --- a/packages/canon/src/components/Table/styles.css +++ b/packages/canon/src/components/Table/styles.css @@ -23,8 +23,3 @@ .canon-TableBody .canon-TableRow:hover { background-color: var(--canon-gray-2); } - -.canon-TableCell { - padding: var(--canon-space-3); - font-size: var(--canon-font-size-3); -} diff --git a/packages/canon/src/css/components.css b/packages/canon/src/css/components.css index f6b1db16c5..7dee4afb97 100644 --- a/packages/canon/src/css/components.css +++ b/packages/canon/src/css/components.css @@ -24,6 +24,9 @@ @import '../components/Icon/styles.css'; @import '../components/Checkbox/styles.css'; @import '../components/Table/styles.css'; +@import '../components/Table/TableCell/TableCell.styles.css'; +@import '../components/Table/TableCellText/TableCellText.styles.css'; +@import '../components/Table/TableCellLink/TableCellLink.styles.css'; @import '../components/Text/styles.css'; @import '../components/Heading/styles.css'; @import '../components/IconButton/styles.css'; From 4b8db95f4f5f97839bbb0246d15c8582f204e5f4 Mon Sep 17 00:00:00 2001 From: patroswastik Date: Sun, 13 Apr 2025 06:08:41 -0500 Subject: [PATCH 34/56] went up the tree to locate createRouter and found deprecated over there Signed-off-by: patroswastik --- .../lint-legacy-backend-exports.ts | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts b/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts index 0ebc7088e3..29fb4e7b20 100644 --- a/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts +++ b/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts @@ -47,9 +47,6 @@ function verifyIndex(pkg: string, packageJson?: BackstagePackageJson) { const tsPath = path.join(pkg, 'src/index.ts'); const sourceFile = project.getSourceFile(tsPath); - const tsRouterPath = path.join(pkg, 'src/service/router.ts'); - const routerFile = project.getSourceFile(tsRouterPath); - if (!sourceFile) { console.log(`Could not find ${tsPath}`); process.exit(1); @@ -74,23 +71,28 @@ function verifyIndex(pkg: string, packageJson?: BackstagePackageJson) { console.log(' ❌ Missing default export'); } let createRouterDeprecated = undefined; + let routerCreateRouterDeprecated = undefined; if (createRouterExport) { createRouterDeprecated = createRouterExport .getJsDocTags() .find(tag => tag.getName() === 'deprecated'); - } - let routerCreateRouterDeprecated = undefined; - if (routerFile) { - const routerSymbols = routerFile?.getExportSymbols(); - const routerCreateRouterExport = routerSymbols?.find( - symbol => symbol.getName() === 'createRouter', - ); - - if (routerCreateRouterExport) { - routerCreateRouterDeprecated = routerCreateRouterExport - .getJsDocTags() - .find(tag => tag.getName() === 'deprecated'); + const declarations = createRouterExport?.getDeclarations(); + const firstDeclaration = declarations?.[0]; + let resolvedSymbol = undefined; + if (firstDeclaration) { + // Try resolving to the definition directly + resolvedSymbol = createRouterExport.getAliasedSymbol(); + if (resolvedSymbol) { + const resolvedDeclarations = resolvedSymbol.getDeclarations(); + const resolvedDeclaration = resolvedDeclarations?.[0]; + if (resolvedDeclaration) { + routerCreateRouterDeprecated = resolvedDeclaration + .getSymbol() + ?.getJsDocTags() + .find(tag => tag.getName() === 'deprecated'); + } + } } } From 7d0bd52f7ac6ede005fc57edd23e33fea564f5f4 Mon Sep 17 00:00:00 2001 From: patroswastik Date: Sun, 13 Apr 2025 06:43:39 -0500 Subject: [PATCH 35/56] Changeset updated with relevant description of the fix Signed-off-by: patroswastik --- .changeset/rich-phones-whisper.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/rich-phones-whisper.md b/.changeset/rich-phones-whisper.md index c27b6de2f8..31d4bdb57f 100644 --- a/.changeset/rich-phones-whisper.md +++ b/.changeset/rich-phones-whisper.md @@ -2,4 +2,4 @@ '@backstage/repo-tools': minor --- -Checking through router.ts files of the packages if they exist and looking for deprecated tag inside it. If exists then only the message will appear +Checking up the files where createRouter has been declared and check if @deprecated tag exists. If it does not exist then only the message will appear. From ff618514ff4c19b198e6471adf4b6ba6150296f9 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sun, 13 Apr 2025 16:43:43 -0400 Subject: [PATCH 36/56] hide deprecated commands from the list of commands Signed-off-by: aramissennyeydd --- .../cli/cli-report.backstage-cli-alpha.md | 28 +++++++++++++++++++ packages/cli/cli-report.backstage-cli.md | 10 ++----- packages/cli/src/commands/index.ts | 1 - packages/cli/src/modules/new/alpha.ts | 2 ++ packages/cli/src/wiring/CliInitializer.ts | 2 +- packages/cli/src/wiring/types.ts | 1 + 6 files changed, 34 insertions(+), 10 deletions(-) diff --git a/packages/cli/cli-report.backstage-cli-alpha.md b/packages/cli/cli-report.backstage-cli-alpha.md index b52d40c809..917a915fe5 100644 --- a/packages/cli/cli-report.backstage-cli-alpha.md +++ b/packages/cli/cli-report.backstage-cli-alpha.md @@ -18,9 +18,11 @@ Commands: config:docs config:print config:schema + create-github-app help [command] info migrate [command] + new package [command] repo [command] versions:bump @@ -132,6 +134,15 @@ Options: --version ``` +### `backstage-cli-alpha create-github-app` + +``` +Usage: program [options] + +Options: + -h, --help +``` + ### `backstage-cli-alpha info` ``` @@ -204,6 +215,23 @@ Options: -h, --help ``` +### `backstage-cli-alpha new` + +``` +Usage: program [options] + +Options: + --baseVersion + --license + --no-private + --npm-registry + --option = + --scope + --select + --skip-install + -h, --help +``` + ### `backstage-cli-alpha package` ``` diff --git a/packages/cli/cli-report.backstage-cli.md b/packages/cli/cli-report.backstage-cli.md index 524bda846d..ed6ae9049e 100644 --- a/packages/cli/cli-report.backstage-cli.md +++ b/packages/cli/cli-report.backstage-cli.md @@ -12,17 +12,11 @@ Options: -h, --help Commands: + build-workspace [options] [packages...] + config:check [options] config:docs [options] config:print [options] config:schema [options] - repo [command] - package [command] - versions:bump [options] - versions:migrate [options] - migrate [command] - build-workspace [options] [packages...] - info - new [options] create-github-app help [command] info diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 3640091f60..8483fd79c0 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -15,7 +15,6 @@ */ import { Command } from 'commander'; -import { lazy } from '../lib/lazy'; import { registerCommands as registerConfigCommands } from '../modules/config'; import { registerPackageCommands as registerPackageBuildCommands, diff --git a/packages/cli/src/modules/new/alpha.ts b/packages/cli/src/modules/new/alpha.ts index 9a9b26e85a..6802077507 100644 --- a/packages/cli/src/modules/new/alpha.ts +++ b/packages/cli/src/modules/new/alpha.ts @@ -69,6 +69,7 @@ export default createCliPlugin({ reg.addCommand({ path: ['create'], description: 'Create a new Backstage app', + deprecated: true, execute: async () => { removed("use 'backstage-cli new' instead")(); }, @@ -76,6 +77,7 @@ export default createCliPlugin({ reg.addCommand({ path: ['create-plugin'], description: 'Create a new Backstage plugin', + deprecated: true, execute: async () => { removed("use 'backstage-cli new' instead")(); }, diff --git a/packages/cli/src/wiring/CliInitializer.ts b/packages/cli/src/wiring/CliInitializer.ts index d7fdfdccab..d3cba34f60 100644 --- a/packages/cli/src/wiring/CliInitializer.ts +++ b/packages/cli/src/wiring/CliInitializer.ts @@ -88,7 +88,7 @@ export class CliInitializer { ); } else { argParser - .command(node.name) + .command(node.name, { hidden: !!node.command.deprecated }) .description(node.command.description) .helpOption(false) .allowUnknownOption(true) diff --git a/packages/cli/src/wiring/types.ts b/packages/cli/src/wiring/types.ts index d9237a4c3a..0decdc6479 100644 --- a/packages/cli/src/wiring/types.ts +++ b/packages/cli/src/wiring/types.ts @@ -18,6 +18,7 @@ import { CommandRegistry } from './CommandRegistry'; export interface BackstageCommand { path: string[]; description: string; + deprecated?: boolean; execute: (options: { args: string[] }) => Promise; } From 4f696087e1d8d6a0ee84408ccc6ad16fe25d2ab7 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Mon, 14 Apr 2025 10:24:19 +0200 Subject: [PATCH 37/56] Added docs for Menu Signed-off-by: Charles de Dreuille --- .../src/app/(docs)/components/menu/page.mdx | 140 +++++++++++++++++ .../src/app/(docs)/components/menu/props.ts | 142 ++++++++++++++++++ canon-docs/src/snippets/stories-snippets.tsx | 8 + canon-docs/src/utils/data.ts | 5 + canon-docs/src/utils/propDefs.ts | 8 + .../src/components/Menu/Menu.stories.tsx | 21 ++- 6 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 canon-docs/src/app/(docs)/components/menu/page.mdx create mode 100644 canon-docs/src/app/(docs)/components/menu/props.ts diff --git a/canon-docs/src/app/(docs)/components/menu/page.mdx b/canon-docs/src/app/(docs)/components/menu/page.mdx new file mode 100644 index 0000000000..baa719375f --- /dev/null +++ b/canon-docs/src/app/(docs)/components/menu/page.mdx @@ -0,0 +1,140 @@ +import { PropsTable } from '@/components/PropsTable'; +import { Snippet } from '@/components/Snippet'; +import { Tabs } from '@/components/Tabs'; +import { CodeBlock } from '@/components/CodeBlock'; +import { MenuSnippet, ButtonSnippet } from '@/snippets/stories-snippets'; +import { BaseUI } from '@/components/HeadlessBanners/BaseUI'; +import { + menuRootPropDefs, + menuTriggerPropDefs, + menuPositionerPropDefs, + menuItemPropDefs, +} from './props'; + +# Menu + +A list of actions in a dropdown, enhanced with keyboard navigation. + +} + code={` + ( + + )} + /> + + + + Settings + Invite new members + Download app + Log out + + + + `} +/> + + + + Usage + Theming + + + + + + + + + + + + `} + /> + + + We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with + your brand. For additional flexibility, you can use the provided class names for each element listed below. + `} + /> + + + + +## API reference + + + +### Menu.Root + + + +### Menu.Trigger + +The trigger by default render a simple unstyled button. Because menus can be rendered in different ways, we recommend +using the `render` prop to render a custom trigger. + + + )} + /> + + + + Settings + Invite new members + Download app + Log out + + + + `} +/> diff --git a/canon-docs/src/app/(docs)/components/menu/props.ts b/canon-docs/src/app/(docs)/components/menu/props.ts new file mode 100644 index 0000000000..5cf41fa5f9 --- /dev/null +++ b/canon-docs/src/app/(docs)/components/menu/props.ts @@ -0,0 +1,142 @@ +import { + classNamePropDefs, + stylePropDefs, + renderPropDefs, +} from '@/utils/propDefs'; +import type { PropDef } from '@/utils/propDefs'; + +export const menuRootPropDefs: Record = { + defaultOpen: { + type: 'boolean', + default: 'false', + }, + open: { + type: 'boolean', + }, + onOpenChange: { + type: 'enum', + values: ['(open, event) => void'], + }, + closeParentOnEsc: { + type: 'boolean', + default: 'true', + }, + modal: { + type: 'boolean', + default: 'true', + }, + onOpenChangeComplete: { + type: 'enum', + values: ['(open) => void'], + }, + disabled: { + type: 'boolean', + default: 'false', + }, + openOnHover: { + type: 'boolean', + }, + delay: { + type: 'number', + default: '100', + }, + loop: { + type: 'boolean', + default: 'true', + }, + orientation: { + type: 'enum', + values: ['horizontal', 'vertical'], + default: 'vertical', + }, + ...classNamePropDefs, + ...stylePropDefs, +}; + +export const menuTriggerPropDefs: Record = { + ...renderPropDefs, + ...classNamePropDefs, + ...stylePropDefs, +}; + +export const menuPositionerPropDefs: Record = { + align: { + type: 'enum', + values: ['start', 'center', 'end'], + default: 'center', + }, + alignOffset: { + type: 'enum', + values: ['number', '(data) => number'], + default: '0', + }, + side: { + type: 'enum', + values: ['bottom', 'inline-end', 'inline-start', 'left', 'right', 'top'], + default: 'bottom', + }, + sideOffset: { + type: 'enum', + values: ['number', '(data) => number'], + default: '0', + }, + arrowPadding: { + type: 'number', + default: '5', + }, + anchor: { + type: 'enum', + values: [ + 'React.Ref', + 'Element', + 'VirtualElement', + '(() => Element | VirtualElement | null)', + 'null', + ], + }, + collisionBoundary: { + type: 'enum', + values: ['clipping-ancestors', 'Element', 'Element[]', 'Rect'], + default: 'clipping-ancestors', + }, + collisionPadding: { + type: 'enum', + values: ['number', 'Rect'], + default: '5', + }, + sticky: { + type: 'boolean', + default: 'false', + }, + positionMethod: { + type: 'enum', + values: ['absolute', 'fixed'], + default: 'absolute', + }, + trackAnchor: { + type: 'boolean', + default: 'true', + }, + ...classNamePropDefs, + ...stylePropDefs, +}; + +export const menuItemPropDefs: Record = { + label: { + type: 'string', + }, + onClick: { + type: 'enum', + values: ['(event) => void'], + }, + closeOnClick: { + type: 'boolean', + default: 'true', + }, + disabled: { + type: 'boolean', + default: 'false', + }, + ...classNamePropDefs, + ...stylePropDefs, +}; diff --git a/canon-docs/src/snippets/stories-snippets.tsx b/canon-docs/src/snippets/stories-snippets.tsx index 5c363d6fc7..9536ba16a2 100644 --- a/canon-docs/src/snippets/stories-snippets.tsx +++ b/canon-docs/src/snippets/stories-snippets.tsx @@ -13,6 +13,7 @@ import * as TextFieldStories from '../../../packages/canon/src/components/TextFi import * as TextStories from '../../../packages/canon/src/components/Text/Text.stories'; import * as FlexStories from '../../../packages/canon/src/components/Flex/Flex.stories'; import * as SelectStories from '../../../packages/canon/src/components/Select/Select.stories'; +import * as MenuStories from '../../../packages/canon/src/components/Menu/Menu.stories'; export const BoxSnippet = ({ story }: { story: keyof typeof BoxStories }) => { const stories = composeStories(BoxStories); @@ -125,3 +126,10 @@ export const SelectSnippet = ({ return StoryComponent ? : null; }; + +export const MenuSnippet = ({ story }: { story: keyof typeof MenuStories }) => { + const stories = composeStories(MenuStories); + const StoryComponent = stories[story as keyof typeof stories]; + + return StoryComponent ? : null; +}; diff --git a/canon-docs/src/utils/data.ts b/canon-docs/src/utils/data.ts index fd4625cd20..43a457d268 100644 --- a/canon-docs/src/utils/data.ts +++ b/canon-docs/src/utils/data.ts @@ -91,6 +91,11 @@ export const components: Page[] = [ slug: 'icon-button', status: 'alpha', }, + { + title: 'Menu', + slug: 'menu', + status: 'alpha', + }, { title: 'Select', slug: 'select', diff --git a/canon-docs/src/utils/propDefs.ts b/canon-docs/src/utils/propDefs.ts index f087d6188f..3805665346 100644 --- a/canon-docs/src/utils/propDefs.ts +++ b/canon-docs/src/utils/propDefs.ts @@ -203,3 +203,11 @@ export const childrenPropDefs: Record = { responsive: false, }, }; + +export const renderPropDefs: Record = { + render: { + type: 'enum', + values: ['React.ReactElement', '(props, state) => React.ReactElement'], + responsive: false, + }, +}; diff --git a/packages/canon/src/components/Menu/Menu.stories.tsx b/packages/canon/src/components/Menu/Menu.stories.tsx index 77bff661e3..d1b8f831c5 100644 --- a/packages/canon/src/components/Menu/Menu.stories.tsx +++ b/packages/canon/src/components/Menu/Menu.stories.tsx @@ -32,7 +32,12 @@ export const Default: Story = { <> ( - )} @@ -40,9 +45,10 @@ export const Default: Story = { - Item 1 - Item 2 - Item 3 + Settings + Invite new members + Download app + Log out @@ -57,3 +63,10 @@ export const Open: Story = { open: true, }, }; + +export const OpenOnHover: Story = { + args: { + ...Default.args, + openOnHover: true, + }, +}; From 06aa4bdc69e0958215f668f66058f1815cdfd636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 14 Apr 2025 10:59:36 +0200 Subject: [PATCH 38/56] Update .changeset/rich-phones-whisper.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/rich-phones-whisper.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/rich-phones-whisper.md b/.changeset/rich-phones-whisper.md index 31d4bdb57f..3f3c226690 100644 --- a/.changeset/rich-phones-whisper.md +++ b/.changeset/rich-phones-whisper.md @@ -2,4 +2,4 @@ '@backstage/repo-tools': minor --- -Checking up the files where createRouter has been declared and check if @deprecated tag exists. If it does not exist then only the message will appear. +Checking up the files where `createRouter` has been declared and check if `@deprecated` tag exists. If it does not exist then only the message will appear. From 9663a04fcde1cb9bc05ef06f7239f85c6fb4625d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 14 Apr 2025 10:59:41 +0200 Subject: [PATCH 39/56] Update .changeset/rich-phones-whisper.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/rich-phones-whisper.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/rich-phones-whisper.md b/.changeset/rich-phones-whisper.md index 3f3c226690..75f0fc0c35 100644 --- a/.changeset/rich-phones-whisper.md +++ b/.changeset/rich-phones-whisper.md @@ -1,5 +1,5 @@ --- -'@backstage/repo-tools': minor +'@backstage/repo-tools': patch --- Checking up the files where `createRouter` has been declared and check if `@deprecated` tag exists. If it does not exist then only the message will appear. From f6745b7c5d1e3959b80b0bdc647f311192a64a1e Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Mon, 14 Apr 2025 11:31:15 +0200 Subject: [PATCH 40/56] Update page.mdx Signed-off-by: Charles de Dreuille --- canon-docs/src/app/(docs)/components/menu/page.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/canon-docs/src/app/(docs)/components/menu/page.mdx b/canon-docs/src/app/(docs)/components/menu/page.mdx index baa719375f..aca381da18 100644 --- a/canon-docs/src/app/(docs)/components/menu/page.mdx +++ b/canon-docs/src/app/(docs)/components/menu/page.mdx @@ -82,6 +82,8 @@ A list of actions in a dropdown, enhanced with keyboard navigation. ### Menu.Root +Groups all parts of the menu. Doesn't render its own HTML element. + ### Menu.Trigger @@ -95,10 +97,14 @@ using the `render` prop to render a custom trigger. ### Menu.Positioner +Positions the menu popup against the trigger. Renders a `
` element. + ### Menu.Item +An individual interactive item in the menu. Renders a `
` element. + ## Examples From 62443c60e819a49ba55e25ed681db25730f117e0 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Mon, 14 Apr 2025 11:42:38 +0200 Subject: [PATCH 41/56] Update sub components naming Signed-off-by: Charles de Dreuille --- packages/canon/css/button.css | 4 +- packages/canon/css/components.css | 80 +++++++++---------- packages/canon/css/iconbutton.css | 4 +- packages/canon/css/select.css | 48 +++++------ packages/canon/css/styles.css | 80 +++++++++---------- packages/canon/css/textfield.css | 24 +++--- .../canon/src/components/Button/Button.tsx | 4 +- .../canon/src/components/Button/styles.css | 4 +- .../src/components/IconButton/IconButton.tsx | 2 +- .../src/components/IconButton/styles.css | 4 +- .../src/components/Select/Select.styles.css | 52 ++++++------ .../canon/src/components/Select/Select.tsx | 22 ++--- .../components/TextField/TextField.styles.css | 24 +++--- .../src/components/TextField/TextField.tsx | 10 +-- 14 files changed, 181 insertions(+), 181 deletions(-) diff --git a/packages/canon/css/button.css b/packages/canon/css/button.css index ccfc3b48f5..eb0c44ce1b 100644 --- a/packages/canon/css/button.css +++ b/packages/canon/css/button.css @@ -78,12 +78,12 @@ height: 32px; } -.canon-Button[data-size="small"] .canon-Button--icon { +.canon-Button[data-size="small"] .canon-ButtonIcon { width: 1rem; height: 1rem; } -.canon-Button[data-size="medium"] .canon-Button--icon { +.canon-Button[data-size="medium"] .canon-ButtonIcon { width: 1.5rem; height: 1.5rem; } diff --git a/packages/canon/css/components.css b/packages/canon/css/components.css index 216f38c968..5139e45c22 100644 --- a/packages/canon/css/components.css +++ b/packages/canon/css/components.css @@ -84,12 +84,12 @@ height: 32px; } -.canon-Button[data-size="small"] .canon-Button--icon { +.canon-Button[data-size="small"] .canon-ButtonIcon { width: 1rem; height: 1rem; } -.canon-Button[data-size="medium"] .canon-Button--icon { +.canon-Button[data-size="medium"] .canon-ButtonIcon { width: 1.5rem; height: 1.5rem; } @@ -410,12 +410,12 @@ height: 32px; } -.canon-IconButton[data-size="small"] .canon-IconButton--icon { +.canon-IconButton[data-size="small"] .canon-IconButtonIcon { width: 1rem; height: 1rem; } -.canon-IconButton[data-size="medium"] .canon-IconButton--icon { +.canon-IconButton[data-size="medium"] .canon-IconButtonIcon { width: 1.5rem; height: 1.5rem; } @@ -427,14 +427,14 @@ display: flex; } -.canon-TextField--label { +.canon-TextFieldLabel { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-primary); margin-bottom: var(--canon-space-1_5); } -.canon-TextField--description { +.canon-TextFieldDescription { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-secondary); @@ -442,7 +442,7 @@ margin: 0; } -.canon-TextField--error { +.canon-TextFieldError { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-danger); @@ -450,7 +450,7 @@ margin: 0; } -.canon-TextField--input { +.canon-TextFieldInput { border-radius: var(--canon-radius-3); border: 1px solid var(--canon-border); padding: 0 var(--canon-space-4); @@ -463,39 +463,39 @@ transition: border-color .2s ease-in-out, outline-color .2s ease-in-out; } -.canon-TextField--input::placeholder { +.canon-TextFieldInput::placeholder { color: var(--canon-fg-secondary); } -.canon-TextField--input:hover { +.canon-TextFieldInput:hover { border-color: var(--canon-border-hover); } -.canon-TextField--input:focus-visible { +.canon-TextFieldInput:focus-visible { outline-color: var(--canon-border-pressed); border-color: var(--canon-border-pressed); outline-width: 0; } -.canon-TextField--input[data-invalid] { +.canon-TextFieldInput[data-invalid] { border-color: var(--canon-fg-danger); } -.canon-TextField--input[data-disabled] { +.canon-TextFieldInput[data-disabled] { opacity: .5; cursor: not-allowed; border: 1px solid var(--canon-border-disabled); } -.canon-TextField--input[data-size="small"] { +.canon-TextFieldInput[data-size="small"] { height: 2rem; } -.canon-TextField--input[data-size="medium"] { +.canon-TextFieldInput[data-size="medium"] { height: 2.5rem; } -.canon-TextField--required { +.canon-TextFieldRequired { color: var(--canon-fg-secondary); font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); @@ -720,14 +720,14 @@ display: flex; } -.canon-Select--label { +.canon-SelectLabel { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-primary); margin-bottom: var(--canon-space-1_5); } -.canon-Select--description { +.canon-SelectDescription { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-secondary); @@ -735,7 +735,7 @@ margin: 0; } -.canon-Select--error { +.canon-SelectError { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-danger); @@ -743,7 +743,7 @@ margin: 0; } -.canon-Select--trigger { +.canon-SelectTrigger { box-sizing: border-box; border-radius: var(--canon-radius-3); border: 1px solid var(--canon-border); @@ -762,38 +762,38 @@ display: flex; } -.canon-Select--trigger::placeholder { +.canon-SelectTrigger::placeholder { color: var(--canon-fg-secondary); } -.canon-Select--trigger:hover { +.canon-SelectTrigger:hover { border-color: var(--canon-border-hover); } -.canon-Select--trigger:focus-visible { +.canon-SelectTrigger:focus-visible { border-color: var(--canon-border-pressed); outline: 0; } -.canon-Select--trigger[data-invalid] { +.canon-SelectTrigger[data-invalid] { border-color: var(--canon-fg-danger); } -.canon-Select--trigger[data-invalid]:hover, .canon-Select--trigger[data-invalid]:focus-visible { +.canon-SelectTrigger[data-invalid]:hover, .canon-SelectTrigger[data-invalid]:focus-visible { border-width: 2px; } -.canon-Select--trigger[data-disabled] { +.canon-SelectTrigger[data-disabled] { cursor: not-allowed; border-color: var(--canon-border-disabled); color: var(--canon-fg-disabled); } -.canon-Select--trigger[data-size="small"] { +.canon-SelectTrigger[data-size="small"] { height: 2rem; } -.canon-Select--trigger[data-size="medium"] { +.canon-SelectTrigger[data-size="medium"] { height: 3rem; } @@ -802,11 +802,11 @@ transition: transform .2s; } -.canon-Select--trigger[data-popup-open] .canon-SelectIcon { +.canon-SelectTrigger[data-popup-open] .canon-SelectIcon { transform: rotate(180deg); } -.canon-Select--popup { +.canon-SelectPopup { box-sizing: border-box; max-height: var(--available-height); background-color: var(--canon-bg-surface-1); @@ -821,12 +821,12 @@ box-shadow: 0 4px 12px #0003; } -.canon-Select--popup[data-starting-style], .canon-Select--popup[data-ending-style] { +.canon-SelectPopup[data-starting-style], .canon-SelectPopup[data-ending-style] { opacity: 0; transform: scale(.9); } -.canon-Select--item { +.canon-SelectItem { width: var(--anchor-width); padding-block: var(--canon-space-2); padding-inline: var(--canon-space-4); @@ -844,13 +844,13 @@ position: relative; } -.canon-Select--item[data-highlighted] { +.canon-SelectItem[data-highlighted] { z-index: 0; color: var(--canon-fg-primary); position: relative; } -.canon-Select--item[data-highlighted]:before { +.canon-SelectItem[data-highlighted]:before { content: ""; z-index: -1; background-color: var(--canon-bg-tint-hover); @@ -860,37 +860,37 @@ inset-inline: .25rem; } -.canon-Select--item[data-disabled] { +.canon-SelectItem[data-disabled] { cursor: not-allowed; color: var(--canon-fg-disabled); } -.canon-Select--item-indicator { +.canon-SelectItemIndicator { grid-area: icon; justify-content: center; align-items: center; display: flex; } -.canon-Select--item-text { +.canon-SelectItemText { flex: 1; grid-area: text; } -.canon-Select--required { +.canon-SelectRequired { color: var(--canon-fg-secondary); font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); margin-left: var(--canon-space-1); } -.canon-Select--icon { +.canon-SelectIcon { justify-content: center; align-items: center; display: flex; } -.canon-Select--value { +.canon-SelectValue { text-overflow: ellipsis; white-space: nowrap; width: 100%; diff --git a/packages/canon/css/iconbutton.css b/packages/canon/css/iconbutton.css index da38b40ba1..e867a1848e 100644 --- a/packages/canon/css/iconbutton.css +++ b/packages/canon/css/iconbutton.css @@ -78,12 +78,12 @@ height: 32px; } -.canon-IconButton[data-size="small"] .canon-IconButton--icon { +.canon-IconButton[data-size="small"] .canon-IconButtonIcon { width: 1rem; height: 1rem; } -.canon-IconButton[data-size="medium"] .canon-IconButton--icon { +.canon-IconButton[data-size="medium"] .canon-IconButtonIcon { width: 1.5rem; height: 1.5rem; } diff --git a/packages/canon/css/select.css b/packages/canon/css/select.css index 3c37952afd..cf8c05622b 100644 --- a/packages/canon/css/select.css +++ b/packages/canon/css/select.css @@ -5,14 +5,14 @@ display: flex; } -.canon-Select--label { +.canon-SelectLabel { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-primary); margin-bottom: var(--canon-space-1_5); } -.canon-Select--description { +.canon-SelectDescription { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-secondary); @@ -20,7 +20,7 @@ margin: 0; } -.canon-Select--error { +.canon-SelectError { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-danger); @@ -28,7 +28,7 @@ margin: 0; } -.canon-Select--trigger { +.canon-SelectTrigger { box-sizing: border-box; border-radius: var(--canon-radius-3); border: 1px solid var(--canon-border); @@ -47,38 +47,38 @@ display: flex; } -.canon-Select--trigger::placeholder { +.canon-SelectTrigger::placeholder { color: var(--canon-fg-secondary); } -.canon-Select--trigger:hover { +.canon-SelectTrigger:hover { border-color: var(--canon-border-hover); } -.canon-Select--trigger:focus-visible { +.canon-SelectTrigger:focus-visible { border-color: var(--canon-border-pressed); outline: 0; } -.canon-Select--trigger[data-invalid] { +.canon-SelectTrigger[data-invalid] { border-color: var(--canon-fg-danger); } -.canon-Select--trigger[data-invalid]:hover, .canon-Select--trigger[data-invalid]:focus-visible { +.canon-SelectTrigger[data-invalid]:hover, .canon-SelectTrigger[data-invalid]:focus-visible { border-width: 2px; } -.canon-Select--trigger[data-disabled] { +.canon-SelectTrigger[data-disabled] { cursor: not-allowed; border-color: var(--canon-border-disabled); color: var(--canon-fg-disabled); } -.canon-Select--trigger[data-size="small"] { +.canon-SelectTrigger[data-size="small"] { height: 2rem; } -.canon-Select--trigger[data-size="medium"] { +.canon-SelectTrigger[data-size="medium"] { height: 3rem; } @@ -87,11 +87,11 @@ transition: transform .2s; } -.canon-Select--trigger[data-popup-open] .canon-SelectIcon { +.canon-SelectTrigger[data-popup-open] .canon-SelectIcon { transform: rotate(180deg); } -.canon-Select--popup { +.canon-SelectPopup { box-sizing: border-box; max-height: var(--available-height); background-color: var(--canon-bg-surface-1); @@ -106,12 +106,12 @@ box-shadow: 0 4px 12px #0003; } -.canon-Select--popup[data-starting-style], .canon-Select--popup[data-ending-style] { +.canon-SelectPopup[data-starting-style], .canon-SelectPopup[data-ending-style] { opacity: 0; transform: scale(.9); } -.canon-Select--item { +.canon-SelectItem { width: var(--anchor-width); padding-block: var(--canon-space-2); padding-inline: var(--canon-space-4); @@ -129,13 +129,13 @@ position: relative; } -.canon-Select--item[data-highlighted] { +.canon-SelectItem[data-highlighted] { z-index: 0; color: var(--canon-fg-primary); position: relative; } -.canon-Select--item[data-highlighted]:before { +.canon-SelectItem[data-highlighted]:before { content: ""; z-index: -1; background-color: var(--canon-bg-tint-hover); @@ -145,37 +145,37 @@ inset-inline: .25rem; } -.canon-Select--item[data-disabled] { +.canon-SelectItem[data-disabled] { cursor: not-allowed; color: var(--canon-fg-disabled); } -.canon-Select--item-indicator { +.canon-SelectItemIndicator { grid-area: icon; justify-content: center; align-items: center; display: flex; } -.canon-Select--item-text { +.canon-SelectItemText { flex: 1; grid-area: text; } -.canon-Select--required { +.canon-SelectRequired { color: var(--canon-fg-secondary); font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); margin-left: var(--canon-space-1); } -.canon-Select--icon { +.canon-SelectIcon { justify-content: center; align-items: center; display: flex; } -.canon-Select--value { +.canon-SelectValue { text-overflow: ellipsis; white-space: nowrap; width: 100%; diff --git a/packages/canon/css/styles.css b/packages/canon/css/styles.css index e4375692f9..8af66d68fa 100644 --- a/packages/canon/css/styles.css +++ b/packages/canon/css/styles.css @@ -9308,12 +9308,12 @@ height: 32px; } -.canon-Button[data-size="small"] .canon-Button--icon { +.canon-Button[data-size="small"] .canon-ButtonIcon { width: 1rem; height: 1rem; } -.canon-Button[data-size="medium"] .canon-Button--icon { +.canon-Button[data-size="medium"] .canon-ButtonIcon { width: 1.5rem; height: 1.5rem; } @@ -9634,12 +9634,12 @@ height: 32px; } -.canon-IconButton[data-size="small"] .canon-IconButton--icon { +.canon-IconButton[data-size="small"] .canon-IconButtonIcon { width: 1rem; height: 1rem; } -.canon-IconButton[data-size="medium"] .canon-IconButton--icon { +.canon-IconButton[data-size="medium"] .canon-IconButtonIcon { width: 1.5rem; height: 1.5rem; } @@ -9651,14 +9651,14 @@ display: flex; } -.canon-TextField--label { +.canon-TextFieldLabel { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-primary); margin-bottom: var(--canon-space-1_5); } -.canon-TextField--description { +.canon-TextFieldDescription { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-secondary); @@ -9666,7 +9666,7 @@ margin: 0; } -.canon-TextField--error { +.canon-TextFieldError { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-danger); @@ -9674,7 +9674,7 @@ margin: 0; } -.canon-TextField--input { +.canon-TextFieldInput { border-radius: var(--canon-radius-3); border: 1px solid var(--canon-border); padding: 0 var(--canon-space-4); @@ -9687,39 +9687,39 @@ transition: border-color .2s ease-in-out, outline-color .2s ease-in-out; } -.canon-TextField--input::placeholder { +.canon-TextFieldInput::placeholder { color: var(--canon-fg-secondary); } -.canon-TextField--input:hover { +.canon-TextFieldInput:hover { border-color: var(--canon-border-hover); } -.canon-TextField--input:focus-visible { +.canon-TextFieldInput:focus-visible { outline-color: var(--canon-border-pressed); border-color: var(--canon-border-pressed); outline-width: 0; } -.canon-TextField--input[data-invalid] { +.canon-TextFieldInput[data-invalid] { border-color: var(--canon-fg-danger); } -.canon-TextField--input[data-disabled] { +.canon-TextFieldInput[data-disabled] { opacity: .5; cursor: not-allowed; border: 1px solid var(--canon-border-disabled); } -.canon-TextField--input[data-size="small"] { +.canon-TextFieldInput[data-size="small"] { height: 2rem; } -.canon-TextField--input[data-size="medium"] { +.canon-TextFieldInput[data-size="medium"] { height: 2.5rem; } -.canon-TextField--required { +.canon-TextFieldRequired { color: var(--canon-fg-secondary); font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); @@ -9944,14 +9944,14 @@ display: flex; } -.canon-Select--label { +.canon-SelectLabel { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-primary); margin-bottom: var(--canon-space-1_5); } -.canon-Select--description { +.canon-SelectDescription { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-secondary); @@ -9959,7 +9959,7 @@ margin: 0; } -.canon-Select--error { +.canon-SelectError { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-danger); @@ -9967,7 +9967,7 @@ margin: 0; } -.canon-Select--trigger { +.canon-SelectTrigger { box-sizing: border-box; border-radius: var(--canon-radius-3); border: 1px solid var(--canon-border); @@ -9986,38 +9986,38 @@ display: flex; } -.canon-Select--trigger::placeholder { +.canon-SelectTrigger::placeholder { color: var(--canon-fg-secondary); } -.canon-Select--trigger:hover { +.canon-SelectTrigger:hover { border-color: var(--canon-border-hover); } -.canon-Select--trigger:focus-visible { +.canon-SelectTrigger:focus-visible { border-color: var(--canon-border-pressed); outline: 0; } -.canon-Select--trigger[data-invalid] { +.canon-SelectTrigger[data-invalid] { border-color: var(--canon-fg-danger); } -.canon-Select--trigger[data-invalid]:hover, .canon-Select--trigger[data-invalid]:focus-visible { +.canon-SelectTrigger[data-invalid]:hover, .canon-SelectTrigger[data-invalid]:focus-visible { border-width: 2px; } -.canon-Select--trigger[data-disabled] { +.canon-SelectTrigger[data-disabled] { cursor: not-allowed; border-color: var(--canon-border-disabled); color: var(--canon-fg-disabled); } -.canon-Select--trigger[data-size="small"] { +.canon-SelectTrigger[data-size="small"] { height: 2rem; } -.canon-Select--trigger[data-size="medium"] { +.canon-SelectTrigger[data-size="medium"] { height: 3rem; } @@ -10026,11 +10026,11 @@ transition: transform .2s; } -.canon-Select--trigger[data-popup-open] .canon-SelectIcon { +.canon-SelectTrigger[data-popup-open] .canon-SelectIcon { transform: rotate(180deg); } -.canon-Select--popup { +.canon-SelectPopup { box-sizing: border-box; max-height: var(--available-height); background-color: var(--canon-bg-surface-1); @@ -10045,12 +10045,12 @@ box-shadow: 0 4px 12px #0003; } -.canon-Select--popup[data-starting-style], .canon-Select--popup[data-ending-style] { +.canon-SelectPopup[data-starting-style], .canon-SelectPopup[data-ending-style] { opacity: 0; transform: scale(.9); } -.canon-Select--item { +.canon-SelectItem { width: var(--anchor-width); padding-block: var(--canon-space-2); padding-inline: var(--canon-space-4); @@ -10068,13 +10068,13 @@ position: relative; } -.canon-Select--item[data-highlighted] { +.canon-SelectItem[data-highlighted] { z-index: 0; color: var(--canon-fg-primary); position: relative; } -.canon-Select--item[data-highlighted]:before { +.canon-SelectItem[data-highlighted]:before { content: ""; z-index: -1; background-color: var(--canon-bg-tint-hover); @@ -10084,37 +10084,37 @@ inset-inline: .25rem; } -.canon-Select--item[data-disabled] { +.canon-SelectItem[data-disabled] { cursor: not-allowed; color: var(--canon-fg-disabled); } -.canon-Select--item-indicator { +.canon-SelectItemIndicator { grid-area: icon; justify-content: center; align-items: center; display: flex; } -.canon-Select--item-text { +.canon-SelectItemText { flex: 1; grid-area: text; } -.canon-Select--required { +.canon-SelectRequired { color: var(--canon-fg-secondary); font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); margin-left: var(--canon-space-1); } -.canon-Select--icon { +.canon-SelectIcon { justify-content: center; align-items: center; display: flex; } -.canon-Select--value { +.canon-SelectValue { text-overflow: ellipsis; white-space: nowrap; width: 100%; diff --git a/packages/canon/css/textfield.css b/packages/canon/css/textfield.css index eb43b1f77f..bf21b3bd74 100644 --- a/packages/canon/css/textfield.css +++ b/packages/canon/css/textfield.css @@ -5,14 +5,14 @@ display: flex; } -.canon-TextField--label { +.canon-TextFieldLabel { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-primary); margin-bottom: var(--canon-space-1_5); } -.canon-TextField--description { +.canon-TextFieldDescription { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-secondary); @@ -20,7 +20,7 @@ margin: 0; } -.canon-TextField--error { +.canon-TextFieldError { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-danger); @@ -28,7 +28,7 @@ margin: 0; } -.canon-TextField--input { +.canon-TextFieldInput { border-radius: var(--canon-radius-3); border: 1px solid var(--canon-border); padding: 0 var(--canon-space-4); @@ -41,39 +41,39 @@ transition: border-color .2s ease-in-out, outline-color .2s ease-in-out; } -.canon-TextField--input::placeholder { +.canon-TextFieldInput::placeholder { color: var(--canon-fg-secondary); } -.canon-TextField--input:hover { +.canon-TextFieldInput:hover { border-color: var(--canon-border-hover); } -.canon-TextField--input:focus-visible { +.canon-TextFieldInput:focus-visible { outline-color: var(--canon-border-pressed); border-color: var(--canon-border-pressed); outline-width: 0; } -.canon-TextField--input[data-invalid] { +.canon-TextFieldInput[data-invalid] { border-color: var(--canon-fg-danger); } -.canon-TextField--input[data-disabled] { +.canon-TextFieldInput[data-disabled] { opacity: .5; cursor: not-allowed; border: 1px solid var(--canon-border-disabled); } -.canon-TextField--input[data-size="small"] { +.canon-TextFieldInput[data-size="small"] { height: 2rem; } -.canon-TextField--input[data-size="medium"] { +.canon-TextFieldInput[data-size="medium"] { height: 2.5rem; } -.canon-TextField--required { +.canon-TextFieldRequired { color: var(--canon-fg-secondary); font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); diff --git a/packages/canon/src/components/Button/Button.tsx b/packages/canon/src/components/Button/Button.tsx index b9a1229fbd..ca652013b4 100644 --- a/packages/canon/src/components/Button/Button.tsx +++ b/packages/canon/src/components/Button/Button.tsx @@ -50,9 +50,9 @@ export const Button = forwardRef( style={style} {...rest} > - {iconStart && } + {iconStart && } {children} - {iconEnd && } + {iconEnd && } ); }, diff --git a/packages/canon/src/components/Button/styles.css b/packages/canon/src/components/Button/styles.css index a05a0b3a4f..785fbf3b88 100644 --- a/packages/canon/src/components/Button/styles.css +++ b/packages/canon/src/components/Button/styles.css @@ -94,12 +94,12 @@ height: 32px; } -.canon-Button[data-size='small'] .canon-Button--icon { +.canon-Button[data-size='small'] .canon-ButtonIcon { width: 1rem; height: 1rem; } -.canon-Button[data-size='medium'] .canon-Button--icon { +.canon-Button[data-size='medium'] .canon-ButtonIcon { width: 1.5rem; height: 1.5rem; } diff --git a/packages/canon/src/components/IconButton/IconButton.tsx b/packages/canon/src/components/IconButton/IconButton.tsx index 7e7f94d22e..b2581844bc 100644 --- a/packages/canon/src/components/IconButton/IconButton.tsx +++ b/packages/canon/src/components/IconButton/IconButton.tsx @@ -45,7 +45,7 @@ export const IconButton = forwardRef( style={style} {...rest} > - + ); }, diff --git a/packages/canon/src/components/IconButton/styles.css b/packages/canon/src/components/IconButton/styles.css index 089dd1fcda..859c512fd6 100644 --- a/packages/canon/src/components/IconButton/styles.css +++ b/packages/canon/src/components/IconButton/styles.css @@ -94,12 +94,12 @@ width: 32px; } -.canon-IconButton[data-size='small'] .canon-IconButton--icon { +.canon-IconButton[data-size='small'] .canon-IconButtonIcon { width: 1rem; height: 1rem; } -.canon-IconButton[data-size='medium'] .canon-IconButton--icon { +.canon-IconButton[data-size='medium'] .canon-IconButtonIcon { width: 1.5rem; height: 1.5rem; } diff --git a/packages/canon/src/components/Select/Select.styles.css b/packages/canon/src/components/Select/Select.styles.css index 718636d954..fa00d81302 100644 --- a/packages/canon/src/components/Select/Select.styles.css +++ b/packages/canon/src/components/Select/Select.styles.css @@ -21,14 +21,14 @@ width: 100%; } -.canon-Select--label { +.canon-SelectLabel { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-primary); margin-bottom: var(--canon-space-1_5); } -.canon-Select--description { +.canon-SelectDescription { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-secondary); @@ -36,7 +36,7 @@ padding-top: var(--canon-space-1_5); } -.canon-Select--error { +.canon-SelectError { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-danger); @@ -44,7 +44,7 @@ padding-top: var(--canon-space-1_5); } -.canon-Select--trigger { +.canon-SelectTrigger { box-sizing: border-box; border-radius: var(--canon-radius-3); border: 1px solid var(--canon-border); @@ -63,41 +63,41 @@ gap: var(--canon-space-2); } -.canon-Select--trigger::placeholder { +.canon-SelectTrigger::placeholder { color: var(--canon-fg-secondary); } -.canon-Select--trigger:hover { +.canon-SelectTrigger:hover { border-color: var(--canon-border-hover); } -.canon-Select--trigger:focus-visible { +.canon-SelectTrigger:focus-visible { border-color: var(--canon-border-pressed); outline: 0; } -.canon-Select--trigger[data-invalid] { +.canon-SelectTrigger[data-invalid] { border-color: var(--canon-fg-danger); } -.canon-Select--trigger[data-invalid]:hover { +.canon-SelectTrigger[data-invalid]:hover { border-width: 2px; } -.canon-Select--trigger[data-invalid]:focus-visible { +.canon-SelectTrigger[data-invalid]:focus-visible { border-width: 2px; } -.canon-Select--trigger[data-disabled] { +.canon-SelectTrigger[data-disabled] { cursor: not-allowed; border-color: var(--canon-border-disabled); color: var(--canon-fg-disabled); } -.canon-Select--trigger[data-size='small'] { +.canon-SelectTrigger[data-size='small'] { height: 2rem; } -.canon-Select--trigger[data-size='medium'] { +.canon-SelectTrigger[data-size='medium'] { height: 3rem; } @@ -106,11 +106,11 @@ transition: transform 0.2s ease; } -.canon-Select--trigger[data-popup-open] .canon-SelectIcon { +.canon-SelectTrigger[data-popup-open] .canon-SelectIcon { transform: rotate(180deg); } -.canon-Select--popup { +.canon-SelectPopup { box-sizing: border-box; max-height: var(--available-height); overflow-y: auto; @@ -125,13 +125,13 @@ transition: transform 150ms, opacity 150ms; } -.canon-Select--popup[data-starting-style], -.canon-Select--popup[data-ending-style] { +.canon-SelectPopup[data-starting-style], +.canon-SelectPopup[data-ending-style] { opacity: 0; transform: scale(0.9); } -.canon-Select--item { +.canon-SelectItem { position: relative; width: var(--anchor-width); display: grid; @@ -149,13 +149,13 @@ outline: none; } -.canon-Select--item[data-highlighted] { +.canon-SelectItem[data-highlighted] { z-index: 0; position: relative; color: var(--canon-fg-primary); } -.canon-Select--item[data-highlighted]::before { +.canon-SelectItem[data-highlighted]::before { content: ''; z-index: -1; position: absolute; @@ -165,37 +165,37 @@ background-color: var(--canon-bg-tint-hover); } -.canon-Select--item[data-disabled] { +.canon-SelectItem[data-disabled] { cursor: not-allowed; color: var(--canon-fg-disabled); } -.canon-Select--item-indicator { +.canon-SelectItemIndicator { grid-area: icon; display: flex; align-items: center; justify-content: center; } -.canon-Select--item-text { +.canon-SelectItemText { flex: 1; grid-area: text; } -.canon-Select--required { +.canon-SelectRequired { color: var(--canon-fg-secondary); font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); margin-left: var(--canon-space-1); } -.canon-Select--icon { +.canon-SelectIcon { display: flex; align-items: center; justify-content: center; } -.canon-Select--value { +.canon-SelectValue { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; diff --git a/packages/canon/src/components/Select/Select.tsx b/packages/canon/src/components/Select/Select.tsx index d8760fc1ea..be8047ca02 100644 --- a/packages/canon/src/components/Select/Select.tsx +++ b/packages/canon/src/components/Select/Select.tsx @@ -48,10 +48,10 @@ export const Select = forwardRef((props, ref) => { return (
{label && ( -