-
Notifications
You must be signed in to change notification settings - Fork 27
feat(table): add experimental component #493
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
32261f6
feat(table): add experimental component
ba9ae76
feat(table): add skeleton
d838b2d
docs(table): add examples
f1badbe
fix(table): fix lost generic props
402da38
chore: update stylelint whitelist
968f287
style(table): move comment
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| import { | ||
| Table as BaseTable, | ||
| TableProps, | ||
| Cell as BaseCell, | ||
| Column as BaseColumn, | ||
| Row as BaseRow, | ||
| TableBody, | ||
| TableHeader | ||
| } from 'react-aria-components'; | ||
| import styled from 'styled-components'; | ||
| import { get } from '../../../utils/experimental/themeGet'; | ||
| import { textStyles } from '../Text/Text'; | ||
| import { getSemanticValue } from '../../../essentials/experimental'; | ||
|
|
||
| const Table = styled(BaseTable)` | ||
| border-collapse: collapse; | ||
| border-spacing: 0; | ||
| position: relative; | ||
| width: 100%; | ||
| max-height: 100vh; | ||
| background: ${getSemanticValue('surface')}; | ||
| color: ${getSemanticValue('on-surface')}; | ||
| ` as typeof BaseTable; | ||
|
|
||
| const Cell = styled(BaseCell)` | ||
| padding: 0 ${get('space.3')}; | ||
| position: relative; | ||
|
|
||
| &::before { | ||
| position: absolute; | ||
| top: 0; | ||
| right: 0; | ||
| left: 0; | ||
| bottom: 0; | ||
| content: ''; | ||
| border-radius: inherit; | ||
| opacity: 0; | ||
| transition: opacity ease 200ms; | ||
| } | ||
|
|
||
| &:first-of-type { | ||
| border-radius: ${get('radii.4')} 0 0 ${get('radii.4')}; | ||
| } | ||
|
|
||
| &:last-of-type { | ||
| border-radius: 0 ${get('radii.4')} ${get('radii.4')} 0; | ||
| } | ||
|
|
||
| &[data-focused] { | ||
| outline: 0; | ||
| } | ||
| ` as typeof BaseCell; | ||
|
|
||
| const Column = styled(BaseColumn)` | ||
| position: sticky; | ||
| top: 0; | ||
| z-index: 1; /* for sticky headers to be on top of cells */ | ||
| padding: 0 ${get('space.3')}; | ||
| height: 3rem; | ||
| background: ${getSemanticValue('surface')}; | ||
| border-bottom: 1px solid ${getSemanticValue('divider')}; | ||
| text-align: start; | ||
| white-space: nowrap; | ||
| outline: 0; | ||
| ${textStyles.variants.title2} | ||
| ` as typeof BaseColumn; | ||
|
|
||
| const Row = styled(BaseRow)` | ||
| height: 3rem; | ||
| border-bottom: 1px solid ${getSemanticValue('divider')}; | ||
| border-radius: ${get('radii.4')}; | ||
| ${textStyles.variants.body1} | ||
|
|
||
| &[data-hovered] td::before { | ||
| background: ${getSemanticValue('on-surface')}; | ||
| opacity: 0.08; | ||
| } | ||
|
|
||
| &[data-selected] { | ||
| background: ${getSemanticValue('interactive-container')}; | ||
| } | ||
|
|
||
| &[data-focused] { | ||
| outline: 0.125rem solid ${getSemanticValue('accent')}; | ||
| outline-offset: -0.125rem; | ||
| } | ||
| ` as typeof BaseRow; | ||
|
|
||
| const Skeleton = styled.div` | ||
| height: 1rem; | ||
| border-radius: ${get('radii.2')}; | ||
| background: ${getSemanticValue('surface-variant')}; | ||
| `; | ||
|
|
||
| export { Table, TableProps, Cell, Column, Row, TableBody, TableHeader, Skeleton }; | ||
169 changes: 169 additions & 0 deletions
169
src/components/experimental/Table/docs/Table.stories.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| import React from 'react'; | ||
| import { StoryObj, Meta } from '@storybook/react'; | ||
| import { Table, TableHeader, TableBody, Row, Cell, Column, Skeleton } from '../Table'; | ||
| import { Text } from '../../Text/Text'; | ||
|
|
||
| const meta: Meta = { | ||
| title: 'Experimental/Components/Table', | ||
| component: Table, | ||
| parameters: { | ||
| layout: 'centered' | ||
| }, | ||
| args: { | ||
| label: 'Files' | ||
| } | ||
| }; | ||
|
|
||
| export default meta; | ||
|
|
||
| type Story = StoryObj<typeof Table>; | ||
|
|
||
| export const Default: Story = { | ||
| render: () => { | ||
| const columns: Array<{ id: string; name: string; isRowHeader?: boolean }> = [ | ||
| { name: 'Name', id: 'name', isRowHeader: true }, | ||
| { name: 'Type', id: 'type' }, | ||
| { name: 'Date Modified', id: 'date' } | ||
| ]; | ||
|
|
||
| const rows: Array<{ id: number; name: string; date: string; type: string }> = [ | ||
| { id: 1, name: 'Games', date: '6/7/2020', type: 'File folder' }, | ||
| { id: 2, name: 'Program Files', date: '4/7/2021', type: 'File folder' }, | ||
| { id: 3, name: 'bootmgr', date: '11/20/2010', type: 'System file' }, | ||
| { id: 4, name: 'log.txt', date: '1/18/2016', type: 'Text Document' }, | ||
| { id: 5, name: 'log.txt', date: '1/18/2016', type: 'Text Document' }, | ||
| { id: 6, name: 'log.txt', date: '1/18/2016', type: 'Text Document' }, | ||
| { id: 7, name: 'log.txt', date: '1/18/2016', type: 'Text Document' } | ||
| ]; | ||
|
|
||
| return ( | ||
| <Table aria-label="Files" selectionMode="multiple" selectionBehavior="replace"> | ||
| <TableHeader columns={columns}> | ||
| {column => <Column isRowHeader={column.isRowHeader}>{column.name}</Column>} | ||
| </TableHeader> | ||
| <TableBody items={rows}> | ||
| {item => <Row columns={columns}>{column => <Cell>{item[column.id]}</Cell>}</Row>} | ||
| </TableBody> | ||
| </Table> | ||
| ); | ||
| } | ||
| }; | ||
|
|
||
| export const Loading: Story = { | ||
| render: () => { | ||
| const columns: Array<{ id: string; name: string; isRowHeader?: boolean }> = [ | ||
| { name: 'Name', id: 'name', isRowHeader: true }, | ||
| { name: 'Type', id: 'type' }, | ||
| { name: 'Date Modified', id: 'date' } | ||
| ]; | ||
|
|
||
| return ( | ||
| <Table aria-label="Files"> | ||
| <TableHeader columns={columns}> | ||
| {column => <Column isRowHeader={column.isRowHeader}>{column.name}</Column>} | ||
| </TableHeader> | ||
| <TableBody items={[{ id: 1 }, { id: 2 }, { id: 3 }]}> | ||
| {() => ( | ||
| <Row columns={columns}> | ||
| {() => ( | ||
| <Cell> | ||
| <Skeleton /> | ||
| </Cell> | ||
| )} | ||
| </Row> | ||
| )} | ||
| </TableBody> | ||
| </Table> | ||
| ); | ||
| } | ||
| }; | ||
|
|
||
| export const Empty: Story = { | ||
| render: () => { | ||
| const columns: Array<{ id: string; name: string; isRowHeader?: boolean }> = [ | ||
| { name: 'Name', id: 'name', isRowHeader: true }, | ||
| { name: 'Type', id: 'type' }, | ||
| { name: 'Date Modified', id: 'date' } | ||
| ]; | ||
|
|
||
| return ( | ||
| <Table aria-label="Files"> | ||
| <TableHeader columns={columns}> | ||
| {column => <Column isRowHeader={column.isRowHeader}>{column.name}</Column>} | ||
| </TableHeader> | ||
| <TableBody | ||
| items={[]} | ||
| renderEmptyState={() => ( | ||
| <div style={{ padding: '1rem', textAlign: 'center' }}> | ||
| <Text variant="body1">No results found</Text> | ||
| </div> | ||
| )} | ||
| > | ||
| {[]} | ||
| </TableBody> | ||
| </Table> | ||
| ); | ||
| } | ||
| }; | ||
|
|
||
| export const Async: Story = { | ||
| render: () => { | ||
| type Character = { name: string; height: number; mass: number; birth_year: string }; | ||
| const emptyCharacter: Character = { | ||
| name: '', | ||
| height: 0, | ||
| mass: 0, | ||
| birth_year: '' | ||
| }; | ||
| const pageSize = 10; | ||
| const [isLoading, setIsLoading] = React.useState(true); | ||
| const [items, setItems] = React.useState<Character[]>( | ||
| /* eslint-disable-next-line unicorn/no-new-array */ | ||
| new Array(pageSize).fill(emptyCharacter).map((value, idx) => ({ ...value, name: idx.toString() })) | ||
| ); | ||
|
|
||
| React.useEffect(() => { | ||
| let ignore = false; | ||
|
|
||
| async function startFetching() { | ||
| const res = await fetch(`https://swapi.py4e.com/api/people`); | ||
| const json = await res.json(); | ||
|
|
||
| if (!ignore) { | ||
| setItems(json.results); | ||
| } | ||
|
|
||
| setIsLoading(false); | ||
| } | ||
|
|
||
| // eslint-disable-next-line no-void | ||
| void startFetching(); | ||
|
|
||
| return () => { | ||
| ignore = true; | ||
| }; | ||
| }, []); | ||
|
|
||
| const columns: Array<{ id: string; name: string; isRowHeader?: boolean }> = [ | ||
| { name: 'Name', id: 'name', isRowHeader: true }, | ||
| { name: 'Height', id: 'height' }, | ||
| { name: 'Mass', id: 'mass' }, | ||
| { name: 'Birth Year', id: 'birth_year' } | ||
| ]; | ||
|
|
||
| return ( | ||
| <Table aria-label="Star Wars Characters"> | ||
| <TableHeader columns={columns}> | ||
| {column => <Column isRowHeader={column.isRowHeader}>{column.name}</Column>} | ||
| </TableHeader> | ||
| <TableBody items={items}> | ||
| {item => ( | ||
| <Row id={item.name} columns={columns}> | ||
| {column => <Cell>{isLoading ? <Skeleton /> : item[column.id]}</Cell>} | ||
| </Row> | ||
| )} | ||
| </TableBody> | ||
| </Table> | ||
| ); | ||
| } | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.