From 1ccd1d920662a08056ba765af2d13dd1187fc211 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Tue, 10 Dec 2024 16:31:52 +0000 Subject: [PATCH 1/3] Improve styling + add Table components Signed-off-by: Charles de Dreuille --- packages/canon/.storybook/preview.tsx | 9 +- .../canon/src/components/Button/styles.css | 2 +- .../canon/src/components/Checkbox/styles.css | 2 +- packages/canon/src/components/Table/Docs.mdx | 5 + packages/canon/src/components/Table/Table.tsx | 93 ++++++++ .../Table/stories/Table.stories.tsx | 32 +++ .../Table/stories/TableBody.stories.tsx | 30 +++ .../Table/stories/TableCaption.stories.tsx | 30 +++ .../Table/stories/TableCell.stories.tsx | 30 +++ .../Table/stories/TableFooter.stories.tsx | 30 +++ .../Table/stories/TableHead.stories.tsx | 30 +++ .../Table/stories/TableHeader.stories.tsx | 30 +++ .../Table/stories/TableRow.stories.tsx | 30 +++ .../canon/src/components/Table/styles.css | 11 + .../{theme/theme.css => css/backstage.css} | 45 ++-- .../{theme/styles.css => css/components.css} | 8 +- packages/canon/src/css/normalize.css | 201 ++++++++++++++++++ 17 files changed, 592 insertions(+), 26 deletions(-) create mode 100644 packages/canon/src/components/Table/Docs.mdx create mode 100644 packages/canon/src/components/Table/Table.tsx create mode 100644 packages/canon/src/components/Table/stories/Table.stories.tsx create mode 100644 packages/canon/src/components/Table/stories/TableBody.stories.tsx create mode 100644 packages/canon/src/components/Table/stories/TableCaption.stories.tsx create mode 100644 packages/canon/src/components/Table/stories/TableCell.stories.tsx create mode 100644 packages/canon/src/components/Table/stories/TableFooter.stories.tsx create mode 100644 packages/canon/src/components/Table/stories/TableHead.stories.tsx create mode 100644 packages/canon/src/components/Table/stories/TableHeader.stories.tsx create mode 100644 packages/canon/src/components/Table/stories/TableRow.stories.tsx create mode 100644 packages/canon/src/components/Table/styles.css rename packages/canon/src/{theme/theme.css => css/backstage.css} (73%) rename packages/canon/src/{theme/styles.css => css/components.css} (77%) create mode 100644 packages/canon/src/css/normalize.css diff --git a/packages/canon/.storybook/preview.tsx b/packages/canon/.storybook/preview.tsx index 8beeb82720..906434e4f7 100644 --- a/packages/canon/.storybook/preview.tsx +++ b/packages/canon/.storybook/preview.tsx @@ -6,7 +6,9 @@ import { withThemeByDataAttribute } from '@storybook/addon-themes'; import '../docs/components/styles.css'; // Canon specific styles -import '../src/theme/styles.css'; + +import '../src/css/backstage.css'; +import '../src/css/components.css'; // Custom themes import './themes/backstage.css'; @@ -22,6 +24,11 @@ const preview: Preview = { backgrounds: { disable: true, }, + options: { + storySort: { + method: 'alphabetical', + }, + }, }, decorators: [ withThemeByDataAttribute({ diff --git a/packages/canon/src/components/Button/styles.css b/packages/canon/src/components/Button/styles.css index aaf1cae8bf..355a759c80 100644 --- a/packages/canon/src/components/Button/styles.css +++ b/packages/canon/src/components/Button/styles.css @@ -22,7 +22,7 @@ user-select: none; font-family: var(--canon-font-regular); font-weight: var(--canon-font-bold); - font-size: var(--canon-font-sizes-md); + font-size: var(--canon-font-size-md); padding: 0; transition: all 150ms ease; cursor: pointer; diff --git a/packages/canon/src/components/Checkbox/styles.css b/packages/canon/src/components/Checkbox/styles.css index fb7e0d0ab7..4c382863c2 100644 --- a/packages/canon/src/components/Checkbox/styles.css +++ b/packages/canon/src/components/Checkbox/styles.css @@ -4,7 +4,7 @@ align-items: center; justify-content: center; gap: var(--canon-spacing-xs); - font-size: var(--canon-font-sizes-xs); + font-size: var(--canon-font-size-xs); font-family: var(--canon-font-regular); color: var(--canon-text-primary); diff --git a/packages/canon/src/components/Table/Docs.mdx b/packages/canon/src/components/Table/Docs.mdx new file mode 100644 index 0000000000..6a6e9c8a59 --- /dev/null +++ b/packages/canon/src/components/Table/Docs.mdx @@ -0,0 +1,5 @@ +import { Meta, Controls } from '@storybook/blocks'; + + + +# Table diff --git a/packages/canon/src/components/Table/Table.tsx b/packages/canon/src/components/Table/Table.tsx new file mode 100644 index 0000000000..887b2a6216 --- /dev/null +++ b/packages/canon/src/components/Table/Table.tsx @@ -0,0 +1,93 @@ +/* + * 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'; + +const Table = React.forwardRef< + HTMLTableElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+ + +)); +Table.displayName = 'Table'; + +const TableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableHeader.displayName = 'TableHeader'; + +const TableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableBody.displayName = 'TableBody'; + +const TableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableFooter.displayName = 'TableFooter'; + +const TableRow = React.forwardRef< + HTMLTableRowElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableRow.displayName = 'TableRow'; + +const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +TableHead.displayName = 'TableHead'; + +const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes +>(({ className, ...props }, ref) => ( + +)); +TableCell.displayName = 'TableCell'; + +const TableCaption = React.forwardRef< + HTMLTableCaptionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +TableCaption.displayName = 'TableCaption'; + +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +}; diff --git a/packages/canon/src/components/Table/stories/Table.stories.tsx b/packages/canon/src/components/Table/stories/Table.stories.tsx new file mode 100644 index 0000000000..0772d3aa91 --- /dev/null +++ b/packages/canon/src/components/Table/stories/Table.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 { Table } from '../Table'; + +const meta = { + title: 'Components/Table/Table', + component: Table, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: "Hello World! I'm a basic table", + }, +}; diff --git a/packages/canon/src/components/Table/stories/TableBody.stories.tsx b/packages/canon/src/components/Table/stories/TableBody.stories.tsx new file mode 100644 index 0000000000..ef08f47913 --- /dev/null +++ b/packages/canon/src/components/Table/stories/TableBody.stories.tsx @@ -0,0 +1,30 @@ +/* + * 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 { TableBody } from '../Table'; + +const meta = { + title: 'Components/Table/TableBody', + component: TableBody, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; diff --git a/packages/canon/src/components/Table/stories/TableCaption.stories.tsx b/packages/canon/src/components/Table/stories/TableCaption.stories.tsx new file mode 100644 index 0000000000..8b86e33d8f --- /dev/null +++ b/packages/canon/src/components/Table/stories/TableCaption.stories.tsx @@ -0,0 +1,30 @@ +/* + * 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 { TableCaption } from '../Table'; + +const meta = { + title: 'Components/Table/TableCaption', + component: TableCaption, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; diff --git a/packages/canon/src/components/Table/stories/TableCell.stories.tsx b/packages/canon/src/components/Table/stories/TableCell.stories.tsx new file mode 100644 index 0000000000..6ef4c9b539 --- /dev/null +++ b/packages/canon/src/components/Table/stories/TableCell.stories.tsx @@ -0,0 +1,30 @@ +/* + * 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 '../Table'; + +const meta = { + title: 'Components/Table/TableCell', + component: TableCell, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; diff --git a/packages/canon/src/components/Table/stories/TableFooter.stories.tsx b/packages/canon/src/components/Table/stories/TableFooter.stories.tsx new file mode 100644 index 0000000000..5d220194c9 --- /dev/null +++ b/packages/canon/src/components/Table/stories/TableFooter.stories.tsx @@ -0,0 +1,30 @@ +/* + * 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 { TableFooter } from '../Table'; + +const meta = { + title: 'Components/Table/TableFooter', + component: TableFooter, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; diff --git a/packages/canon/src/components/Table/stories/TableHead.stories.tsx b/packages/canon/src/components/Table/stories/TableHead.stories.tsx new file mode 100644 index 0000000000..af20d5b9fc --- /dev/null +++ b/packages/canon/src/components/Table/stories/TableHead.stories.tsx @@ -0,0 +1,30 @@ +/* + * 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 { TableHead } from '../Table'; + +const meta = { + title: 'Components/Table/TableHead', + component: TableHead, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; diff --git a/packages/canon/src/components/Table/stories/TableHeader.stories.tsx b/packages/canon/src/components/Table/stories/TableHeader.stories.tsx new file mode 100644 index 0000000000..faaab22ac9 --- /dev/null +++ b/packages/canon/src/components/Table/stories/TableHeader.stories.tsx @@ -0,0 +1,30 @@ +/* + * 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 { TableHeader } from '../Table'; + +const meta = { + title: 'Components/Table/TableHeader', + component: TableHeader, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; diff --git a/packages/canon/src/components/Table/stories/TableRow.stories.tsx b/packages/canon/src/components/Table/stories/TableRow.stories.tsx new file mode 100644 index 0000000000..f048df6b53 --- /dev/null +++ b/packages/canon/src/components/Table/stories/TableRow.stories.tsx @@ -0,0 +1,30 @@ +/* + * 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 { TableRow } from '../Table'; + +const meta = { + title: 'Components/Table/TableRow', + component: TableRow, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; diff --git a/packages/canon/src/components/Table/styles.css b/packages/canon/src/components/Table/styles.css new file mode 100644 index 0000000000..f57280f642 --- /dev/null +++ b/packages/canon/src/components/Table/styles.css @@ -0,0 +1,11 @@ +.table { + position: relative; + overflow: auto; + width: 100%; + + table { + width: 100%; + caption-side: bottom; + font-size: var(--canon-font-size-sm); + } +} diff --git a/packages/canon/src/theme/theme.css b/packages/canon/src/css/backstage.css similarity index 73% rename from packages/canon/src/theme/theme.css rename to packages/canon/src/css/backstage.css index ef78b2221a..40f143baac 100644 --- a/packages/canon/src/theme/theme.css +++ b/packages/canon/src/css/backstage.css @@ -14,7 +14,20 @@ * limitations under the License. */ -/* Light theme */ +/* Normalize */ +@import './normalize.css'; + +/* Geist font */ +@import url('https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap'); + +*, +html, +body { + font-family: var(--canon-font-regular); +} + +/* Light theme tokens */ :root { /* Colors */ --canon-accent: #000; @@ -51,20 +64,20 @@ --canon-font-bold: 600; /* Font sizes */ - --canon-font-sizes-2xs: 0.625rem; - --canon-font-sizes-xs: 0.75rem; - --canon-font-sizes-sm: 0.875rem; - --canon-font-sizes-md: 1rem; - --canon-font-sizes-lg: 1.125rem; - --canon-font-sizes-xl: 1.25rem; - --canon-font-sizes-2xl: 1.5rem; - --canon-font-sizes-3xl: 1.875rem; - --canon-font-sizes-4xl: 2.25rem; - --canon-font-sizes-5xl: 3rem; - --canon-font-sizes-6xl: 3.75rem; - --canon-font-sizes-7xl: 4.5rem; - --canon-font-sizes-8xl: 6rem; - --canon-font-sizes-9xl: 8rem; + --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; /* Spacing */ --canon-spacing-2xs: 0.25rem; @@ -80,7 +93,7 @@ --canon-container-padding: 1rem; } -/* Dark theme */ +/* Dark theme tokens */ [data-theme='dark'] { /* Colors */ --canon-accent: #fff; diff --git a/packages/canon/src/theme/styles.css b/packages/canon/src/css/components.css similarity index 77% rename from packages/canon/src/theme/styles.css rename to packages/canon/src/css/components.css index 8cb74374c6..50978a6710 100644 --- a/packages/canon/src/theme/styles.css +++ b/packages/canon/src/css/components.css @@ -14,13 +14,6 @@ * limitations under the License. */ -/* Geist font */ -@import url('https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap'); -@import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap'); - -/* Theme */ -@import './theme.css'; - /* Components */ @import '../components/Button/styles.css'; @import '../components/Stack/styles.css'; @@ -29,3 +22,4 @@ @import '../components/Container/styles.css'; @import '../components/Icon/styles.css'; @import '../components/Checkbox/styles.css'; +@import '../components/Table/styles.css'; diff --git a/packages/canon/src/css/normalize.css b/packages/canon/src/css/normalize.css new file mode 100644 index 0000000000..80e1ca4119 --- /dev/null +++ b/packages/canon/src/css/normalize.css @@ -0,0 +1,201 @@ +/*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */ + +/* +Document +======== +*/ + +/** +Use a better box model (opinionated). +*/ + +*, +::before, +::after { + box-sizing: border-box; +} + +html { + /* Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) */ + font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, + 'Apple Color Emoji', 'Segoe UI Emoji'; + line-height: 1.15; /* 1. Correct the line height in all browsers. */ + -webkit-text-size-adjust: 100%; /* 2. Prevent adjustments of font size after orientation changes in iOS. */ + tab-size: 4; /* 3. Use a more readable tab size (opinionated). */ +} + +/* +Sections +======== +*/ + +body { + margin: 0; /* Remove the margin in all browsers. */ +} + +/* +Text-level semantics +==================== +*/ + +/** +Add the correct font weight in Chrome and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/** +1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) +2. Correct the odd 'em' font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', Menlo, + monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/** +Prevent 'sub' and 'sup' elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +Tabular data +============ +*/ + +/** +Correct table border color inheritance in Chrome and Safari. (https://issues.chromium.org/issues/40615503, https://bugs.webkit.org/show_bug.cgi?id=195016) +*/ + +table { + border-color: currentcolor; +} + +/* +Forms +===== +*/ + +/** +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** +Correct the inability to style clickable types in iOS and Safari. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; +} + +/** +Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers. +*/ + +legend { + padding: 0; +} + +/** +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/** +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/** +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to 'inherit' in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* +Interactive +=========== +*/ + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} From e587b2213756145263e05ea8ed3f1bec7181cf18 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Wed, 11 Dec 2024 18:04:02 +0000 Subject: [PATCH 2/3] 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; From 5bbd9fd8ecf126b81bd13cd35d6e911b7344322d Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 13 Dec 2024 17:20:57 +0000 Subject: [PATCH 3/3] Improve Table API Signed-off-by: Charles de Dreuille --- packages/canon/.storybook/preview.tsx | 3 +- packages/canon/report.api.md | 43 +++++++++++++++++++ packages/canon/src/components/Table/Table.tsx | 8 ++++ packages/canon/src/components/Table/index.ts | 24 +++++++++++ .../canon/src/css/{backstage.css => core.css} | 16 +++---- packages/canon/src/index.ts | 1 + 6 files changed, 83 insertions(+), 12 deletions(-) create mode 100644 packages/canon/src/components/Table/index.ts rename packages/canon/src/css/{backstage.css => core.css} (92%) diff --git a/packages/canon/.storybook/preview.tsx b/packages/canon/.storybook/preview.tsx index 906434e4f7..e78a9bb923 100644 --- a/packages/canon/.storybook/preview.tsx +++ b/packages/canon/.storybook/preview.tsx @@ -6,8 +6,7 @@ import { withThemeByDataAttribute } from '@storybook/addon-themes'; import '../docs/components/styles.css'; // Canon specific styles - -import '../src/css/backstage.css'; +import '../src/css/core.css'; import '../src/css/components.css'; // Custom themes diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index b314020d81..cba0983972 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -7,6 +7,7 @@ import { ForwardRefExoticComponent } from 'react'; import { default as React_2 } from 'react'; +import * as React_3 from 'react'; import { RefAttributes } from 'react'; // @public (undocumented) @@ -457,6 +458,48 @@ export interface StackProps extends SpaceProps, ColorProps { style?: React.CSSProperties; } +// @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 (undocumented) export type Theme = keyof typeof themes; diff --git a/packages/canon/src/components/Table/Table.tsx b/packages/canon/src/components/Table/Table.tsx index bb40d9c92f..efd5181036 100644 --- a/packages/canon/src/components/Table/Table.tsx +++ b/packages/canon/src/components/Table/Table.tsx @@ -15,6 +15,7 @@ */ import * as React from 'react'; +/** @public */ const Table = React.forwardRef< HTMLTableElement, React.HTMLAttributes @@ -25,6 +26,7 @@ const Table = React.forwardRef< )); Table.displayName = 'Table'; +/** @public */ const TableHeader = React.forwardRef< HTMLTableSectionElement, React.HTMLAttributes @@ -37,6 +39,7 @@ const TableHeader = React.forwardRef< )); TableHeader.displayName = 'TableHeader'; +/** @public */ const TableBody = React.forwardRef< HTMLTableSectionElement, React.HTMLAttributes @@ -45,6 +48,7 @@ const TableBody = React.forwardRef< )); TableBody.displayName = 'TableBody'; +/** @public */ const TableFooter = React.forwardRef< HTMLTableSectionElement, React.HTMLAttributes @@ -57,6 +61,7 @@ const TableFooter = React.forwardRef< )); TableFooter.displayName = 'TableFooter'; +/** @public */ const TableRow = React.forwardRef< HTMLTableRowElement, React.HTMLAttributes @@ -67,6 +72,7 @@ const TableRow = React.forwardRef< )); TableRow.displayName = 'TableRow'; +/** @public */ const TableHead = React.forwardRef< HTMLTableCellElement, React.ThHTMLAttributes @@ -75,6 +81,7 @@ const TableHead = React.forwardRef< )); TableHead.displayName = 'TableHead'; +/** @public */ const TableCell = React.forwardRef< HTMLTableCellElement, React.TdHTMLAttributes @@ -83,6 +90,7 @@ const TableCell = React.forwardRef< )); TableCell.displayName = 'TableCell'; +/** @public */ const TableCaption = React.forwardRef< HTMLTableCaptionElement, React.HTMLAttributes diff --git a/packages/canon/src/components/Table/index.ts b/packages/canon/src/components/Table/index.ts new file mode 100644 index 0000000000..cf88de2b1a --- /dev/null +++ b/packages/canon/src/components/Table/index.ts @@ -0,0 +1,24 @@ +/* + * 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 { + Table, + TableHead, + TableRow, + TableHeader, + TableCell, + TableBody, + TableFooter, +} from './Table'; diff --git a/packages/canon/src/css/backstage.css b/packages/canon/src/css/core.css similarity index 92% rename from packages/canon/src/css/backstage.css rename to packages/canon/src/css/core.css index 178a714ee3..95e4b6ce5b 100644 --- a/packages/canon/src/css/backstage.css +++ b/packages/canon/src/css/core.css @@ -17,16 +17,6 @@ /* Normalize */ @import './normalize.css'; -/* Geist font */ -@import url('https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap'); -@import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap'); - -*, -html, -body { - font-family: var(--canon-font-regular); -} - /* Light theme tokens */ :root { /* Colors */ @@ -101,6 +91,12 @@ body { /* Container */ --canon-container-max-width: 1200px; --canon-container-padding: 1rem; + + *, + html, + body { + font-family: var(--canon-font-regular); + } } /* Dark theme tokens */ diff --git a/packages/canon/src/index.ts b/packages/canon/src/index.ts index 5d0ebb5a2e..d6a375b8dd 100644 --- a/packages/canon/src/index.ts +++ b/packages/canon/src/index.ts @@ -34,3 +34,4 @@ export * from './components/Container'; export * from './components/Button'; export * from './components/Icon'; export * from './components/Checkbox'; +export * from './components/Table';