diff --git a/package.json b/package.json index c9f4e1962..a16d3f015 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "dependencies": { "@headlessui/react": "^1.7.19", "@popperjs/core": "^2.11.8", + "@tanstack/react-table": "^8.20.5", "@tippyjs/react": "^4.2.6", "chalk": "^4.1.2", "clsx": "^2.1.1", diff --git a/src/components/DataTable/DataTable.module.css b/src/components/DataTable/DataTable.module.css index dd2170c2e..23c3f8777 100644 --- a/src/components/DataTable/DataTable.module.css +++ b/src/components/DataTable/DataTable.module.css @@ -6,6 +6,7 @@ * DataTable */ + /* Visible table caption */ .data-table__caption-container { display: flex; @@ -37,7 +38,6 @@ } .data-table__table { - border: 1px solid; width: 100%; .data-table__caption + &, @@ -50,11 +50,67 @@ width: calc(var(--eds-size-34) / 16 * 1rem); } +.data-table--tableStyle-border { + /* TODO-AH: token for 1px */ + border: 1px solid; +} + +.data-table__header-cell { + font: var(--eds-theme-typography-title-md); + text-align: left; + + .data-table--size-sm & { + padding: calc(var(--eds-size-half) / 16 * 1rem) calc(var(--eds-size-1) / 16 * 1rem); + } + + .data-table--size-md &{ + padding: calc(var(--eds-size-2) / 16 * 1rem); + } +} + +.data-table__cell { + font: var(--eds-theme-typography-body-md); + + .data-table--size-sm & { + padding: calc(var(--eds-size-half) / 16 * 1rem) calc(var(--eds-size-1) / 16 * 1rem); + } + + .data-table--size-md &{ + padding: calc(var(--eds-size-2) / 16 * 1rem); + } +} + +.data-table__row { + .data-table--rowStyle-lined & { + border-bottom: 1px solid; + } + + .data-table--rowStyle-striped &:nth-child(even) { + background-color: var(--eds-theme-color-background-table-row-stripe-2); + } + + .data-table--rowStyle-striped &:nth-child(odd) { + background-color: var(--eds-theme-color-background-table-row-stripe-1); + } +} + +.data-table__header-row { + border-bottom: 1px solid; + /* TODO-AH: figure out positioning styles for sticky headers/columns */ + position: sticky; + top: 0; +} + /** * Color Tokens */ .data-table { display: block; + position: relative; + + .data-table__table { + background-color: var(--eds-theme-color-background-utility-base-1); + } .data-table__caption { color: var(--eds-theme-color-text-utility-default-primary); @@ -63,4 +119,20 @@ .data-table__subcaption { color: var(--eds-theme-color-text-utility-default-secondary); } + + .data-table--tableStyle-border, .data-table__header-row { + border-color: var(--eds-theme-color-border-utility-default-low-emphasis); + } + + .data-table__header-cell { + color: var(--eds-theme-color-text-utility-default-primary); + } + + .data-table__cell { + color: var(--eds-theme-color-text-utility-default-primary); + } + + .data-table--rowStyle-lined { + color: var(--eds-theme-color-border-utility-default-low-emphasis); + } } diff --git a/src/components/DataTable/DataTable.stories.tsx b/src/components/DataTable/DataTable.stories.tsx index 38c077a98..d0215584a 100644 --- a/src/components/DataTable/DataTable.stories.tsx +++ b/src/components/DataTable/DataTable.stories.tsx @@ -1,8 +1,15 @@ import { BADGE } from '@geometricpanda/storybook-addon-badges'; import type { StoryObj, Meta } from '@storybook/react'; + +import { + createColumnHelper, + getCoreRowModel, + useReactTable, +} from '@tanstack/react-table'; + import React from 'react'; -import { DataTable } from './DataTable'; +import { DataTable, type DataTableProps } from './DataTable'; import { chromaticViewports } from '../../util/viewports'; import Button from '../Button'; import Menu from '../Menu'; @@ -24,7 +31,172 @@ export default { actions: { control: false, }, + children: { + control: false, + }, + }, +} as Meta; + +type Args = DataTableProps; + +type Person = { + firstName: string; + lastName: string; + age: number; + visits: number; + progress: number; +}; + +const defaultData: Person[] = [ + { + firstName: 'Tanner', + lastName: 'Lindsey', + age: 24, + visits: 100, + progress: 50, + }, + { + firstName: 'Tandy', + lastName: 'Miller', + age: 40, + visits: 40, + progress: 80, + }, + { + firstName: 'Joe', + lastName: 'Dirte', + age: 45, + visits: 20, + progress: 10, }, +]; + +const columnHelper = createColumnHelper(); + +const columns = [ + columnHelper.accessor('firstName', { + cell: (info) => info.getValue(), + header: () => First Name, + footer: (info) => info.column.id, + }), + columnHelper.accessor((row) => row.lastName, { + id: 'lastName', + cell: (info) => {info.getValue()}, + header: () => Last Name, + footer: (info) => info.column.id, + }), + columnHelper.accessor('age', { + header: () => 'Age', + cell: (info) => info.renderValue(), + footer: (info) => info.column.id, + }), + columnHelper.accessor('visits', { + header: () => Visits, + footer: (info) => info.column.id, + }), + columnHelper.accessor('progress', { + header: 'Profile Progress', + footer: (info) => info.column.id, + }), +]; + +export const Default: StoryObj = { + args: {}, + render: (args) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const [data] = React.useState(() => [...defaultData]); + + // eslint-disable-next-line react-hooks/rules-of-hooks + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }); + + const PersonDataTable = (args: DataTableProps) => { + // @ts-expect-error TODO-AH: what is the type issue here + return ; + }; + + return ; + }, +}; + +export const TableStyleBorder: StoryObj = { + args: { + tableStyle: 'border', + }, + render: (args) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const [data] = React.useState(() => [...defaultData]); + + // eslint-disable-next-line react-hooks/rules-of-hooks + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }); + + const PersonDataTable = (args: DataTableProps) => { + // @ts-expect-error TODO-AH: what is the type issue here + return ; + }; + + return ; + }, +}; + +export const TableSizeSm: StoryObj = { + args: { + tableStyle: 'border', + size: 'sm', + }, + render: (args) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const [data] = React.useState(() => [...defaultData]); + + // eslint-disable-next-line react-hooks/rules-of-hooks + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }); + + const PersonDataTable = (args: DataTableProps) => { + // @ts-expect-error TODO-AH: what is the type issue here + return ; + }; + + return ; + }, +}; + +export const RowStyleStriped: StoryObj = { + args: { + tableStyle: 'border', + rowStyle: 'striped', + }, + render: (args) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const [data] = React.useState(() => [...defaultData]); + + // eslint-disable-next-line react-hooks/rules-of-hooks + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }); + + const PersonDataTable = (args: DataTableProps) => { + // @ts-expect-error TODO-AH: what is the type issue here + return ; + }; + + return ; + }, +}; + +export const DefaultWithTable: StoryObj = { args: { children: ( @@ -36,22 +208,19 @@ export default {
), }, -} as Meta; - -type Args = React.ComponentProps; - -export const Default: StoryObj = { - args: {}, }; export const WithBasicCaption: StoryObj = { args: { + ...DefaultWithTable.args, caption: 'Fruits of the world', }, }; export const WithFullCaption: StoryObj = { + ...DefaultWithTable, args: { + ...DefaultWithTable.args, caption: 'Fruits of the world', subcaption: "Aren't they all so delicious?", }, @@ -59,6 +228,7 @@ export const WithFullCaption: StoryObj = { export const WithSearch: StoryObj = { args: { + ...DefaultWithTable.args, caption: 'Fruits of the world', subcaption: "Aren't they all so delicious?", onSearchChange: () => {}, @@ -67,6 +237,7 @@ export const WithSearch: StoryObj = { export const WithOnlyActions: StoryObj = { args: { + ...DefaultWithTable.args, actions: ( - - - - - - - - + + + + @@ -130,223 +304,142 @@ exports[` TableA story renders snapshot 1`] = ` `; -exports[` TableB story renders snapshot 1`] = ` +exports[` TableSizeSm story renders snapshot 1`] = `
-
-
- - -
-
+ + + + + + + + - - - - - - -
- TODO: Table rows/cells Here + Joe + + + Dirte + + + 45 + + 20 + + 10
- -
- -
- - - -
-
+ First Name + +
+ + Last Name + + + Age + + + Visits + + + Profile Progress +
- - - + + + + - -
- TODO: Table rows/cells Here + Tanner + + + Lindsey + + + 24 + + 100 + + 50
- -`; - -exports[` TableC story renders snapshot 1`] = ` -
-
-
- - -
- -
-
+ + + Miller + + + + 40 + + + 40 + + + 80 + + + - - - -
-
-
- - - - + + + + @@ -354,111 +447,142 @@ exports[` TableC story renders snapshot 1`] = ` `; -exports[` TableD story renders snapshot 1`] = ` +exports[` TableStyleBorder story renders snapshot 1`] = `
-
-
-
+ + + + + + + + - This is a really long title that really should not be this long and it just keeps going and going and going - - + + + + + - Seriously, who let this happen? - - - -
-
+
+ + + + + - - - - - - -
- TODO: Table rows/cells Here + Joe + + + Dirte + + + 45 + + 20 + + 10
+ + First Name + + + + Last Name + + + Age + + + Visits + + + Profile Progress +
+ + Lindsey + + + 24 + + 100 + + 50 +
+ + Miller + + + 40 + + 40 + + 80 +
- - - + + + + diff --git a/yarn.lock b/yarn.lock index caea688eb..05b5dac16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2762,6 +2762,7 @@ __metadata: "@storybook/test": "npm:^8.2.9" "@storybook/testing-library": "npm:^0.2.2" "@storybook/theming": "npm:^8.2.9" + "@tanstack/react-table": "npm:^8.20.5" "@testing-library/jest-dom": "npm:^6.5.0" "@testing-library/react": "npm:^16.0.1" "@testing-library/user-event": "npm:^14.5.2" @@ -5879,6 +5880,18 @@ __metadata: languageName: node linkType: hard +"@tanstack/react-table@npm:^8.20.5": + version: 8.20.5 + resolution: "@tanstack/react-table@npm:8.20.5" + dependencies: + "@tanstack/table-core": "npm:8.20.5" + peerDependencies: + react: ">=16.8" + react-dom: ">=16.8" + checksum: 10/df67094795a0b7e4b34f73abe346443c2e806c572fea31b58759aa8ec5274f613e5e6941090eb16f861bda10d3088731bc6e7f15e5f90326db273bc55b9141ce + languageName: node + linkType: hard + "@tanstack/react-virtual@npm:^3.0.0-beta.60": version: 3.0.1 resolution: "@tanstack/react-virtual@npm:3.0.1" @@ -5891,6 +5904,13 @@ __metadata: languageName: node linkType: hard +"@tanstack/table-core@npm:8.20.5": + version: 8.20.5 + resolution: "@tanstack/table-core@npm:8.20.5" + checksum: 10/5408237920d5796951e925278edbbe76f71006627a4e3da248a810970256f75d973538fe7ae75a32155d4a25a95abc4fffaea337b5120f7940d7e664dc9da87f + languageName: node + linkType: hard + "@tanstack/virtual-core@npm:3.0.0": version: 3.0.0 resolution: "@tanstack/virtual-core@npm:3.0.0"
- TODO: Table rows/cells Here + Joe + + + Dirte + + + 45 + + 20 + + 10