From 67d3698100b1eb0c18680aa0aae4a57096a0ed18 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 25 Jul 2025 07:36:59 +0100 Subject: [PATCH 01/14] Improve Table cells Signed-off-by: Charles de Dreuille --- .../components/DataTable/mocked-columns.tsx | 59 +++++++++- .../components/DataTable/mocked-components.ts | 107 ++++++++++++++++++ packages/ui/src/components/Table/Table.tsx | 2 - .../TableCellLink/TableCellLink.stories.tsx | 48 -------- .../TableCellLink/TableCellLink.styles.css | 21 ---- .../Table/TableCellLink/TableCellLink.tsx | 47 -------- .../components/Table/TableCellLink/types.ts | 26 ----- .../TableCellProfile.stories.tsx | 2 +- .../TableCellProfile/TableCellProfile.tsx | 15 ++- .../Table/TableCellProfile/types.ts | 5 +- .../TableCellText/TableCellText.stories.tsx | 50 +++++++- .../TableCellText/TableCellText.styles.css | 14 +++ .../Table/TableCellText/TableCellText.tsx | 37 +++++- .../components/Table/TableCellText/types.ts | 3 + packages/ui/src/components/Table/index.ts | 1 - packages/ui/src/css/components.css | 1 - packages/ui/src/utils/componentDefinitions.ts | 3 +- 17 files changed, 278 insertions(+), 163 deletions(-) delete mode 100644 packages/ui/src/components/Table/TableCellLink/TableCellLink.stories.tsx delete mode 100644 packages/ui/src/components/Table/TableCellLink/TableCellLink.styles.css delete mode 100644 packages/ui/src/components/Table/TableCellLink/TableCellLink.tsx delete mode 100644 packages/ui/src/components/Table/TableCellLink/types.ts diff --git a/packages/ui/src/components/DataTable/mocked-columns.tsx b/packages/ui/src/components/DataTable/mocked-columns.tsx index c44a434d6a..4b06bbfaa1 100644 --- a/packages/ui/src/components/DataTable/mocked-columns.tsx +++ b/packages/ui/src/components/DataTable/mocked-columns.tsx @@ -66,7 +66,7 @@ export const columns: ColumnDef[] = [ ); }, @@ -84,3 +84,60 @@ export const columns: ColumnDef[] = [ size: 150, }, ]; + +export const columns2: ColumnDef[] = [ + { + accessorKey: 'type', + header: 'Type', + cell: ({ row }) => ( + + ), + size: 100, + }, + { + accessorKey: 'name', + header: 'Name', + cell: ({ row }) => ( + + ), + size: 450, + }, + { + accessorKey: 'owner', + header: 'Owner', + cell: ({ row }) => { + const owner = row.getValue('owner') as DataProps['owner']; + + return ( + + ); + }, + }, + { + accessorKey: 'lifecycle', + header: 'Lifecycle', + cell: ({ row }) => ( + + ), + size: 100, + }, + { + accessorKey: 'system', + header: 'System', + cell: ({ row }) => ( + + ), + size: 100, + }, +]; diff --git a/packages/ui/src/components/DataTable/mocked-components.ts b/packages/ui/src/components/DataTable/mocked-components.ts index 40d790de78..2c7b1f9064 100644 --- a/packages/ui/src/components/DataTable/mocked-components.ts +++ b/packages/ui/src/components/DataTable/mocked-components.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + export interface DataProps { name: string; owner: { @@ -23,6 +24,7 @@ export interface DataProps { type: 'documentation' | 'library' | 'service' | 'website' | 'other'; description?: string; tags?: string[]; + lifecycle: 'experimental' | 'production'; } export const data: DataProps[] = [ @@ -37,6 +39,7 @@ export const data: DataProps[] = [ description: 'A comprehensive service handling user authentication and role-based access control across all applications.', tags: ['security', 'authentication', 'authorization'], + lifecycle: 'production', }, { name: 'user-interface-dashboard-and-analytics-platform', @@ -49,6 +52,7 @@ export const data: DataProps[] = [ description: 'Interactive dashboard providing real-time analytics and data visualization for business metrics.', tags: ['analytics', 'visualization', 'dashboard'], + lifecycle: 'production', }, { name: 'payment-gateway', @@ -61,6 +65,7 @@ export const data: DataProps[] = [ description: 'Secure payment processing system supporting multiple payment methods and currencies.', tags: ['payments', 'security', 'finance'], + lifecycle: 'production', }, { name: 'real-time-analytics-processing-and-visualization-engine', @@ -73,6 +78,7 @@ export const data: DataProps[] = [ description: 'High-performance engine for processing and visualizing streaming data analytics.', tags: ['analytics', 'real-time', 'data-processing'], + lifecycle: 'experimental', }, { name: 'notification-center', @@ -85,6 +91,7 @@ export const data: DataProps[] = [ description: 'Centralized system for managing and delivering notifications across multiple channels.', tags: ['notifications', 'messaging'], + lifecycle: 'production', }, { name: 'administrative-control-panel-and-user-management-interface', @@ -97,6 +104,7 @@ export const data: DataProps[] = [ description: 'Admin interface for managing users, permissions, and system configurations.', tags: ['admin', 'user-management', 'configuration'], + lifecycle: 'production', }, { name: 'search-indexer', @@ -109,6 +117,7 @@ export const data: DataProps[] = [ description: 'Service responsible for indexing and updating searchable content across the platform.', tags: ['search', 'indexing'], + lifecycle: 'production', }, { name: 'cross-platform-mobile-application-framework', @@ -121,6 +130,7 @@ export const data: DataProps[] = [ description: 'Framework enabling development of cross-platform mobile applications with shared codebase.', tags: ['mobile', 'framework', 'cross-platform'], + lifecycle: 'experimental', }, { name: 'database-migration', @@ -133,6 +143,7 @@ export const data: DataProps[] = [ description: 'Tools and scripts for managing database schema migrations and data transformations.', tags: ['database', 'migration', 'devops'], + lifecycle: 'production', }, { name: 'api-gateway', @@ -145,6 +156,7 @@ export const data: DataProps[] = [ description: 'Central entry point for all API requests, handling routing, authentication, and rate limiting.', tags: ['api', 'gateway', 'security', 'routing'], + lifecycle: 'production', }, { name: 'content-management', @@ -157,6 +169,7 @@ export const data: DataProps[] = [ description: 'System for managing and delivering digital content across multiple channels.', tags: ['content', 'management', 'delivery'], + lifecycle: 'production', }, { name: 'enterprise-reporting-and-analytics-dashboard', @@ -169,6 +182,7 @@ export const data: DataProps[] = [ description: 'Comprehensive business intelligence platform for enterprise-wide reporting and analytics.', tags: ['analytics', 'reporting', 'business-intelligence'], + lifecycle: 'production', }, { name: 'image-processing-and-optimization-service', @@ -181,6 +195,7 @@ export const data: DataProps[] = [ description: 'Service for processing, optimizing, and delivering images across different devices and networks.', tags: ['media', 'optimization', 'processing'], + lifecycle: 'production', }, { name: 'customer-portal', @@ -193,6 +208,7 @@ export const data: DataProps[] = [ description: 'Self-service portal for customers to manage their accounts and access services.', tags: ['customer', 'self-service'], + lifecycle: 'production', }, { name: 'log-aggregator', @@ -205,6 +221,7 @@ export const data: DataProps[] = [ description: 'Centralized logging system for collecting, processing, and analyzing application logs.', tags: ['logging', 'monitoring', 'devops'], + lifecycle: 'production', }, { name: 'identity-provider', @@ -217,6 +234,7 @@ export const data: DataProps[] = [ description: 'Service managing user identities and authentication across the organization.', tags: ['identity', 'security', 'authentication'], + lifecycle: 'production', }, { name: 'document-storage', @@ -229,6 +247,7 @@ export const data: DataProps[] = [ description: 'Secure and scalable document storage system with version control and access management.', tags: ['storage', 'documents', 'version-control'], + lifecycle: 'production', }, { name: 'workflow-engine', @@ -241,6 +260,7 @@ export const data: DataProps[] = [ description: 'Engine for defining and executing business processes and workflows.', tags: ['workflow', 'automation'], + lifecycle: 'experimental', }, { name: 'mobile-backend', @@ -253,6 +273,7 @@ export const data: DataProps[] = [ description: 'Backend services supporting mobile applications with optimized APIs and data synchronization.', tags: ['mobile', 'backend', 'api'], + lifecycle: 'production', }, { name: 'system-monitoring-and-alerting-dashboard', @@ -265,6 +286,7 @@ export const data: DataProps[] = [ description: 'Real-time monitoring and alerting system for infrastructure and application health.', tags: ['monitoring', 'alerting', 'devops', 'infrastructure'], + lifecycle: 'production', }, { name: 'email-service', @@ -277,6 +299,7 @@ export const data: DataProps[] = [ description: 'Reliable email delivery service with templates and tracking capabilities.', tags: ['email', 'communication'], + lifecycle: 'production', }, { name: 'data-pipeline', @@ -289,6 +312,7 @@ export const data: DataProps[] = [ description: 'ETL pipeline for processing and transforming large volumes of data.', tags: ['data', 'etl', 'pipeline'], + lifecycle: 'production', }, { name: 'configuration-manager', @@ -301,6 +325,7 @@ export const data: DataProps[] = [ description: 'Centralized system for managing application configurations across environments.', tags: ['configuration', 'management'], + lifecycle: 'production', }, { name: 'testing-framework', @@ -313,6 +338,7 @@ export const data: DataProps[] = [ description: 'Comprehensive testing framework supporting various types of automated tests.', tags: ['testing', 'automation', 'qa'], + lifecycle: 'production', }, { name: 'cache-service', @@ -325,6 +351,7 @@ export const data: DataProps[] = [ description: 'Distributed caching service for improving application performance.', tags: ['caching', 'performance'], + lifecycle: 'production', }, { name: 'billing-system', @@ -337,6 +364,7 @@ export const data: DataProps[] = [ description: 'System for managing customer billing, invoicing, and payment processing.', tags: ['billing', 'finance', 'payments'], + lifecycle: 'production', }, { name: 'comprehensive-product-documentation-and-api-reference', @@ -349,6 +377,7 @@ export const data: DataProps[] = [ description: 'Complete documentation covering product features, APIs, and integration guides.', tags: ['documentation', 'api', 'reference'], + lifecycle: 'production', }, { name: 'queue-manager', @@ -361,6 +390,7 @@ export const data: DataProps[] = [ description: 'Message queue system for asynchronous processing and event handling.', tags: ['queue', 'messaging', 'async'], + lifecycle: 'production', }, { name: 'security-scanner', @@ -373,6 +403,7 @@ export const data: DataProps[] = [ description: 'Automated security scanning tool for identifying vulnerabilities in code and infrastructure.', tags: ['security', 'scanning', 'vulnerability'], + lifecycle: 'experimental', }, { name: 'user-profile', @@ -385,6 +416,7 @@ export const data: DataProps[] = [ description: 'User profile management interface with personalization features.', tags: ['user', 'profile', 'personalization'], + lifecycle: 'production', }, { name: 'data-warehouse', @@ -397,6 +429,7 @@ export const data: DataProps[] = [ description: 'Centralized data repository for business intelligence and analytics.', tags: ['data', 'warehouse', 'analytics'], + lifecycle: 'production', }, { name: 'deployment-automation', @@ -409,6 +442,7 @@ export const data: DataProps[] = [ description: 'Automated deployment pipeline for continuous integration and delivery.', tags: ['deployment', 'automation', 'ci-cd', 'devops'], + lifecycle: 'production', }, { name: 'chat-service', @@ -421,6 +455,7 @@ export const data: DataProps[] = [ description: 'Real-time chat service supporting text, file sharing, and group conversations.', tags: ['chat', 'communication', 'real-time'], + lifecycle: 'experimental', }, { name: 'analytics-dashboard', @@ -433,6 +468,7 @@ export const data: DataProps[] = [ description: 'Interactive dashboard for visualizing and analyzing business metrics.', tags: ['analytics', 'dashboard', 'visualization'], + lifecycle: 'production', }, { name: 'file-uploader', @@ -445,6 +481,7 @@ export const data: DataProps[] = [ description: 'Service for handling secure file uploads with progress tracking and validation.', tags: ['storage', 'upload', 'files'], + lifecycle: 'production', }, { name: 'search-service', @@ -457,6 +494,7 @@ export const data: DataProps[] = [ description: 'Full-text search service with advanced filtering and ranking capabilities.', tags: ['search', 'full-text'], + lifecycle: 'production', }, { name: 'mobile-sdk', @@ -469,6 +507,7 @@ export const data: DataProps[] = [ description: 'Software development kit for building mobile applications with native features.', tags: ['mobile', 'sdk', 'development'], + lifecycle: 'production', }, { name: 'performance-monitor', @@ -481,6 +520,7 @@ export const data: DataProps[] = [ description: 'System for monitoring and analyzing application performance metrics.', tags: ['performance', 'monitoring', 'metrics'], + lifecycle: 'production', }, { name: 'content-delivery', @@ -493,6 +533,7 @@ export const data: DataProps[] = [ description: 'CDN service for optimized content delivery across global networks.', tags: ['cdn', 'content', 'delivery'], + lifecycle: 'production', }, { name: 'user-authentication', @@ -505,6 +546,7 @@ export const data: DataProps[] = [ description: 'Service handling user login, session management, and authentication flows.', tags: ['authentication', 'security', 'user'], + lifecycle: 'production', }, { name: 'data-export', @@ -517,6 +559,7 @@ export const data: DataProps[] = [ description: 'Service for exporting data in various formats with scheduling capabilities.', tags: ['data', 'export', 'scheduling'], + lifecycle: 'production', }, { name: 'admin-api', @@ -529,6 +572,7 @@ export const data: DataProps[] = [ description: 'API endpoints for administrative functions and system management.', tags: ['api', 'admin', 'management'], + lifecycle: 'production', }, { name: 'testing-dashboard', @@ -540,6 +584,7 @@ export const data: DataProps[] = [ type: 'website', description: 'Dashboard for monitoring test results and quality metrics.', tags: ['testing', 'dashboard', 'qa'], + lifecycle: 'production', }, { name: 'message-broker', @@ -552,6 +597,7 @@ export const data: DataProps[] = [ description: 'Message broker service for reliable event-driven communication between services.', tags: ['messaging', 'broker', 'event-driven'], + lifecycle: 'production', }, { name: 'payment-processor', @@ -564,6 +610,7 @@ export const data: DataProps[] = [ description: 'Service for processing financial transactions and payment methods.', tags: ['payments', 'finance', 'processing'], + lifecycle: 'production', }, { name: 'document-viewer', @@ -575,6 +622,7 @@ export const data: DataProps[] = [ type: 'website', description: 'Web-based document viewer supporting multiple file formats.', tags: ['documents', 'viewer'], + lifecycle: 'production', }, { name: 'load-balancer', @@ -587,6 +635,7 @@ export const data: DataProps[] = [ description: 'Service for distributing network traffic across multiple servers.', tags: ['load-balancing', 'networking', 'infrastructure'], + lifecycle: 'production', }, { name: 'security-audit', @@ -599,6 +648,7 @@ export const data: DataProps[] = [ description: 'Tools and processes for conducting security audits and compliance checks.', tags: ['security', 'audit', 'compliance'], + lifecycle: 'production', }, { name: 'user-settings', @@ -611,6 +661,7 @@ export const data: DataProps[] = [ description: 'Interface for users to manage their preferences and account settings.', tags: ['user', 'settings', 'preferences'], + lifecycle: 'production', }, { name: 'data-import', @@ -623,6 +674,7 @@ export const data: DataProps[] = [ description: 'Service for importing and validating data from external sources.', tags: ['data', 'import', 'validation'], + lifecycle: 'production', }, { name: 'infrastructure-monitor', @@ -635,6 +687,7 @@ export const data: DataProps[] = [ description: 'Monitoring system for infrastructure components and resources.', tags: ['monitoring', 'infrastructure', 'devops'], + lifecycle: 'production', }, { name: 'notification-manager', @@ -647,6 +700,7 @@ export const data: DataProps[] = [ description: 'Service for managing and delivering notifications across multiple channels.', tags: ['notifications', 'management'], + lifecycle: 'production', }, { name: 'analytics-processor', @@ -659,6 +713,7 @@ export const data: DataProps[] = [ description: 'Service for processing and analyzing business data and metrics.', tags: ['analytics', 'processing', 'metrics'], + lifecycle: 'production', }, { name: 'file-manager', @@ -670,6 +725,7 @@ export const data: DataProps[] = [ type: 'website', description: 'Web interface for managing files and storage resources.', tags: ['files', 'storage', 'management'], + lifecycle: 'production', }, { name: 'search-index', @@ -681,6 +737,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for maintaining and updating search indices.', tags: ['search', 'indexing'], + lifecycle: 'production', }, { name: 'mobile-authentication', @@ -693,6 +750,7 @@ export const data: DataProps[] = [ description: 'Authentication service specifically designed for mobile applications.', tags: ['mobile', 'authentication', 'security'], + lifecycle: 'experimental', }, { name: 'system-monitor', @@ -705,6 +763,7 @@ export const data: DataProps[] = [ description: 'Monitoring service for system health and performance metrics.', tags: ['monitoring', 'system', 'metrics'], + lifecycle: 'production', }, { name: 'media-processor', @@ -716,6 +775,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for processing and optimizing media files.', tags: ['media', 'processing', 'optimization'], + lifecycle: 'production', }, { name: 'user-management', @@ -727,6 +787,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for managing user accounts and permissions.', tags: ['user', 'management', 'security'], + lifecycle: 'production', }, { name: 'data-transformer', @@ -739,6 +800,7 @@ export const data: DataProps[] = [ description: 'Service for transforming data between different formats and structures.', tags: ['data', 'transformation'], + lifecycle: 'production', }, { name: 'admin-dashboard', @@ -751,6 +813,7 @@ export const data: DataProps[] = [ description: 'Administrative interface for system management and monitoring.', tags: ['admin', 'dashboard', 'management'], + lifecycle: 'production', }, { name: 'test-automation', @@ -762,6 +825,7 @@ export const data: DataProps[] = [ type: 'other', description: 'Tools and frameworks for automating testing processes.', tags: ['testing', 'automation', 'qa'], + lifecycle: 'production', }, { name: 'event-bus', @@ -773,6 +837,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Event-driven communication system between services.', tags: ['events', 'messaging', 'communication'], + lifecycle: 'production', }, { name: 'invoice-generator', @@ -784,6 +849,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for generating and managing invoices.', tags: ['invoices', 'finance'], + lifecycle: 'production', }, { name: 'document-editor', @@ -795,6 +861,7 @@ export const data: DataProps[] = [ type: 'website', description: 'Web-based document editing interface.', tags: ['documents', 'editor'], + lifecycle: 'experimental', }, { name: 'service-discovery', @@ -806,6 +873,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for discovering and registering available services.', tags: ['discovery', 'services', 'devops'], + lifecycle: 'production', }, { name: 'security-monitor', @@ -817,6 +885,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for monitoring security events and threats.', tags: ['security', 'monitoring', 'threats'], + lifecycle: 'production', }, { name: 'user-preferences', @@ -828,6 +897,7 @@ export const data: DataProps[] = [ type: 'website', description: 'Interface for managing user preferences and settings.', tags: ['user', 'preferences'], + lifecycle: 'production', }, { name: 'data-validator', @@ -839,6 +909,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for validating data integrity and format.', tags: ['data', 'validation'], + lifecycle: 'production', }, { name: 'infrastructure-automation', @@ -851,6 +922,7 @@ export const data: DataProps[] = [ description: 'Tools for automating infrastructure provisioning and management.', tags: ['infrastructure', 'automation', 'devops'], + lifecycle: 'production', }, { name: 'notification-dispatcher', @@ -863,6 +935,7 @@ export const data: DataProps[] = [ description: 'Service for dispatching notifications to appropriate channels.', tags: ['notifications', 'dispatch'], + lifecycle: 'production', }, { name: 'analytics-collector', @@ -874,6 +947,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for collecting and aggregating analytics data.', tags: ['analytics', 'collection', 'aggregation'], + lifecycle: 'production', }, { name: 'file-processor', @@ -885,6 +959,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for processing and managing files.', tags: ['files', 'processing'], + lifecycle: 'production', }, { name: 'search-analyzer', @@ -896,6 +971,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for analyzing search queries and results.', tags: ['search', 'analysis'], + lifecycle: 'experimental', }, { name: 'mobile-notifications', @@ -907,6 +983,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for sending notifications to mobile devices.', tags: ['mobile', 'notifications'], + lifecycle: 'experimental', }, { name: 'system-alerts', @@ -918,6 +995,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for managing and dispatching system alerts.', tags: ['alerts', 'system', 'monitoring'], + lifecycle: 'production', }, { name: 'media-encoder', @@ -929,6 +1007,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for encoding and processing media files.', tags: ['media', 'encoding'], + lifecycle: 'production', }, { name: 'user-authorization', @@ -940,6 +1019,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for managing user permissions and access control.', tags: ['authorization', 'security', 'user'], + lifecycle: 'production', }, { name: 'data-aggregator', @@ -951,6 +1031,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for aggregating data from multiple sources.', tags: ['data', 'aggregation'], + lifecycle: 'production', }, { name: 'admin-authentication', @@ -962,6 +1043,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Authentication service for administrative access.', tags: ['admin', 'authentication', 'security'], + lifecycle: 'production', }, { name: 'test-coverage', @@ -973,6 +1055,7 @@ export const data: DataProps[] = [ type: 'other', description: 'Tools for measuring and reporting test coverage.', tags: ['testing', 'coverage', 'qa'], + lifecycle: 'production', }, { name: 'event-processor', @@ -984,6 +1067,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for processing and handling events.', tags: ['events', 'processing'], + lifecycle: 'production', }, { name: 'payment-validator', @@ -995,6 +1079,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for validating payment transactions.', tags: ['payments', 'validation', 'finance'], + lifecycle: 'production', }, { name: 'document-converter', @@ -1006,6 +1091,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for converting documents between different formats.', tags: ['documents', 'conversion'], + lifecycle: 'experimental', }, { name: 'service-health', @@ -1017,6 +1103,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for monitoring and reporting service health status.', tags: ['health', 'monitoring', 'services'], + lifecycle: 'production', }, { name: 'security-logger', @@ -1028,6 +1115,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for logging security-related events and activities.', tags: ['security', 'logging'], + lifecycle: 'production', }, { name: 'user-analytics', @@ -1040,6 +1128,7 @@ export const data: DataProps[] = [ description: 'Analytics dashboard for user behavior and engagement metrics.', tags: ['analytics', 'user', 'metrics'], + lifecycle: 'experimental', }, { name: 'data-cleaner', @@ -1051,6 +1140,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for cleaning and standardizing data.', tags: ['data', 'cleaning'], + lifecycle: 'production', }, { name: 'infrastructure-deployer', @@ -1062,6 +1152,7 @@ export const data: DataProps[] = [ type: 'other', description: 'Tools for deploying and managing infrastructure resources.', tags: ['infrastructure', 'deployment', 'devops'], + lifecycle: 'production', }, { name: 'notification-queue', @@ -1073,6 +1164,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Queue system for managing notification delivery.', tags: ['notifications', 'queue'], + lifecycle: 'production', }, { name: 'analytics-exporter', @@ -1084,6 +1176,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for exporting analytics data in various formats.', tags: ['analytics', 'export'], + lifecycle: 'production', }, { name: 'file-validator', @@ -1095,6 +1188,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for validating file integrity and format.', tags: ['files', 'validation'], + lifecycle: 'production', }, { name: 'search-optimizer', @@ -1106,6 +1200,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for optimizing search performance and relevance.', tags: ['search', 'optimization'], + lifecycle: 'experimental', }, { name: 'mobile-analytics', @@ -1117,6 +1212,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Analytics service specifically for mobile applications.', tags: ['mobile', 'analytics'], + lifecycle: 'experimental', }, { name: 'system-logger', @@ -1128,6 +1224,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for logging system events and activities.', tags: ['logging', 'system'], + lifecycle: 'production', }, { name: 'media-validator', @@ -1139,6 +1236,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for validating media files and formats.', tags: ['media', 'validation'], + lifecycle: 'production', }, { name: 'user-audit', @@ -1150,6 +1248,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for auditing user activities and access.', tags: ['audit', 'user', 'security'], + lifecycle: 'production', }, { name: 'data-normalizer', @@ -1161,6 +1260,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for normalizing data formats and structures.', tags: ['data', 'normalization'], + lifecycle: 'production', }, { name: 'admin-authorization', @@ -1172,6 +1272,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Authorization service for administrative functions.', tags: ['admin', 'authorization', 'security'], + lifecycle: 'production', }, { name: 'test-reporting', @@ -1183,6 +1284,7 @@ export const data: DataProps[] = [ type: 'other', description: 'Tools for generating and managing test reports.', tags: ['testing', 'reporting', 'qa'], + lifecycle: 'production', }, { name: 'event-aggregator', @@ -1194,6 +1296,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for aggregating and processing events.', tags: ['events', 'aggregation'], + lifecycle: 'production', }, { name: 'payment-reconciler', @@ -1205,6 +1308,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for reconciling payment transactions.', tags: ['payments', 'reconciliation', 'finance'], + lifecycle: 'production', }, { name: 'document-validator', @@ -1216,6 +1320,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for validating document formats and content.', tags: ['documents', 'validation'], + lifecycle: 'production', }, { name: 'service-monitor', @@ -1227,6 +1332,7 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for monitoring service health and performance.', tags: ['monitoring', 'services', 'health'], + lifecycle: 'production', }, { name: 'security-validator', @@ -1238,5 +1344,6 @@ export const data: DataProps[] = [ type: 'service', description: 'Service for validating security configurations and policies.', tags: ['security', 'validation'], + lifecycle: 'production', }, ]; diff --git a/packages/ui/src/components/Table/Table.tsx b/packages/ui/src/components/Table/Table.tsx index ca89cae0c0..4c2597ba79 100644 --- a/packages/ui/src/components/Table/Table.tsx +++ b/packages/ui/src/components/Table/Table.tsx @@ -18,7 +18,6 @@ import { forwardRef } from 'react'; import clsx from 'clsx'; import { TableCell } from './TableCell/TableCell'; import { TableCellText } from './TableCellText/TableCellText'; -import { TableCellLink } from './TableCellLink/TableCellLink'; import { TableCellProfile } from './TableCellProfile/TableCellProfile'; import { useStyles } from '../../hooks/useStyles'; @@ -116,7 +115,6 @@ export const Table = { Row: TableRow, Cell: TableCell, CellText: TableCellText, - CellLink: TableCellLink, CellProfile: TableCellProfile, Caption: TableCaption, }; diff --git a/packages/ui/src/components/Table/TableCellLink/TableCellLink.stories.tsx b/packages/ui/src/components/Table/TableCellLink/TableCellLink.stories.tsx deleted file mode 100644 index bc9b66f18a..0000000000 --- a/packages/ui/src/components/Table/TableCellLink/TableCellLink.stories.tsx +++ /dev/null @@ -1,48 +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, StoryFn, StoryObj } from '@storybook/react'; -import { TableCellLink } from './TableCellLink'; -import { MemoryRouter } from 'react-router-dom'; - -const meta = { - title: 'Components/Table/TableCellLink', - component: TableCellLink, - decorators: [ - (Story: StoryFn) => ( - - - - ), - ], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - title: 'I am a link', - href: 'https://ui.backstage.io', - }, -}; - -export const WithDescription: Story = { - args: { - ...Default.args, - description: 'This is a description', - }, -}; diff --git a/packages/ui/src/components/Table/TableCellLink/TableCellLink.styles.css b/packages/ui/src/components/Table/TableCellLink/TableCellLink.styles.css deleted file mode 100644 index d4e10631fe..0000000000 --- a/packages/ui/src/components/Table/TableCellLink/TableCellLink.styles.css +++ /dev/null @@ -1,21 +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. - */ - -.bui-TableCellLink { - display: flex; - flex-direction: column; - gap: var(--bui-space-0_5); -} diff --git a/packages/ui/src/components/Table/TableCellLink/TableCellLink.tsx b/packages/ui/src/components/Table/TableCellLink/TableCellLink.tsx deleted file mode 100644 index e27a37977f..0000000000 --- a/packages/ui/src/components/Table/TableCellLink/TableCellLink.tsx +++ /dev/null @@ -1,47 +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 { forwardRef } from 'react'; -import clsx from 'clsx'; -import { TableCellLinkProps } from './types'; -import { Text } from '../../Text/Text'; -import { Link } from '../../Link/Link'; -import { useStyles } from '../../../hooks/useStyles'; - -/** @public */ -const TableCellLink = forwardRef( - ({ className, title, description, href, render, ...props }, ref) => { - const { classNames } = useStyles('Table'); - - return ( -
- {title && {title}} - {description && ( - - {description} - - )} -
- ); - }, -); -TableCellLink.displayName = 'TableCellLink'; - -export { TableCellLink }; diff --git a/packages/ui/src/components/Table/TableCellLink/types.ts b/packages/ui/src/components/Table/TableCellLink/types.ts deleted file mode 100644 index 51df3724cf..0000000000 --- a/packages/ui/src/components/Table/TableCellLink/types.ts +++ /dev/null @@ -1,26 +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 { useRender } from '@base-ui-components/react/use-render'; - -/** @public */ -export interface TableCellLinkProps - extends React.HTMLAttributes { - title: string; - description?: string; - href: string; - render?: useRender.ComponentProps<'a'>['render']; -} diff --git a/packages/ui/src/components/Table/TableCellProfile/TableCellProfile.stories.tsx b/packages/ui/src/components/Table/TableCellProfile/TableCellProfile.stories.tsx index 230fc0e3d1..9543d751ac 100644 --- a/packages/ui/src/components/Table/TableCellProfile/TableCellProfile.stories.tsx +++ b/packages/ui/src/components/Table/TableCellProfile/TableCellProfile.stories.tsx @@ -50,6 +50,6 @@ export const Fallback: Story = { export const WithLink: Story = { args: { ...Default.args, - to: 'https://www.google.com', + href: 'https://www.google.com', }, }; diff --git a/packages/ui/src/components/Table/TableCellProfile/TableCellProfile.tsx b/packages/ui/src/components/Table/TableCellProfile/TableCellProfile.tsx index bb2371bd6d..25e5337e0d 100644 --- a/packages/ui/src/components/Table/TableCellProfile/TableCellProfile.tsx +++ b/packages/ui/src/components/Table/TableCellProfile/TableCellProfile.tsx @@ -24,7 +24,10 @@ import { useStyles } from '../../../hooks/useStyles'; /** @public */ const TableCellProfile = forwardRef( - ({ className, src, name, to, withImage = true, ...rest }, ref) => { + ( + { className, src, name, href, description, color = 'primary', ...rest }, + ref, + ) => { const { classNames } = useStyles('Table'); return ( @@ -33,7 +36,7 @@ const TableCellProfile = forwardRef( className={clsx(classNames.cellProfile, className)} {...rest} > - {withImage && ( + {src && ( ( )} - {name && to ? ( - {name} + {name && href ? ( + {name} ) : ( - {name} + + {name} + )} ); diff --git a/packages/ui/src/components/Table/TableCellProfile/types.ts b/packages/ui/src/components/Table/TableCellProfile/types.ts index d5a7b18cc2..9c66ca2d3a 100644 --- a/packages/ui/src/components/Table/TableCellProfile/types.ts +++ b/packages/ui/src/components/Table/TableCellProfile/types.ts @@ -19,6 +19,7 @@ export interface TableCellProfileProps extends React.HTMLAttributes { src?: string; name?: string; - to?: string; - withImage?: boolean; + href?: string; + description?: string; + color?: 'primary' | 'secondary'; } diff --git a/packages/ui/src/components/Table/TableCellText/TableCellText.stories.tsx b/packages/ui/src/components/Table/TableCellText/TableCellText.stories.tsx index 3756dd2544..108ad5341d 100644 --- a/packages/ui/src/components/Table/TableCellText/TableCellText.stories.tsx +++ b/packages/ui/src/components/Table/TableCellText/TableCellText.stories.tsx @@ -14,8 +14,10 @@ * limitations under the License. */ -import type { Meta, StoryObj } from '@storybook/react'; +import type { Meta, StoryFn, StoryObj } from '@storybook/react'; import { TableCellText } from './TableCellText'; +import { Icon } from '../../Icon/Icon'; +import { MemoryRouter } from 'react-router-dom'; const meta = { title: 'Components/Table/TableCellText', @@ -37,3 +39,49 @@ export const WithDescription: Story = { description: 'This is a description', }, }; + +export const WithIcon: Story = { + args: { + ...Default.args, + }, + render: args => ( + } /> + ), +}; + +export const WithIconAndDescription: Story = { + args: { + ...WithDescription.args, + }, + render: args => ( + } /> + ), +}; + +export const WithLink: Story = { + args: { + ...WithDescription.args, + href: '/home', + }, + decorators: [ + (Story: StoryFn) => ( + + + + ), + ], +}; + +export const WithExternalLink: Story = { + args: { + ...WithDescription.args, + href: 'https://www.google.com', + }, + decorators: [ + (Story: StoryFn) => ( + + + + ), + ], +}; diff --git a/packages/ui/src/components/Table/TableCellText/TableCellText.styles.css b/packages/ui/src/components/Table/TableCellText/TableCellText.styles.css index f38f8b3689..1dbc168b47 100644 --- a/packages/ui/src/components/Table/TableCellText/TableCellText.styles.css +++ b/packages/ui/src/components/Table/TableCellText/TableCellText.styles.css @@ -15,6 +15,20 @@ */ .bui-TableCellText { + display: inline-flex; + flex-direction: row; + align-items: center; + gap: var(--bui-space-2); +} + +.bui-TableCellTextIcon, +.bui-TableCellTextIcon svg { + display: inline-flex; + align-items: center; + color: var(--bui-fg-primary); +} + +.bui-TableCellTextContent { display: flex; flex-direction: column; gap: var(--bui-space-0_5); diff --git a/packages/ui/src/components/Table/TableCellText/TableCellText.tsx b/packages/ui/src/components/Table/TableCellText/TableCellText.tsx index ee8db20ada..ddf4258c56 100644 --- a/packages/ui/src/components/Table/TableCellText/TableCellText.tsx +++ b/packages/ui/src/components/Table/TableCellText/TableCellText.tsx @@ -18,11 +18,23 @@ import { forwardRef } from 'react'; import clsx from 'clsx'; import { TableCellTextProps } from './types'; import { Text } from '../../Text/Text'; +import { Link } from '../../Link/Link'; import { useStyles } from '../../../hooks/useStyles'; /** @public */ const TableCellText = forwardRef( - ({ className, title, description, ...props }, ref) => { + ( + { + className, + title, + description, + color = 'primary', + leadingIcon, + href, + ...props + }, + ref, + ) => { const { classNames } = useStyles('Table'); return ( @@ -31,12 +43,25 @@ const TableCellText = forwardRef( className={clsx(classNames.cellText, className)} {...props} > - {title && {title}} - {description && ( - - {description} - + {leadingIcon && ( +
{leadingIcon}
)} +
+ {href ? ( + + {title} + + ) : ( + + {title} + + )} + {description && ( + + {description} + + )} +
); }, diff --git a/packages/ui/src/components/Table/TableCellText/types.ts b/packages/ui/src/components/Table/TableCellText/types.ts index 99a27ad248..f497f2ef0a 100644 --- a/packages/ui/src/components/Table/TableCellText/types.ts +++ b/packages/ui/src/components/Table/TableCellText/types.ts @@ -19,4 +19,7 @@ export interface TableCellTextProps extends React.HTMLAttributes { title: string; description?: string; + color?: 'primary' | 'secondary'; + leadingIcon?: React.ReactNode; + href?: string; } diff --git a/packages/ui/src/components/Table/index.ts b/packages/ui/src/components/Table/index.ts index 5c7f3da195..ecf1cd7b18 100644 --- a/packages/ui/src/components/Table/index.ts +++ b/packages/ui/src/components/Table/index.ts @@ -16,5 +16,4 @@ export * from './Table'; export * from './TableCellText/types'; -export * from './TableCellLink/types'; export * from './TableCellProfile/types'; diff --git a/packages/ui/src/css/components.css b/packages/ui/src/css/components.css index 268bbe40dd..68f834b36a 100644 --- a/packages/ui/src/css/components.css +++ b/packages/ui/src/css/components.css @@ -38,7 +38,6 @@ @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/Table/TableCellProfile/TableCellProfile.styles.css'; @import '../components/Tabs/Tabs.styles.css'; @import '../components/Text/styles.css'; diff --git a/packages/ui/src/utils/componentDefinitions.ts b/packages/ui/src/utils/componentDefinitions.ts index 30d1c93d23..5f48c8524e 100644 --- a/packages/ui/src/utils/componentDefinitions.ts +++ b/packages/ui/src/utils/componentDefinitions.ts @@ -239,7 +239,8 @@ export const componentDefinitions = { caption: 'bui-TableCaption', cell: 'bui-TableCell', cellText: 'bui-TableCellText', - cellLink: 'bui-TableCellLink', + cellTextContent: 'bui-TableCellTextContent', + cellTextIcon: 'bui-TableCellTextIcon', cellProfile: 'bui-TableCellProfile', cellProfileAvatar: 'bui-TableCellProfileAvatar', cellProfileAvatarImage: 'bui-TableCellProfileAvatarImage', From 660a378678c5a8f99f1f013da4a3da60ca751a3b Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 25 Jul 2025 07:52:31 +0100 Subject: [PATCH 02/14] Improve Table components Signed-off-by: Charles de Dreuille --- .../ui/src/components/DataTable/DataTable.tsx | 36 ++++++----- .../DataTable/Table/DataTableTable.tsx | 45 ++++++++------ .../components/DataTable/mocked-columns.tsx | 4 +- .../ui/src/components/Table/Table.stories.tsx | 59 ++++++++++--------- packages/ui/src/components/Table/Table.tsx | 41 +++++-------- packages/ui/src/components/Table/index.ts | 11 ++-- 6 files changed, 103 insertions(+), 93 deletions(-) diff --git a/packages/ui/src/components/DataTable/DataTable.tsx b/packages/ui/src/components/DataTable/DataTable.tsx index 5a587df56f..1f2270bd28 100644 --- a/packages/ui/src/components/DataTable/DataTable.tsx +++ b/packages/ui/src/components/DataTable/DataTable.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * 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. @@ -15,17 +15,26 @@ */ import { forwardRef } from 'react'; -import { Table } from '../Table'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableCellProfile, + TableCellText, + TableHeader, + TableRow, +} from '../Table'; import { DataTableRoot } from './Root/DataTableRoot'; import { DataTablePagination } from './Pagination/DataTablePagination'; import { Table as TanstackTable } from '@tanstack/react-table'; import { DataTableTable } from './Table/DataTableTable'; const TableRoot = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ); -TableRoot.displayName = Table.Root.displayName; + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ); +TableRoot.displayName = Table.displayName; /** * DataTable component for displaying tabular data with pagination @@ -40,12 +49,11 @@ export const DataTable = { Pagination: DataTablePagination, Table: DataTableTable, TableRoot: TableRoot, - TableHeader: Table.Header, - TableBody: Table.Body, - TableRow: Table.Row, - TableCell: Table.Cell, - TableCellText: Table.CellText, - TableCellLink: Table.CellLink, - TableCellProfile: Table.CellProfile, - TableHead: Table.Head, + TableHeader: TableHeader, + TableBody: TableBody, + TableRow: TableRow, + TableCell: TableCell, + TableCellText: TableCellText, + TableCellProfile: TableCellProfile, + TableHead: TableHead, }; diff --git a/packages/ui/src/components/DataTable/Table/DataTableTable.tsx b/packages/ui/src/components/DataTable/Table/DataTableTable.tsx index 954cf9f703..368d31e78b 100644 --- a/packages/ui/src/components/DataTable/Table/DataTableTable.tsx +++ b/packages/ui/src/components/DataTable/Table/DataTableTable.tsx @@ -17,7 +17,14 @@ import { forwardRef } from 'react'; import clsx from 'clsx'; import { DataTableTableProps } from './types'; -import { Table } from '../../Table'; +import { + Table, + TableRow, + TableHeader, + TableHead, + TableBody, + TableCell, +} from '../../Table'; import { useDataTable } from '../Root/DataTableRoot'; import { flexRender } from '@tanstack/react-table'; @@ -28,18 +35,18 @@ const DataTableTable = forwardRef( const { table } = useDataTable(); return ( - - + {table.getHeaderGroups().map(headerGroup => ( - + {headerGroup.headers.map(header => { return ( - @@ -49,42 +56,42 @@ const DataTableTable = forwardRef( 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/ui/src/components/DataTable/mocked-columns.tsx b/packages/ui/src/components/DataTable/mocked-columns.tsx index 4b06bbfaa1..600065bdf5 100644 --- a/packages/ui/src/components/DataTable/mocked-columns.tsx +++ b/packages/ui/src/components/DataTable/mocked-columns.tsx @@ -48,7 +48,7 @@ export const columns: ColumnDef[] = [ accessorKey: 'name', header: 'Name', cell: ({ row }) => ( - [] = [ accessorKey: 'name', header: 'Name', cell: ({ row }) => ( - + ), size: 450, }, diff --git a/packages/ui/src/components/Table/Table.stories.tsx b/packages/ui/src/components/Table/Table.stories.tsx index f008e73bae..c8279e5a83 100644 --- a/packages/ui/src/components/Table/Table.stories.tsx +++ b/packages/ui/src/components/Table/Table.stories.tsx @@ -16,7 +16,14 @@ import { ComponentType } from 'react'; import type { Meta, StoryObj } from '@storybook/react'; -import { Table } from '.'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '.'; const invoices = [ { @@ -65,13 +72,13 @@ const invoices = [ const meta = { title: 'Components/Table', - component: Table.Root, + component: Table, subcomponents: { - 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, + Body: TableBody as ComponentType, + Cell: TableCell as ComponentType, + Head: TableHead as ComponentType, + Header: TableHeader as ComponentType, + Row: TableRow as ComponentType, }, } satisfies Meta; @@ -80,27 +87,25 @@ type Story = StoryObj; export const Default: Story = { render: () => ( - - - - Invoice - Status - Method - Amount - - - + + + + Invoice + Status + Method + Amount + + + {invoices.map(invoice => ( - - {invoice.invoice} - {invoice.paymentStatus} - {invoice.paymentMethod} - - {invoice.totalAmount} - - + + {invoice.invoice} + {invoice.paymentStatus} + {invoice.paymentMethod} + {invoice.totalAmount} + ))} - - + +
), }; diff --git a/packages/ui/src/components/Table/Table.tsx b/packages/ui/src/components/Table/Table.tsx index 4c2597ba79..e82885d318 100644 --- a/packages/ui/src/components/Table/Table.tsx +++ b/packages/ui/src/components/Table/Table.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * 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. @@ -16,12 +16,10 @@ import { forwardRef } from 'react'; import clsx from 'clsx'; -import { TableCell } from './TableCell/TableCell'; -import { TableCellText } from './TableCellText/TableCellText'; -import { TableCellProfile } from './TableCellProfile/TableCellProfile'; import { useStyles } from '../../hooks/useStyles'; -const TableRoot = forwardRef< +/** @public */ +export const Table = forwardRef< HTMLTableElement, React.HTMLAttributes >(({ className, ...props }, ref) => { @@ -31,9 +29,10 @@ const TableRoot = forwardRef< ); }); -TableRoot.displayName = 'TableRoot'; +Table.displayName = 'Table'; -const TableHeader = forwardRef< +/** @public */ +export const TableHeader = forwardRef< HTMLTableSectionElement, React.HTMLAttributes >(({ className, ...props }, ref) => { @@ -49,7 +48,8 @@ const TableHeader = forwardRef< }); TableHeader.displayName = 'TableHeader'; -const TableBody = forwardRef< +/** @public */ +export const TableBody = forwardRef< HTMLTableSectionElement, React.HTMLAttributes >(({ className, ...props }, ref) => { @@ -61,7 +61,8 @@ const TableBody = forwardRef< }); TableBody.displayName = 'TableBody'; -const TableRow = forwardRef< +/** @public */ +export const TableRow = forwardRef< HTMLTableRowElement, React.HTMLAttributes >(({ className, ...props }, ref) => { @@ -75,7 +76,8 @@ const TableRow = forwardRef< }); TableRow.displayName = 'TableRow'; -const TableHead = forwardRef< +/** @public */ +export const TableHead = forwardRef< HTMLTableCellElement, React.ThHTMLAttributes >(({ className, ...props }, ref) => { @@ -87,7 +89,8 @@ const TableHead = forwardRef< }); TableHead.displayName = 'TableHead'; -const TableCaption = forwardRef< +/** @public */ +export const TableCaption = forwardRef< HTMLTableCaptionElement, React.HTMLAttributes >(({ className, ...props }, ref) => { @@ -102,19 +105,3 @@ const TableCaption = forwardRef< ); }); TableCaption.displayName = 'TableCaption'; - -/** - * Table component for displaying tabular data - * @public - */ -export const Table = { - Root: TableRoot, - Header: TableHeader, - Body: TableBody, - Head: TableHead, - Row: TableRow, - Cell: TableCell, - CellText: TableCellText, - CellProfile: TableCellProfile, - Caption: TableCaption, -}; diff --git a/packages/ui/src/components/Table/index.ts b/packages/ui/src/components/Table/index.ts index ecf1cd7b18..aa9bd105cd 100644 --- a/packages/ui/src/components/Table/index.ts +++ b/packages/ui/src/components/Table/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * 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. @@ -14,6 +14,9 @@ * limitations under the License. */ -export * from './Table'; -export * from './TableCellText/types'; -export * from './TableCellProfile/types'; +export { Table, TableBody, TableHead, TableHeader, TableRow } from './Table'; +export { TableCell } from './TableCell/TableCell'; +export { TableCellText } from './TableCellText/TableCellText'; +export { TableCellProfile } from './TableCellProfile/TableCellProfile'; +export type { TableCellTextProps } from './TableCellText/types'; +export type { TableCellProfileProps } from './TableCellProfile/types'; From 917d004aa6f1c0cc931fc65f669bd5ff65ea40b8 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 25 Jul 2025 19:20:35 +0100 Subject: [PATCH 03/14] First pass at bringing new DataTable to BUI Signed-off-by: Charles de Dreuille --- .../DataTable/DataTable.stories.tsx | 69 +++++---- .../components/DataTable/DataTable.styles.css | 37 +++++ .../ui/src/components/DataTable/DataTable.tsx | 133 +++++++++++++----- .../DataTable/DataTableHeadContent.tsx | 85 +++++++++++ .../Pagination/DataTablePagination.tsx | 96 ------------- .../DataTable/Root/DataTableRoot.styles.css | 5 - .../DataTable/Root/DataTableRoot.tsx | 58 -------- .../DataTable/Table/DataTableTable.tsx | 101 ------------- packages/ui/src/components/DataTable/index.ts | 8 +- .../components/DataTable/mocked-columns.tsx | 29 ++-- .../components/DataTable/{Root => }/types.ts | 7 +- .../DataTablePagination.stories.tsx | 13 +- .../DataTablePagination.styles.css | 0 .../DataTablePagination.tsx | 108 ++++++++++++++ .../types.ts => DataTablePagination/index.ts} | 5 +- .../components/DataTablePagination/types.ts | 34 +++++ .../src/components/Hidden/Hidden.stories.tsx | 32 +++++ .../src/components/Hidden/Hidden.styles.css | 28 ++++ packages/ui/src/components/Hidden/Hidden.tsx | 22 +++ .../Table/types.ts => Hidden/index.ts} | 4 +- .../components/Table/TableCell/TableCell.tsx | 2 +- packages/ui/src/css/components.css | 7 +- packages/ui/src/index.ts | 2 + 23 files changed, 512 insertions(+), 373 deletions(-) create mode 100644 packages/ui/src/components/DataTable/DataTable.styles.css create mode 100644 packages/ui/src/components/DataTable/DataTableHeadContent.tsx delete mode 100644 packages/ui/src/components/DataTable/Pagination/DataTablePagination.tsx delete mode 100644 packages/ui/src/components/DataTable/Root/DataTableRoot.styles.css delete mode 100644 packages/ui/src/components/DataTable/Root/DataTableRoot.tsx delete mode 100644 packages/ui/src/components/DataTable/Table/DataTableTable.tsx rename packages/ui/src/components/DataTable/{Root => }/types.ts (84%) rename packages/ui/src/components/{DataTable/Pagination => DataTablePagination}/DataTablePagination.stories.tsx (81%) rename packages/ui/src/components/{DataTable/Pagination => DataTablePagination}/DataTablePagination.styles.css (100%) create mode 100644 packages/ui/src/components/DataTablePagination/DataTablePagination.tsx rename packages/ui/src/components/{DataTable/Pagination/types.ts => DataTablePagination/index.ts} (83%) create mode 100644 packages/ui/src/components/DataTablePagination/types.ts create mode 100644 packages/ui/src/components/Hidden/Hidden.stories.tsx create mode 100644 packages/ui/src/components/Hidden/Hidden.styles.css create mode 100644 packages/ui/src/components/Hidden/Hidden.tsx rename packages/ui/src/components/{DataTable/Table/types.ts => Hidden/index.ts} (85%) diff --git a/packages/ui/src/components/DataTable/DataTable.stories.tsx b/packages/ui/src/components/DataTable/DataTable.stories.tsx index 29f38e7bc2..cca1fa1f35 100644 --- a/packages/ui/src/components/DataTable/DataTable.stories.tsx +++ b/packages/ui/src/components/DataTable/DataTable.stories.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * 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. @@ -16,6 +16,15 @@ import type { Meta, StoryFn, StoryObj } from '@storybook/react'; import { DataTable } from '.'; +import { DataTablePagination } from '../DataTablePagination'; +import { + TableHeader, + TableBody, + TableRow, + TableCell, + TableHead, + Table, +} from '../Table'; import { data, DataProps } from './mocked-components'; import { columns } from './mocked-columns'; import { @@ -52,10 +61,10 @@ export const Uncontrolled: Story = { }); return ( - - - - + <> + + + ); }, }; @@ -79,10 +88,10 @@ export const Controlled: Story = { }); return ( - - - - + <> + + + ); }, }; @@ -97,57 +106,57 @@ export const WithCustomTable: Story = { }); 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/ui/src/components/DataTable/DataTable.styles.css b/packages/ui/src/components/DataTable/DataTable.styles.css new file mode 100644 index 0000000000..78c463a883 --- /dev/null +++ b/packages/ui/src/components/DataTable/DataTable.styles.css @@ -0,0 +1,37 @@ +.bui-DataTableRoot { + display: flex; + flex-direction: column; + gap: var(--bui-space-3); +} + +.bui-DataTableRoot--sort { + cursor: pointer; + user-select: none; + display: inline-flex; + align-items: center; + gap: var(--bui-space-1); +} + +.bui-DataTableRoot--sort .bui-DataTableRoot--unsorted { + opacity: 0; + transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; +} + +.bui-DataTableRoot--sort:hover .bui-DataTableRoot--unsorted, +.bui-DataTableRoot--sort:focus .bui-DataTableRoot--unsorted { + opacity: 0.5; +} + +.bui-DataTableRoot--sort .bui-DataTableRoot--sorted-asc, +.bui-DataTableRoot--sort:hover .bui-DataTableRoot--sorted-asc, +.bui-DataTableRoot--sort:focus .bui-DataTableRoot--sorted-asc { + opacity: 1; + transform: rotate(0); +} + +.bui-DataTableRoot--sort .bui-DataTableRoot--sorted-desc, +.bui-DataTableRoot--sort:hover .bui-DataTableRoot--sorted-desc, +.bui-DataTableRoot--sort:focus .bui-DataTableRoot--sorted-desc { + opacity: 1; + transform: rotate(180deg); +} diff --git a/packages/ui/src/components/DataTable/DataTable.tsx b/packages/ui/src/components/DataTable/DataTable.tsx index 1f2270bd28..b092b9b94d 100644 --- a/packages/ui/src/components/DataTable/DataTable.tsx +++ b/packages/ui/src/components/DataTable/DataTable.tsx @@ -14,46 +14,107 @@ * limitations under the License. */ -import { forwardRef } from 'react'; +import clsx from 'clsx'; +import { DataTableProps } from './types'; import { Table, + TableRow, + TableHeader, + TableHead, TableBody, TableCell, - TableHead, - TableCellProfile, - TableCellText, - TableHeader, - TableRow, } from '../Table'; -import { DataTableRoot } from './Root/DataTableRoot'; -import { DataTablePagination } from './Pagination/DataTablePagination'; -import { Table as TanstackTable } from '@tanstack/react-table'; -import { DataTableTable } from './Table/DataTableTable'; +import { flexRender } from '@tanstack/react-table'; +import { DataTableHeadContent } from './DataTableHeadContent'; -const TableRoot = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ); -TableRoot.displayName = Table.displayName; +function getAriaSort(sortDirection: string | false) { + if (sortDirection === 'asc') { + return 'ascending'; + } + if (sortDirection === 'desc') { + return 'descending'; + } + return 'none'; +} -/** - * 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: DataTableTable, - TableRoot: TableRoot, - TableHeader: TableHeader, - TableBody: TableBody, - TableRow: TableRow, - TableCell: TableCell, - TableCellText: TableCellText, - TableCellProfile: TableCellProfile, - TableHead: TableHead, -}; +/** @public */ +function DataTable( + props: DataTableProps & { ref?: React.ForwardedRef }, +) { + const { className, table, ref, ...rest } = props; + + return ( +
+ + {table.getHeaderGroups().map(headerGroup => ( + + {headerGroup.headers.map(header => { + return ( + + {header.isPlaceholder ? null : ( + + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map(row => { + const rowData = row.original as TData & { onClick?: () => void }; + const handleRowClick = rowData.onClick + ? (e: React.MouseEvent) => { + if (!e.isPropagationStopped()) { + rowData.onClick!(); + } + } + : undefined; + + return ( + + {row.getVisibleCells().map(cell => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ); + }) + ) : ( + + + No results. + + + )} + +
+ ); +} + +DataTable.displayName = 'DataTable'; + +export { DataTable }; diff --git a/packages/ui/src/components/DataTable/DataTableHeadContent.tsx b/packages/ui/src/components/DataTable/DataTableHeadContent.tsx new file mode 100644 index 0000000000..8a8453715a --- /dev/null +++ b/packages/ui/src/components/DataTable/DataTableHeadContent.tsx @@ -0,0 +1,85 @@ +/* + * 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 { useCallback, useMemo } from 'react'; +import clsx from 'clsx'; +import { flexRender } from '@tanstack/react-table'; +import { Icon } from '../Icon'; +import { Header } from '@tanstack/react-table'; +import { Hidden } from '../Hidden'; + +function getSortTitle(nextOrder: string | false) { + if (nextOrder === 'asc') { + return 'Sort ascending'; + } + if (nextOrder === 'desc') { + return 'Sort descending'; + } + return 'Clear sort'; +} + +interface DataTableHeadContentProps { + header: Header; +} + +export function DataTableHeadContent({ + header, +}: DataTableHeadContentProps) { + const headerContent = useMemo( + () => flexRender(header.column.columnDef.header, header.getContext()), + [header], + ); + + const handleSort = useCallback( + (e: React.MouseEvent | React.KeyboardEvent) => { + if ('key' in e && e.key !== 'Enter' && e.key !== ' ') { + return; + } + e.preventDefault(); + header.column.getToggleSortingHandler()?.(e); + }, + [header], + ); + + if (!header.column.getCanSort()) { + return headerContent; + } + + return ( + + {headerContent} + , {getSortTitle(header.column.getNextSortingOrder())} + + + ); +} diff --git a/packages/ui/src/components/DataTable/Pagination/DataTablePagination.tsx b/packages/ui/src/components/DataTable/Pagination/DataTablePagination.tsx deleted file mode 100644 index eb319d948e..0000000000 --- a/packages/ui/src/components/DataTable/Pagination/DataTablePagination.tsx +++ /dev/null @@ -1,96 +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 { forwardRef } from 'react'; -import { Text } from '../../Text'; -import { DataTablePaginationProps } from './types'; -import { ButtonIcon } from '../../ButtonIcon'; -import clsx from 'clsx'; -import { Select } from '../../Select'; -import { useDataTable } from '../Root/DataTableRoot'; -import { Icon } from '../../Icon'; - -/** @public */ -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; - const rowCount = table?.getRowCount(); - const fromCount = (pageIndex ?? 0) * (pageSize ?? 10) + 1; - const toCount = Math.min( - ((pageIndex ?? 0) + 1) * (pageSize ?? 10), - rowCount, - ); - - return ( -
-
- {!table.options.manualPagination && ( - { + const newPageSize = Number(value); + table?.setPageSize(newPageSize); + onPageSizeChange?.(newPageSize); + }} + className="bui-DataTablePagination--select" + /> + )} +
+
+ {`${fromCount} - ${toCount} of ${rowCount}`} + { + table?.previousPage(); + onPreviousPage?.(); + }} + isDisabled={!table?.getCanPreviousPage()} + icon={} + aria-label="Previous" + /> + { + table?.nextPage(); + onNextPage?.(); + }} + isDisabled={!table?.getCanNextPage()} + icon={} + aria-label="Next" + /> +
+
+ ); +} + +DataTablePagination.displayName = 'DataTablePagination'; + +export { DataTablePagination }; diff --git a/packages/ui/src/components/DataTable/Pagination/types.ts b/packages/ui/src/components/DataTablePagination/index.ts similarity index 83% rename from packages/ui/src/components/DataTable/Pagination/types.ts rename to packages/ui/src/components/DataTablePagination/index.ts index b2a895c465..0ddc63a304 100644 --- a/packages/ui/src/components/DataTable/Pagination/types.ts +++ b/packages/ui/src/components/DataTablePagination/index.ts @@ -14,6 +14,5 @@ * limitations under the License. */ -/** @public */ -export interface DataTablePaginationProps - extends React.HTMLAttributes {} +export { DataTablePagination } from './DataTablePagination'; +export type { DataTablePaginationProps } from './types'; diff --git a/packages/ui/src/components/DataTablePagination/types.ts b/packages/ui/src/components/DataTablePagination/types.ts new file mode 100644 index 0000000000..ca73af1ec7 --- /dev/null +++ b/packages/ui/src/components/DataTablePagination/types.ts @@ -0,0 +1,34 @@ +/* + * 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 { Table } from '@tanstack/react-table'; + +/** @public */ +export interface DataTablePaginationProps + extends React.HTMLAttributes { + table?: Table; + onNextPage?: () => void; + onPreviousPage?: () => void; + onPageSizeChange?: (pageSize: number) => void; + showPageSizeOptions?: boolean; +} + +/** @public */ +export type DataTablePaginationComponent = ( + props: DataTablePaginationProps & { + ref?: React.ForwardedRef; + }, +) => React.ReactElement; diff --git a/packages/ui/src/components/Hidden/Hidden.stories.tsx b/packages/ui/src/components/Hidden/Hidden.stories.tsx new file mode 100644 index 0000000000..0957bf2e18 --- /dev/null +++ b/packages/ui/src/components/Hidden/Hidden.stories.tsx @@ -0,0 +1,32 @@ +/* + * 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 type { Meta, StoryObj } from '@storybook/react'; +import { Hidden } from './Hidden'; + +const meta = { + title: 'Components/Hidden', + component: Hidden, +} satisfies Meta