From e587b2213756145263e05ea8ed3f1bec7181cf18 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Wed, 11 Dec 2024 18:04:02 +0000 Subject: [PATCH] Improve table component styling Signed-off-by: Charles de Dreuille --- packages/canon/src/components/Table/Table.tsx | 30 +++-- .../Table/stories/Example1.stories.tsx | 111 ++++++++++++++++++ .../Table/stories/Table.stories.tsx | 2 +- .../Table/stories/TableBody.stories.tsx | 16 ++- .../Table/stories/TableCaption.stories.tsx | 2 +- .../Table/stories/TableCell.stories.tsx | 2 +- .../Table/stories/TableFooter.stories.tsx | 2 +- .../Table/stories/TableHead.stories.tsx | 2 +- .../Table/stories/TableHeader.stories.tsx | 16 ++- .../Table/stories/TableRow.stories.tsx | 18 ++- .../canon/src/components/Table/styles.css | 54 +++++++++ packages/canon/src/css/backstage.css | 52 ++++---- 12 files changed, 264 insertions(+), 43 deletions(-) create mode 100644 packages/canon/src/components/Table/stories/Example1.stories.tsx diff --git a/packages/canon/src/components/Table/Table.tsx b/packages/canon/src/components/Table/Table.tsx index 887b2a6216..bb40d9c92f 100644 --- a/packages/canon/src/components/Table/Table.tsx +++ b/packages/canon/src/components/Table/Table.tsx @@ -20,7 +20,7 @@ const Table = React.forwardRef< React.HTMLAttributes >(({ className, ...props }, ref) => (
- +
)); Table.displayName = 'Table'; @@ -29,7 +29,11 @@ const TableHeader = React.forwardRef< HTMLTableSectionElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( - + )); TableHeader.displayName = 'TableHeader'; @@ -37,7 +41,7 @@ const TableBody = React.forwardRef< HTMLTableSectionElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( - + )); TableBody.displayName = 'TableBody'; @@ -45,7 +49,11 @@ const TableFooter = React.forwardRef< HTMLTableSectionElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( - + )); TableFooter.displayName = 'TableFooter'; @@ -53,7 +61,9 @@ const TableRow = React.forwardRef< HTMLTableRowElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( - + + {props.children} + )); TableRow.displayName = 'TableRow'; @@ -61,7 +71,7 @@ const TableHead = React.forwardRef< HTMLTableCellElement, React.ThHTMLAttributes >(({ className, ...props }, ref) => ( -
+ )); TableHead.displayName = 'TableHead'; @@ -69,7 +79,7 @@ const TableCell = React.forwardRef< HTMLTableCellElement, React.TdHTMLAttributes >(({ className, ...props }, ref) => ( - + )); TableCell.displayName = 'TableCell'; @@ -77,7 +87,11 @@ const TableCaption = React.forwardRef< HTMLTableCaptionElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( -
+ )); TableCaption.displayName = 'TableCaption'; diff --git a/packages/canon/src/components/Table/stories/Example1.stories.tsx b/packages/canon/src/components/Table/stories/Example1.stories.tsx new file mode 100644 index 0000000000..140293438e --- /dev/null +++ b/packages/canon/src/components/Table/stories/Example1.stories.tsx @@ -0,0 +1,111 @@ +/* + * 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 type { Meta, StoryObj } from '@storybook/react'; +import { + Table, + TableHead, + TableRow, + TableHeader, + TableCell, + TableBody, + TableFooter, +} from '../Table'; + +const meta = { + title: 'Components/Table/Examples/Simple', + component: Table, +} satisfies Meta; + +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', + }, +]; + +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 + + +
+ ), +}; diff --git a/packages/canon/src/components/Table/stories/Table.stories.tsx b/packages/canon/src/components/Table/stories/Table.stories.tsx index 0772d3aa91..2267b77093 100644 --- a/packages/canon/src/components/Table/stories/Table.stories.tsx +++ b/packages/canon/src/components/Table/stories/Table.stories.tsx @@ -18,7 +18,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { Table } from '../Table'; const meta = { - title: 'Components/Table/Table', + title: 'Components/Table/Components/Table', component: Table, } satisfies Meta; diff --git a/packages/canon/src/components/Table/stories/TableBody.stories.tsx b/packages/canon/src/components/Table/stories/TableBody.stories.tsx index ef08f47913..f69d26f0bd 100644 --- a/packages/canon/src/components/Table/stories/TableBody.stories.tsx +++ b/packages/canon/src/components/Table/stories/TableBody.stories.tsx @@ -14,17 +14,27 @@ * limitations under the License. */ +import * as React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; -import { TableBody } from '../Table'; +import { Table, TableBody } from '../Table'; const meta = { - title: 'Components/Table/TableBody', + title: 'Components/Table/Components/TableBody', component: TableBody, + decorators: [ + Story => ( + + +
+ ), + ], } satisfies Meta; export default meta; type Story = StoryObj; export const Default: Story = { - args: {}, + args: { + children: "Hello World! I'm a basic table body", + }, }; diff --git a/packages/canon/src/components/Table/stories/TableCaption.stories.tsx b/packages/canon/src/components/Table/stories/TableCaption.stories.tsx index 8b86e33d8f..46ea5f0f4d 100644 --- a/packages/canon/src/components/Table/stories/TableCaption.stories.tsx +++ b/packages/canon/src/components/Table/stories/TableCaption.stories.tsx @@ -18,7 +18,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { TableCaption } from '../Table'; const meta = { - title: 'Components/Table/TableCaption', + title: 'Components/Table/Components/TableCaption', component: TableCaption, } satisfies Meta; diff --git a/packages/canon/src/components/Table/stories/TableCell.stories.tsx b/packages/canon/src/components/Table/stories/TableCell.stories.tsx index 6ef4c9b539..126b72bf95 100644 --- a/packages/canon/src/components/Table/stories/TableCell.stories.tsx +++ b/packages/canon/src/components/Table/stories/TableCell.stories.tsx @@ -18,7 +18,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { TableCell } from '../Table'; const meta = { - title: 'Components/Table/TableCell', + title: 'Components/Table/Components/TableCell', component: TableCell, } satisfies Meta; diff --git a/packages/canon/src/components/Table/stories/TableFooter.stories.tsx b/packages/canon/src/components/Table/stories/TableFooter.stories.tsx index 5d220194c9..b7dbacbc30 100644 --- a/packages/canon/src/components/Table/stories/TableFooter.stories.tsx +++ b/packages/canon/src/components/Table/stories/TableFooter.stories.tsx @@ -18,7 +18,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { TableFooter } from '../Table'; const meta = { - title: 'Components/Table/TableFooter', + title: 'Components/Table/Components/TableFooter', component: TableFooter, } satisfies Meta; diff --git a/packages/canon/src/components/Table/stories/TableHead.stories.tsx b/packages/canon/src/components/Table/stories/TableHead.stories.tsx index af20d5b9fc..c7cfac51e8 100644 --- a/packages/canon/src/components/Table/stories/TableHead.stories.tsx +++ b/packages/canon/src/components/Table/stories/TableHead.stories.tsx @@ -18,7 +18,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { TableHead } from '../Table'; const meta = { - title: 'Components/Table/TableHead', + title: 'Components/Table/Components/TableHead', component: TableHead, } satisfies Meta; diff --git a/packages/canon/src/components/Table/stories/TableHeader.stories.tsx b/packages/canon/src/components/Table/stories/TableHeader.stories.tsx index faaab22ac9..a2f044dcc3 100644 --- a/packages/canon/src/components/Table/stories/TableHeader.stories.tsx +++ b/packages/canon/src/components/Table/stories/TableHeader.stories.tsx @@ -14,17 +14,27 @@ * limitations under the License. */ +import * as React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; -import { TableHeader } from '../Table'; +import { Table, TableHeader } from '../Table'; const meta = { - title: 'Components/Table/TableHeader', + title: 'Components/Table/Components/TableHeader', component: TableHeader, + decorators: [ + Story => ( + + +
+ ), + ], } satisfies Meta; export default meta; type Story = StoryObj; export const Default: Story = { - args: {}, + args: { + children: "Hello World! I'm a basic table header", + }, }; diff --git a/packages/canon/src/components/Table/stories/TableRow.stories.tsx b/packages/canon/src/components/Table/stories/TableRow.stories.tsx index f048df6b53..7eb58ed5a4 100644 --- a/packages/canon/src/components/Table/stories/TableRow.stories.tsx +++ b/packages/canon/src/components/Table/stories/TableRow.stories.tsx @@ -14,17 +14,29 @@ * limitations under the License. */ +import * as React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; -import { TableRow } from '../Table'; +import { Table, TableRow, TableHeader } from '../Table'; const meta = { - title: 'Components/Table/TableRow', + title: 'Components/Table/Components/TableRow', component: TableRow, + decorators: [ + Story => ( + + + + +
+ ), + ], } satisfies Meta; export default meta; type Story = StoryObj; export const Default: Story = { - args: {}, + args: { + children: "Hello World! I'm a basic table row", + }, }; diff --git a/packages/canon/src/components/Table/styles.css b/packages/canon/src/components/Table/styles.css index f57280f642..bf3d8603e3 100644 --- a/packages/canon/src/components/Table/styles.css +++ b/packages/canon/src/components/Table/styles.css @@ -2,10 +2,64 @@ position: relative; overflow: auto; width: 100%; + background-color: var(--canon-surface-1); + border-radius: var(--canon-border-radius-xs); + padding-bottom: var(--canon-spacing-5xs); + padding-top: var(--canon-spacing-5xs); + font-size: var(--canon-font-size-body); table { width: 100%; caption-side: bottom; font-size: var(--canon-font-size-sm); + border-collapse: collapse; } } + +.table-header { + tr { + /* border-bottom: 1px solid var(--canon-outline); */ + } +} + +.table-head { + text-align: left; + padding: var(--canon-spacing-xs); +} + +.table-body { + tr:last-child { + border-bottom: none; + } +} + +.table-row { + transition: color 0.2s ease-in-out; + + &:hover td { + background-color: var(--canon-surface-3); + } + + & .table-cell:first-child { + border-top-left-radius: var(--canon-border-radius-xs); + border-bottom-left-radius: var(--canon-border-radius-xs); + box-shadow: inset 4px 2px 0 0 var(--canon-surface-1), + inset 4px -2px 0 0 var(--canon-surface-1); + padding-left: var(--canon-spacing-xs); + } + + & .table-cell:last-child { + border-top-right-radius: var(--canon-border-radius-xs); + border-bottom-right-radius: var(--canon-border-radius-xs); + box-shadow: inset -4px 2px 0 0 var(--canon-surface-1), + inset -4px -2px 0 0 var(--canon-surface-1); + padding-right: var(--canon-spacing-xs); + } +} + +.table-cell { + padding: var(--canon-spacing-xs); + background-color: var(--canon-surface-2); + box-shadow: inset 0px 2px 0 0 var(--canon-surface-1), + inset 0px -2px 0 0 var(--canon-surface-1); +} diff --git a/packages/canon/src/css/backstage.css b/packages/canon/src/css/backstage.css index 40f143baac..178a714ee3 100644 --- a/packages/canon/src/css/backstage.css +++ b/packages/canon/src/css/backstage.css @@ -34,6 +34,7 @@ body { --canon-background: #f8f8f8; --canon-surface-1: #fff; --canon-surface-2: #f4f4f4; + --canon-surface-3: #f1f1f1; /* Outlines */ --canon-outline: rgba(0, 0, 0, 0.1); @@ -64,29 +65,38 @@ body { --canon-font-bold: 600; /* Font sizes */ - --canon-font-size-2xs: 0.625rem; - --canon-font-size-xs: 0.75rem; - --canon-font-size-sm: 0.875rem; - --canon-font-size-md: 1rem; - --canon-font-size-lg: 1.125rem; - --canon-font-size-xl: 1.25rem; - --canon-font-size-2xl: 1.5rem; - --canon-font-size-3xl: 1.875rem; - --canon-font-size-4xl: 2.25rem; - --canon-font-size-5xl: 3rem; - --canon-font-size-6xl: 3.75rem; - --canon-font-size-7xl: 4.5rem; - --canon-font-size-8xl: 6rem; - --canon-font-size-9xl: 8rem; + --canon-font-size-label: 0.625rem; /* 10px */ + --canon-font-size-caption: 0.75rem; /* 12px */ + --canon-font-size-body: 0.875rem; /* 14px */ + --canon-font-size-subtitle: 1rem; /* 16px */ + --canon-font-size-title1: 1.25rem; /* 20px */ + --canon-font-size-title2: 1.5rem; /* 24px */ + --canon-font-size-title3: 2rem; /* 32px */ + --canon-font-size-title4: 3rem; /* 48px */ + --canon-font-size-title5: 4rem; /* 64px */ + --canon-font-size-display: 5.75rem; /* 48px */ /* Spacing */ - --canon-spacing-2xs: 0.25rem; - --canon-spacing-xs: 0.5rem; - --canon-spacing-sm: 0.75rem; - --canon-spacing-md: 1.25rem; - --canon-spacing-lg: 2rem; - --canon-spacing-xl: 3.25rem; - --canon-spacing-2xl: 5.25rem; + --canon-spacing-5xs: 0.125rem; /* 2px */ + --canon-spacing-4xs: 0.25rem; /* 4px */ + --canon-spacing-3xs: 0.375rem; /* 6px */ + --canon-spacing-2xs: 0.5rem; /* 8px */ + --canon-spacing-xs: 0.75rem; /* 12px */ + --canon-spacing-sm: 1rem; /* 16px */ + --canon-spacing-md: 1.5rem; /* 24px */ + --canon-spacing-lg: 2rem; /* 32px */ + --canon-spacing-xl: 2.5rem; /* 40px */ + --canon-spacing-2xl: 3rem; /* 48px */ + --canon-spacing-3xl: 3.5rem; /* 56px */ + + /* Border radius */ + --canon-border-radius-2xs: 0.125rem; /* 2px */ + --canon-border-radius-xs: 0.25rem; /* 4px */ + --canon-border-radius-sm: 0.5rem; /* 8px */ + --canon-border-radius-md: 0.75rem; /* 12px */ + --canon-border-radius-lg: 1rem; /* 16px */ + --canon-border-radius-xl: 1.25rem; /* 20px */ + --canon-border-radius-2xl: 1.5rem; /* 24px */ /* Container */ --canon-container-max-width: 1200px;