Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1711 lines (1677 sloc)
54 KB
This file contains 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
| /* | |
| * Copyright 2020 Adobe. All rights reserved. | |
| * This file is licensed to you 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 REPRESENTATIONS | |
| * OF ANY KIND, either express or implied. See the License for the specific language | |
| * governing permissions and limitations under the License. | |
| */ | |
| import {action} from '@storybook/addon-actions'; | |
| import {ActionButton, Button} from '@react-spectrum/button'; | |
| import Add from '@spectrum-icons/workflow/Add'; | |
| import {Breadcrumbs, Item} from '@react-spectrum/breadcrumbs'; | |
| import {ButtonGroup} from '@react-spectrum/buttongroup'; | |
| import {Cell, Column, Row, TableBody, TableHeader, TableView} from '../'; | |
| import {Content} from '@react-spectrum/view'; | |
| import {CRUDExample} from './CRUDExample'; | |
| import Delete from '@spectrum-icons/workflow/Delete'; | |
| import {Dialog, DialogTrigger} from '@react-spectrum/dialog'; | |
| import {Divider} from '@react-spectrum/divider'; | |
| import {Flex} from '@react-spectrum/layout'; | |
| import {Heading} from '@react-spectrum/text'; | |
| import {HidingColumns} from './HidingColumns'; | |
| import {HidingColumnsAllowsResizing} from './HidingColumnsAllowsResizing'; | |
| import {IllustratedMessage} from '@react-spectrum/illustratedmessage'; | |
| import {Link} from '@react-spectrum/link'; | |
| import {LoadingState, SelectionMode} from '@react-types/shared'; | |
| import {Radio, RadioGroup} from '@react-spectrum/radio'; | |
| import React, {Key, useState} from 'react'; | |
| import {SearchField} from '@react-spectrum/searchfield'; | |
| import {storiesOf} from '@storybook/react'; | |
| import {Switch} from '@react-spectrum/switch'; | |
| import {TextField} from '@react-spectrum/textfield'; | |
| import {useAsyncList} from '@react-stately/data'; | |
| import {useFilter} from '@react-aria/i18n'; | |
| import {View} from '@react-spectrum/view'; | |
| let columns = [ | |
| {name: 'Foo', key: 'foo'}, | |
| {name: 'Bar', key: 'bar'}, | |
| {name: 'Baz', key: 'baz'} | |
| ]; | |
| let nestedColumns = [ | |
| {name: 'Test', key: 'test'}, | |
| {name: 'Tiered One Header', key: 'tier1', children: [ | |
| {name: 'Tier Two Header A', key: 'tier2a', children: [ | |
| {name: 'Foo', key: 'foo'}, | |
| {name: 'Bar', key: 'bar'} | |
| ]}, | |
| {name: 'Yay', key: 'yay'}, | |
| {name: 'Tier Two Header B', key: 'tier2b', children: [ | |
| {name: 'Baz', key: 'baz'} | |
| ]} | |
| ]} | |
| ]; | |
| let items = [ | |
| {test: 'Test 1', foo: 'Foo 1', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1'}, | |
| {test: 'Test 2', foo: 'Foo 2', bar: 'Bar 2', yay: 'Yay 2', baz: 'Baz 2'}, | |
| {test: 'Test 1', foo: 'Foo 3', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1'}, | |
| {test: 'Test 2', foo: 'Foo 4', bar: 'Bar 2', yay: 'Yay 2', baz: 'Baz 2'}, | |
| {test: 'Test 1', foo: 'Foo 5', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1'}, | |
| {test: 'Test 2', foo: 'Foo 6', bar: 'Bar 2', yay: 'Yay 2', baz: 'Baz 2'}, | |
| {test: 'Test 1', foo: 'Foo 7', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1'}, | |
| {test: 'Test 2', foo: 'Foo 8', bar: 'Bar 2', yay: 'Yay 2', baz: 'Baz 2'} | |
| ]; | |
| let itemsWithFalsyId = [ | |
| {test: 'Test 1', foo: 'Foo 1', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1', id: 0}, | |
| {test: 'Test 1', foo: 'Foo 2', bar: 'Bar 1', yay: 'Yay 1', baz: 'Baz 1', id: 1} | |
| ]; | |
| let manyColunns = []; | |
| for (let i = 0; i < 100; i++) { | |
| manyColunns.push({name: 'Column ' + i, key: 'C' + i}); | |
| } | |
| let manyRows = []; | |
| for (let i = 0; i < 1000; i++) { | |
| let row = {key: 'R' + i}; | |
| for (let j = 0; j < 100; j++) { | |
| row['C' + j] = `${i}, ${j}`; | |
| } | |
| manyRows.push(row); | |
| } | |
| function renderEmptyState() { | |
| return ( | |
| <IllustratedMessage> | |
| <svg width="150" height="103" viewBox="0 0 150 103"> | |
| <path d="M133.7,8.5h-118c-1.9,0-3.5,1.6-3.5,3.5v27c0,0.8,0.7,1.5,1.5,1.5s1.5-0.7,1.5-1.5V23.5h119V92c0,0.3-0.2,0.5-0.5,0.5h-118c-0.3,0-0.5-0.2-0.5-0.5V69c0-0.8-0.7-1.5-1.5-1.5s-1.5,0.7-1.5,1.5v23c0,1.9,1.6,3.5,3.5,3.5h118c1.9,0,3.5-1.6,3.5-3.5V12C137.2,10.1,135.6,8.5,133.7,8.5z M15.2,21.5V12c0-0.3,0.2-0.5,0.5-0.5h118c0.3,0,0.5,0.2,0.5,0.5v9.5H15.2z M32.6,16.5c0,0.6-0.4,1-1,1h-10c-0.6,0-1-0.4-1-1s0.4-1,1-1h10C32.2,15.5,32.6,15.9,32.6,16.5z M13.6,56.1l-8.6,8.5C4.8,65,4.4,65.1,4,65.1c-0.4,0-0.8-0.1-1.1-0.4c-0.6-0.6-0.6-1.5,0-2.1l8.6-8.5l-8.6-8.5c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l8.6,8.5l8.6-8.5c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1L15.8,54l8.6,8.5c0.6,0.6,0.6,1.5,0,2.1c-0.3,0.3-0.7,0.4-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4L13.6,56.1z" /> | |
| </svg> | |
| <Heading>No results</Heading> | |
| <Content>No results found</Content> | |
| </IllustratedMessage> | |
| ); | |
| } | |
| let onSelectionChange = action('onSelectionChange'); | |
| storiesOf('TableView', module) | |
| .add( | |
| 'static', | |
| () => ( | |
| <TableView aria-label="TableView with static contents" width={300} height={200}> | |
| <TableHeader> | |
| <Column key="foo">Foo</Column> | |
| <Column key="bar">Bar</Column> | |
| <Column key="baz">Baz</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>One</Cell> | |
| <Cell>Two</Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>One</Cell> | |
| <Cell>Two</Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'static with selection', | |
| () => ( | |
| <TableView aria-label="TableView with static contents" selectionMode="multiple" width={300} height={200} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column key="foo">Foo</Column> | |
| <Column key="bar">Bar</Column> | |
| <Column key="baz">Baz</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>One</Cell> | |
| <Cell>Two</Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>One</Cell> | |
| <Cell>Two</Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'dynamic', | |
| () => ( | |
| <TableView aria-label="TableView with dynamic contents" width={300} height={200}> | |
| <TableHeader columns={columns}> | |
| {column => <Column>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'dynamic, falsy row keys', | |
| () => ( | |
| <TableView aria-label="TableView with dynamic contents and falsy keys" width={300} height={200}> | |
| <TableHeader columns={columns}> | |
| {column => <Column>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={itemsWithFalsyId}> | |
| {item => | |
| (<Row> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'dynamic with selection', | |
| () => ( | |
| <TableView aria-label="TableView with dynamic contents" selectionMode="multiple" width={300} height={200} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={columns}> | |
| {column => <Column>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'dynamic with single selection', | |
| () => ( | |
| <TableView aria-label="TableView with dynamic contents" selectionMode="single" width={300} height={200} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={columns}> | |
| {column => <Column>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'horizontal scrolling only', | |
| () => ( | |
| <TableView aria-label="TableView with dynamic contents" selectionMode="single" width={200} height={220} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={columns}> | |
| {column => <Column>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items.slice(0, 3)}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'horizontal scrolling only flush bottom', | |
| () => ( | |
| <TableView aria-label="TableView with dynamic contents" selectionMode="single" width={200} height={174} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={columns}> | |
| {column => <Column>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items.slice(0, 3)}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'dynamic with disabled, single selection', | |
| () => ( | |
| <TableView disabledKeys={['Foo 1', 'Foo 3']} aria-label="TableView with dynamic contents" selectionMode="single" width={300} height={200} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={columns}> | |
| {column => <Column>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'dynamic with disabled, multiple selection', | |
| () => ( | |
| <TableView disabledKeys={['Foo 1', 'Foo 3']} aria-label="TableView with dynamic contents" selectionMode="multiple" width={300} height={200} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={columns}> | |
| {column => <Column>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'dynamic with disabled, multiple selection, highlight', | |
| () => ( | |
| <TableView disabledKeys={['Foo 1', 'Foo 3']} aria-label="TableView with dynamic contents" selectionStyle="highlight" selectionMode="multiple" width={300} height={200} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={columns}> | |
| {column => <Column>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'dynamic with disabled, multiple selection, quiet', | |
| () => ( | |
| <TableView isQuiet disabledKeys={['Foo 1', 'Foo 3']} aria-label="TableView with dynamic contents" selectionMode="multiple" width={300} height={200} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={columns}> | |
| {column => <Column>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'defaultSelectedKeys, dynamic, multiple selection, showDivider', | |
| () => ( | |
| <TableView defaultSelectedKeys={['Foo 1', 'Foo 3']} onSelectionChange={s => onSelectionChange([...s])} selectionMode="multiple" aria-label="TableView with dynamic contents" width={300} height={200}> | |
| <TableHeader columns={columns}> | |
| {column => <Column showDivider>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'selectedKeys, dynamic, multiple selection, quiet, showDider', | |
| () => ( | |
| <TableView isQuiet selectedKeys={['Foo 1', 'Foo 3']} onSelectionChange={s => onSelectionChange([...s])} selectionMode="multiple" aria-label="TableView with dynamic contents" width={300} height={200}> | |
| <TableHeader columns={columns}> | |
| {column => <Column showDivider>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'selectionStyle: highlight', | |
| () => ( | |
| <TableView aria-label="TableView with dynamic contents" selectionMode="multiple" selectionStyle="highlight" width={400} height={300} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={columns}> | |
| {column => <Column minWidth={200}>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'selectionStyle: highlight, onAction', | |
| () => ( | |
| <TableView aria-label="TableView with dynamic contents" selectionMode="multiple" selectionStyle="highlight" width={400} height={300} onSelectionChange={s => onSelectionChange([...s])} onAction={action('onAction')}> | |
| <TableHeader columns={columns}> | |
| {column => <Column minWidth={200}>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'selectionMode: none, onAction', | |
| () => ( | |
| <TableView aria-label="TableView with dynamic contents" width={400} height={300} onSelectionChange={s => onSelectionChange([...s])} onAction={action('onAction')}> | |
| <TableHeader columns={columns}> | |
| {column => <Column minWidth={200}>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'selectionStyle: checkbox, onAction', | |
| () => ( | |
| <TableView aria-label="TableView with dynamic contents" width={400} height={300} selectionMode="multiple" selectionStyle="checkbox" onSelectionChange={s => onSelectionChange([...s])} onAction={action('onAction')}> | |
| <TableHeader columns={columns}> | |
| {column => <Column minWidth={200}>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| // For testing https://github.com/adobe/react-spectrum/issues/1885 | |
| 'swap selection mode', | |
| () => ( | |
| <ChangableSelectionMode /> | |
| ) | |
| ) | |
| .add( | |
| 'static with nested columns', | |
| () => ( | |
| <TableView aria-label="TableView with nested columns" selectionMode="multiple" width={500} height={200} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column key="test">Test</Column> | |
| <Column title="Group 1"> | |
| <Column key="foo">Foo</Column> | |
| <Column key="bar">Bar</Column> | |
| </Column> | |
| <Column title="Group 2"> | |
| <Column key="baz">Baz</Column> | |
| </Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>Test1</Cell> | |
| <Cell>One</Cell> | |
| <Cell>Two</Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Test2</Cell> | |
| <Cell>One</Cell> | |
| <Cell>Two</Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'dynamic with nested columns', | |
| () => ( | |
| <TableView aria-label="TableView with nested columns" selectionMode="multiple" width={700} height={300} overflowMode="wrap" onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={nestedColumns}> | |
| {column => | |
| <Column childColumns={column.children}>{column.name}</Column> | |
| } | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'focusable cells', | |
| () => ( | |
| <Flex direction="column"> | |
| <label htmlFor="focus-before">Focus before</label> | |
| <input id="focus-before" /> | |
| <TableView aria-label="TableView with focusable cells" selectionMode="multiple" width={450} height={200} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column key="foo">Foo</Column> | |
| <Column key="bar">Bar</Column> | |
| <Column key="baz">baz</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell><Switch aria-label="Foo" /></Cell> | |
| <Cell><Link><a href="https://yahoo.com" target="_blank">Yahoo</a></Link></Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| <Row> | |
| <Cell><Switch aria-label="Foo" /><Switch aria-label="Bar" /></Cell> | |
| <Cell><Link><a href="https://google.com" target="_blank">Google</a></Link></Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| <Row> | |
| <Cell><Switch aria-label="Foo" /></Cell> | |
| <Cell><Link><a href="https://yahoo.com" target="_blank">Yahoo</a></Link></Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| <label htmlFor="focus-after">Focus after</label> | |
| <input id="focus-after" /> | |
| </Flex> | |
| ) | |
| ) | |
| .add( | |
| 'many columns and rows', | |
| () => ( | |
| <> | |
| <label htmlFor="focus-before">Focus before</label> | |
| <input id="focus-before" /> | |
| <TableView aria-label="TableView with many columns and rows" selectionMode="multiple" width={700} height={500} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={manyColunns}> | |
| {column => | |
| <Column minWidth={100}>{column.name}</Column> | |
| } | |
| </TableHeader> | |
| <TableBody items={manyRows}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| <label htmlFor="focus-after">Focus after</label> | |
| <input id="focus-after" /> | |
| </> | |
| ), | |
| {chromatic: {disable: true}} | |
| ) | |
| .add( | |
| 'isQuiet, many columns and rows', | |
| () => ( | |
| <TableView aria-label="Quiet TableView with many columns and rows" selectionMode="multiple" width={700} height={500} isQuiet onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={manyColunns}> | |
| {column => | |
| <Column minWidth={100}>{column.name}</Column> | |
| } | |
| </TableHeader> | |
| <TableBody items={manyRows}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ), | |
| {chromatic: {disable: true}} | |
| ) | |
| .add( | |
| 'should fill cell width', | |
| () => ( | |
| <TableView aria-label="TableView with filled cells" selectionMode="multiple" width={500} height={200} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column>File Name</Column> | |
| <Column align="center">Type</Column> | |
| <Column align="end">Size</Column> | |
| <Column>Description</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| <Cell>very very very very very very long long long long long description</Cell> | |
| </Row> | |
| <Row> | |
| <Cell> | |
| <View | |
| width="100%" | |
| backgroundColor="gray-200"> | |
| 100% | |
| </View> | |
| </Cell> | |
| <Cell> | |
| <View | |
| UNSAFE_style={{margin: 'auto', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} | |
| width="100%" | |
| backgroundColor="gray-200"> | |
| 100% | |
| </View> | |
| </Cell> | |
| <Cell> | |
| <View | |
| UNSAFE_style={{marginInlineStart: 'auto', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} | |
| width="100%" | |
| backgroundColor="gray-200"> | |
| 100% | |
| </View> | |
| </Cell> | |
| <Cell> | |
| <View | |
| UNSAFE_style={{overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} | |
| width="100%" | |
| backgroundColor="gray-200"> | |
| very very very very very very long long long long long description | |
| </View> | |
| </Cell> | |
| </Row> | |
| <Row> | |
| <Cell> | |
| <View | |
| UNSAFE_style={{overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} | |
| width="50%" | |
| backgroundColor="gray-200"> | |
| 50% div | |
| </View> | |
| </Cell> | |
| <Cell> | |
| <View | |
| UNSAFE_style={{margin: 'auto', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} | |
| width="70%" | |
| backgroundColor="gray-200"> | |
| 70% div | |
| </View> | |
| </Cell> | |
| <Cell> | |
| <View | |
| UNSAFE_style={{float: 'right', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} | |
| width="70%" | |
| backgroundColor="gray-200"> | |
| 70% div | |
| </View> | |
| </Cell> | |
| <Cell> | |
| <View | |
| UNSAFE_style={{overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}} | |
| width="70%" | |
| backgroundColor="gray-200"> | |
| very very very very very very long long long long long description | |
| </View> | |
| </Cell> | |
| </Row> | |
| <Row> | |
| <Cell> | |
| <span style={{backgroundColor: 'var(--spectrum-global-color-gray-200'}}> | |
| span child | |
| </span> | |
| </Cell> | |
| <Cell> | |
| <span style={{backgroundColor: 'var(--spectrum-global-color-gray-200'}}> | |
| span child</span> | |
| </Cell> | |
| <Cell> | |
| <span style={{backgroundColor: 'var(--spectrum-global-color-gray-200'}}> | |
| span child | |
| </span> | |
| </Cell> | |
| <Cell> | |
| <span style={{backgroundColor: 'var(--spectrum-global-color-gray-200'}}> | |
| very very very very very very long long long long long description | |
| </span> | |
| </Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'column widths and dividers', | |
| () => ( | |
| <TableView aria-label="TableView with column widths and dividers" selectionMode="multiple" width={500} height={200} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column width={250} showDivider>File Name</Column> | |
| <Column>Type</Column> | |
| <Column align="end">Size</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'isQuiet, column widths and dividers', | |
| () => ( | |
| <TableView aria-label="Quiet TableView with column widths and dividers" selectionMode="multiple" width={500} height={200} isQuiet onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column width={250} showDivider>File Name</Column> | |
| <Column>Type</Column> | |
| <Column align="end">Size</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'density="compact"', | |
| () => ( | |
| <TableView aria-label="TableView with custom row height" selectionMode="multiple" width={500} height={200} isQuiet density="compact" onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column width={250} showDivider>File Name</Column> | |
| <Column>Type</Column> | |
| <Column align="end">Size</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'density="spacious"', | |
| () => ( | |
| <TableView aria-label="TableView with custom row height" selectionMode="multiple" width={500} height={200} isQuiet density="spacious" onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column width={250} showDivider>File Name</Column> | |
| <Column>Type</Column> | |
| <Column align="end">Size</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'overflowMode="wrap"', | |
| () => ( | |
| <TableView aria-label="TableView with variable row heights" selectionMode="multiple" width={500} height={300} isQuiet overflowMode="wrap" onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column width={250} showDivider>File Name</Column> | |
| <Column>Type</Column> | |
| <Column align="end">Size</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal with very very very very very very long long long long long filename</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'overflowMode="wrap", density="compact"', | |
| () => ( | |
| <TableView aria-label="TableView with variable row heights" selectionMode="multiple" width={500} height={300} isQuiet overflowMode="wrap" density="compact" onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column width={250} showDivider>File Name</Column> | |
| <Column>Type</Column> | |
| <Column align="end">Size</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal with very very very very very very long long long long long filename</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'overflowMode="wrap", density="spacious"', | |
| () => ( | |
| <TableView aria-label="TableView with variable row heights" selectionMode="multiple" width={500} height={300} isQuiet overflowMode="wrap" density="spacious" onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column width={250} showDivider>File Name</Column> | |
| <Column>Type</Column> | |
| <Column align="end">Size</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal with very very very very very very long long long long long filename</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'custom isRowHeader labeling', | |
| () => ( | |
| <TableView aria-label="TableView with custom row header labeling" selectionMode="multiple" width={500} height={200} isQuiet onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column isRowHeader>First Name</Column> | |
| <Column isRowHeader>Last Name</Column> | |
| <Column>Birthday</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>Sam</Cell> | |
| <Cell>Smith</Cell> | |
| <Cell>May 3</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Julia</Cell> | |
| <Cell>Jones</Cell> | |
| <Cell>February 10</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'CRUD', | |
| () => ( | |
| <CRUDExample /> | |
| ) | |
| ) | |
| .add( | |
| 'hiding columns', | |
| () => ( | |
| <HidingColumns /> | |
| ) | |
| ) | |
| .add( | |
| 'isLoading', | |
| () => ( | |
| <TableView aria-label="TableView loading" width={700} height={200}> | |
| <TableHeader columns={manyColunns}> | |
| {column => | |
| <Column minWidth={100}>{column.name}</Column> | |
| } | |
| </TableHeader> | |
| <TableBody items={[]} loadingState="loading"> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'isLoading more', | |
| () => ( | |
| <TableView aria-label="TableView loading more" width={700} height={200} selectionMode="multiple"> | |
| <TableHeader columns={columns}> | |
| {column => | |
| <Column minWidth={100}>{column.name}</Column> | |
| } | |
| </TableHeader> | |
| <TableBody items={items} loadingState="loadingMore"> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'filtering', | |
| () => ( | |
| <TableView aria-label="Table filtering" width={700} height={200}> | |
| <TableHeader columns={columns}> | |
| {column => | |
| <Column minWidth={100}>{column.name}</Column> | |
| } | |
| </TableHeader> | |
| <TableBody items={items} loadingState="filtering"> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'renderEmptyState', | |
| () => ( | |
| <TableView aria-label="TableView with empty state" width={700} height={400} isQuiet renderEmptyState={renderEmptyState}> | |
| <TableHeader columns={manyColunns}> | |
| {column => | |
| <Column minWidth={100}>{column.name}</Column> | |
| } | |
| </TableHeader> | |
| <TableBody> | |
| {[]} | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'async loading', | |
| () => <AsyncLoadingExample />, | |
| {chromatic: {disable: true}} | |
| ) | |
| .add( | |
| 'hideHeader', | |
| () => ( | |
| <TableView | |
| aria-label="TableView with static contents" | |
| width={350} | |
| height={200}> | |
| <TableHeader> | |
| <Column key="foo"> | |
| Foo | |
| </Column> | |
| <Column key="addAction" hideHeader> | |
| Add Info | |
| </Column> | |
| <Column key="deleteAction" hideHeader showDivider> | |
| Delete Item | |
| </Column> | |
| <Column key="bar">Bar</Column> | |
| <Column key="baz">Baz</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>One</Cell> | |
| <Cell> | |
| <ActionButton isQuiet aria-label="Add Info"> | |
| <Add /> | |
| </ActionButton> | |
| </Cell> | |
| <Cell> | |
| <ActionButton isQuiet aria-label="Delete"> | |
| <Delete /> | |
| </ActionButton> | |
| </Cell> | |
| <Cell>Two</Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>One</Cell> | |
| <Cell> | |
| <ActionButton isQuiet aria-label="Add Info"> | |
| <Add /> | |
| </ActionButton> | |
| </Cell> | |
| <Cell> | |
| <ActionButton isQuiet aria-label="Delete"> | |
| <Delete /> | |
| </ActionButton> | |
| </Cell> | |
| <Cell>Two</Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>One</Cell> | |
| <Cell> | |
| <ActionButton isQuiet aria-label="Add Info"> | |
| <Add /> | |
| </ActionButton> | |
| </Cell> | |
| <Cell> | |
| <ActionButton isQuiet aria-label="Delete"> | |
| <Delete /> | |
| </ActionButton> | |
| </Cell> | |
| <Cell>Two</Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>One</Cell> | |
| <Cell> | |
| <ActionButton isQuiet aria-label="Add Info"> | |
| <Add /> | |
| </ActionButton> | |
| </Cell> | |
| <Cell> | |
| <ActionButton isQuiet aria-label="Delete"> | |
| <Delete /> | |
| </ActionButton> | |
| </Cell> | |
| <Cell>Two</Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>One</Cell> | |
| <Cell> | |
| <ActionButton isQuiet aria-label="Add Info"> | |
| <Add /> | |
| </ActionButton> | |
| </Cell> | |
| <Cell> | |
| <ActionButton isQuiet aria-label="Delete"> | |
| <Delete /> | |
| </ActionButton> | |
| </Cell> | |
| <Cell>Two</Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>One</Cell> | |
| <Cell> | |
| <ActionButton isQuiet aria-label="Add Info"> | |
| <Add /> | |
| </ActionButton> | |
| </Cell> | |
| <Cell> | |
| <ActionButton isQuiet aria-label="Delete"> | |
| <Delete /> | |
| </ActionButton> | |
| </Cell> | |
| <Cell>Two</Cell> | |
| <Cell>Three</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'async client side filter loading', | |
| () => <ProjectListTable />, | |
| {chromatic: {disable: true}} | |
| ) | |
| .add( | |
| 'async server side filter loading', | |
| () => <AsyncServerFilterTable />, | |
| {chromatic: {disable: true}} | |
| ) | |
| .add( | |
| 'loads more on scroll when contentSize.height < rect.height * 2', | |
| () => <AsyncServerFilterTable height={500} />, | |
| {chromatic: {disable: true}} | |
| ) | |
| .add( | |
| 'with dialog trigger', | |
| () => ( | |
| <TableView aria-label="TableView with static contents" selectionMode="multiple" width={300} height={200} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader> | |
| <Column key="foo">Foo</Column> | |
| <Column key="bar">Bar</Column> | |
| <Column key="baz">Baz</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>One</Cell> | |
| <Cell>Two</Cell> | |
| <Cell> | |
| <DialogTrigger> | |
| <ActionButton aria-label="Add"><Add /></ActionButton> | |
| {close => ( | |
| <Dialog> | |
| <Heading>The Heading</Heading> | |
| <Divider /> | |
| <Content> | |
| <TextField label="Last Words" /> | |
| </Content> | |
| <ButtonGroup> | |
| <Button variant="secondary" onPress={close}>Cancel</Button> | |
| <Button variant="cta" onPress={close}>Confirm</Button> | |
| </ButtonGroup> | |
| </Dialog> | |
| )} | |
| </DialogTrigger> | |
| </Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add('table with breadcrumb navigation', () => <TableWithBreadcrumbs />) | |
| .add( | |
| 'allowsResizing, uncontrolled, dynamic widths', | |
| () => ( | |
| <> | |
| <label htmlFor="focusable-before">Focusable before</label> | |
| <input id="focusable-before" /> | |
| <TableView aria-label="TableView with resizable columns" width={800} height={200}> | |
| <TableHeader> | |
| <Column allowsResizing defaultWidth="1fr">File Name</Column> | |
| <Column allowsResizing defaultWidth="2fr">Type</Column> | |
| <Column allowsResizing defaultWidth="2fr">Size</Column> | |
| <Column allowsResizing defaultWidth="1fr">Weight</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| <Cell>1 LB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| <Cell>20 LB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| <label htmlFor="focusable-after">Focusable after</label> | |
| <input id="focusable-after" /> | |
| </> | |
| ) | |
| ) | |
| .add( | |
| 'allowsResizing, uncontrolled, static widths', () => ( | |
| <TableView aria-label="TableView with resizable columns" width={800} height={200}> | |
| <TableHeader> | |
| <Column allowsResizing defaultWidth="50%">File Name</Column> | |
| <Column allowsResizing defaultWidth="20%">Type</Column> | |
| <Column allowsResizing defaultWidth={239}>Size</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'allowsResizing, uncontrolled, column divider', () => ( | |
| <TableView aria-label="TableView with resizable columns and divider" width={800} height={200}> | |
| <TableHeader> | |
| <Column allowsResizing showDivider>File Name</Column> | |
| <Column allowsResizing defaultWidth="3fr">Type</Column> | |
| <Column allowsResizing>Size</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'allowsResizing, uncontrolled, min/max widths', () => ( | |
| <TableView aria-label="TableView with resizable columns" width={800} height={200}> | |
| <TableHeader> | |
| <Column allowsResizing defaultWidth={200} minWidth={175} maxWidth={300}>File Name</Column> | |
| <Column allowsResizing defaultWidth="1fr" minWidth={175} maxWidth={500}>Size</Column> | |
| <Column allowsResizing defaultWidth={200} minWidth={175} maxWidth={300}>Type</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'allowsResizing, uncontrolled, some columns not allowed resizing', () => ( | |
| <TableView aria-label="TableView with resizable columns" width={800} height={200}> | |
| <TableHeader> | |
| <Column allowsResizing >File Name</Column> | |
| <Column defaultWidth="1fr">Type</Column> | |
| <Column defaultWidth="2fr">Size</Column> | |
| <Column allowsResizing defaultWidth="2fr">Weight</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| <Cell>1 LB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| <Cell>20 LB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'allowsResizing, uncontrolled, undefined table width and height', () => ( | |
| <TableView aria-label="TableView with resizable columns and no width or height set"> | |
| <TableHeader> | |
| <Column allowsResizing defaultWidth={150}>File Name</Column> | |
| <Column allowsResizing defaultWidth={100}>Type</Column> | |
| <Column allowsResizing defaultWidth={100}>Size</Column> | |
| <Column allowsResizing defaultWidth={100}>Weight</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| <Cell>1 LB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| <Cell>20 LB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| ) | |
| ) | |
| .add( | |
| 'allowsResizing, uncontrolled, sortable columns', | |
| () => <AsyncLoadingExample isResizable />, | |
| {chromatic: {disable: true}} | |
| ) | |
| .add( | |
| 'allowsResizing, many columns and rows', | |
| () => ( | |
| <> | |
| <label htmlFor="focusable-before">Focusable before</label> | |
| <input id="focusable-before" /> | |
| <TableView aria-label="TableView with many columns and rows" selectionMode="multiple" width={700} height={500} onSelectionChange={s => onSelectionChange([...s])}> | |
| <TableHeader columns={manyColunns}> | |
| {column => | |
| <Column allowsResizing minWidth={100}>{column.name}</Column> | |
| } | |
| </TableHeader> | |
| <TableBody items={manyRows}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| <label htmlFor="focusable-after">Focusable after</label> | |
| <input id="focusable-after" /> | |
| </> | |
| ), | |
| {chromatic: {disable: true}} | |
| ) | |
| .add( | |
| 'allowsResizing, onColumnResize action', | |
| () => ( | |
| <TableView aria-label="TableView with resizable columns" width={800} height={200} onColumnResize={action('onColumnResize')}> | |
| <TableHeader> | |
| <Column allowsResizing defaultWidth="1fr">File Name</Column> | |
| <Column allowsResizing defaultWidth="2fr">Type</Column> | |
| <Column allowsResizing defaultWidth="2fr">Size</Column> | |
| <Column allowsResizing defaultWidth="1fr">Weight</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| <Cell>1 LB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| <Cell>20 LB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| )) | |
| .add( | |
| 'allowsResizing, onColumnResizeEnd action', | |
| () => ( | |
| <TableView aria-label="TableView with resizable columns" width={800} height={200} onColumnResizeEnd={action('onColumnResizeEnd')}> | |
| <TableHeader> | |
| <Column allowsResizing defaultWidth="1fr">File Name</Column> | |
| <Column allowsResizing defaultWidth="2fr">Type</Column> | |
| <Column allowsResizing defaultWidth="2fr">Size</Column> | |
| <Column allowsResizing defaultWidth="1fr">Weight</Column> | |
| </TableHeader> | |
| <TableBody> | |
| <Row> | |
| <Cell>2018 Proposal</Cell> | |
| <Cell>PDF</Cell> | |
| <Cell>214 KB</Cell> | |
| <Cell>1 LB</Cell> | |
| </Row> | |
| <Row> | |
| <Cell>Budget</Cell> | |
| <Cell>XLS</Cell> | |
| <Cell>120 KB</Cell> | |
| <Cell>20 LB</Cell> | |
| </Row> | |
| </TableBody> | |
| </TableView> | |
| )) | |
| .add( | |
| 'allowsResizing, hiding columns', | |
| () => ( | |
| <HidingColumnsAllowsResizing /> | |
| ) | |
| ); | |
| function AsyncLoadingExample(props) { | |
| const {isResizable} = props; | |
| interface Item { | |
| data: { | |
| id: string, | |
| url: string, | |
| title: string | |
| } | |
| } | |
| let list = useAsyncList<Item>({ | |
| getKey: (item) => item.data.id, | |
| async load({signal, cursor}) { | |
| let url = new URL('https://www.reddit.com/r/news.json'); | |
| if (cursor) { | |
| url.searchParams.append('after', cursor); | |
| } | |
| let res = await fetch(url.toString(), {signal}); | |
| let json = await res.json(); | |
| return {items: json.data.children, cursor: json.data.after}; | |
| }, | |
| sort({items, sortDescriptor}) { | |
| return { | |
| items: items.slice().sort((a, b) => { | |
| let cmp = a.data[sortDescriptor.column] < b.data[sortDescriptor.column] ? -1 : 1; | |
| if (sortDescriptor.direction === 'descending') { | |
| cmp *= -1; | |
| } | |
| return cmp; | |
| }) | |
| }; | |
| } | |
| }); | |
| return ( | |
| <div> | |
| <ActionButton marginBottom={10} onPress={() => list.remove(list.items[0].data.id)}>Remove first item</ActionButton> | |
| <TableView aria-label="Top news from Reddit" selectionMode="multiple" width={1000} height={400} isQuiet sortDescriptor={list.sortDescriptor} onSortChange={list.sort} selectedKeys={list.selectedKeys} onSelectionChange={list.setSelectedKeys}> | |
| <TableHeader> | |
| <Column key="score" defaultWidth={100} allowsResizing={isResizable} allowsSorting>Score</Column> | |
| <Column key="title" isRowHeader allowsResizing={isResizable} allowsSorting>Title</Column> | |
| <Column key="author" defaultWidth={200} allowsResizing={isResizable} allowsSorting>Author</Column> | |
| <Column key="num_comments" defaultWidth={100} allowsResizing={isResizable} allowsSorting>Comments</Column> | |
| </TableHeader> | |
| <TableBody items={list.items} loadingState={list.loadingState} onLoadMore={list.loadMore}> | |
| {item => | |
| (<Row key={item.data.id}> | |
| {key => | |
| key === 'title' | |
| ? <Cell textValue={item.data.title}><Link isQuiet><a href={item.data.url} target="_blank">{item.data.title}</a></Link></Cell> | |
| : <Cell>{item.data[key]}</Cell> | |
| } | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| </div> | |
| ); | |
| } | |
| let COLUMNS = [ | |
| { | |
| name: 'Name', | |
| key: 'name', | |
| minWidth: 200 | |
| }, | |
| { | |
| name: 'Owner', | |
| key: 'ownerName' | |
| } | |
| ]; | |
| async function getCollectionItems(): Promise<any> { | |
| const result = [ | |
| { | |
| id: 'xx', | |
| name: 'abc', | |
| ownerName: 'xx' | |
| }, | |
| { | |
| id: 'aa', | |
| name: 'efg', | |
| ownerName: 'aa' | |
| }, | |
| { | |
| id: 'yy', | |
| name: 'abcd', | |
| ownerName: 'yy' | |
| }, | |
| { | |
| id: 'bb', | |
| name: 'efgh', | |
| ownerName: 'bb' | |
| }, | |
| { | |
| id: 'zz', | |
| name: 'abce', | |
| ownerName: 'zz' | |
| }, | |
| { | |
| id: 'cc', | |
| name: 'efgi', | |
| ownerName: 'cc' | |
| } | |
| ]; | |
| return new Promise((resolve) => { | |
| setTimeout(() => { | |
| resolve(result); | |
| }, 5); | |
| }); | |
| } | |
| function ProjectListTable() { | |
| interface Item { | |
| id: string, | |
| name: string, | |
| ownerName: string | |
| } | |
| let {contains} = useFilter({sensitivity: 'base'}); | |
| let [filterText, setFilterText] = React.useState(''); | |
| let list = useAsyncList<Item>({ | |
| async load() { | |
| let projects = await getCollectionItems(); | |
| return {items: projects}; | |
| } | |
| }); | |
| let filteredItems = React.useMemo(() => list.items.filter(item => contains(item.name, filterText)), [list.items, filterText, contains]); | |
| const onChange = (value) => { | |
| setFilterText(value); | |
| }; | |
| return ( | |
| <div> | |
| <SearchField | |
| marginStart={'size-200'} | |
| marginBottom={'size-200'} | |
| marginTop={'size-200'} | |
| width={'size-3600'} | |
| aria-label={'Search by name'} | |
| value={filterText} | |
| onChange={(onChange)} /> | |
| <View flexGrow={1} height={700} overflow="hidden"> | |
| <TableView | |
| aria-label={'Project list'} | |
| height={'100%'} | |
| isQuiet | |
| sortDescriptor={list.sortDescriptor} | |
| onSortChange={list.sort}> | |
| <TableHeader columns={COLUMNS}> | |
| {(column) => { | |
| const {name, ...columnProps} = column; | |
| return <Column {...columnProps}>{name}</Column>; | |
| }} | |
| </TableHeader> | |
| <TableBody | |
| items={filteredItems} | |
| loadingState={list.loadingState}> | |
| {(item) => ( | |
| <Row key={item.id}>{(key) => <Cell>{item[key]}</Cell>}</Row> | |
| )} | |
| </TableBody> | |
| </TableView> | |
| </View> | |
| </div> | |
| ); | |
| } | |
| function AsyncServerFilterTable(props) { | |
| interface Item { | |
| name: string, | |
| height: string, | |
| mass: string | |
| } | |
| let columns = [ | |
| { | |
| name: 'Name', | |
| key: 'name', | |
| minWidth: 200 | |
| }, | |
| { | |
| name: 'Height', | |
| key: 'height' | |
| }, | |
| { | |
| name: 'Mass', | |
| key: 'mass' | |
| } | |
| ]; | |
| let list = useAsyncList<Item>({ | |
| getKey: (item) => item.name, | |
| async load({signal, cursor, filterText}) { | |
| if (cursor) { | |
| cursor = cursor.replace(/^http:\/\//i, 'https://'); | |
| } | |
| let res = await fetch(cursor || `https://swapi.py4e.com/api/people/?search=${filterText}`, {signal}); | |
| let json = await res.json(); | |
| return { | |
| items: json.results, | |
| cursor: json.next | |
| }; | |
| } | |
| }); | |
| const onChange = (value) => { | |
| list.setFilterText(value); | |
| }; | |
| return ( | |
| <div> | |
| <SearchField | |
| marginStart={'size-200'} | |
| marginBottom={'size-200'} | |
| marginTop={'size-200'} | |
| width={'size-3600'} | |
| aria-label={'Search by name'} | |
| defaultValue={list.filterText} | |
| onChange={(onChange)} /> | |
| <TableView | |
| aria-label={'Star Wars Characters'} | |
| height={200} | |
| width={600} | |
| isQuiet | |
| sortDescriptor={list.sortDescriptor} | |
| onSortChange={list.sort} | |
| {...props}> | |
| <TableHeader columns={columns}> | |
| {(column) => { | |
| const {name, ...columnProps} = column; | |
| return <Column {...columnProps}>{name}</Column>; | |
| }} | |
| </TableHeader> | |
| <TableBody | |
| items={list.items} | |
| loadingState={list.loadingState} | |
| onLoadMore={list.loadMore}> | |
| {(item) => ( | |
| <Row key={item.name}>{(key) => <Cell>{item[key]}</Cell>}</Row> | |
| )} | |
| </TableBody> | |
| </TableView> | |
| </div> | |
| ); | |
| } | |
| function ChangableSelectionMode() { | |
| let [selectionMode, setSelectionMode] = useState('none' as SelectionMode); | |
| let [selectedKeys, setSelectedKeys] = React.useState(new Set([]) as 'all' | Iterable<Key>); | |
| return ( | |
| <Flex direction="column" flexGrow={1} maxWidth="size-6000"> | |
| <RadioGroup defaultValue="none" onChange={(value: SelectionMode) => setSelectionMode(value)} label="Selection Mode"> | |
| <Radio value="multiple">Multiple</Radio> | |
| <Radio value="single">Single</Radio> | |
| <Radio value="none">None</Radio> | |
| </RadioGroup> | |
| <TableView overflowMode="wrap" selectionMode={selectionMode} selectedKeys={selectedKeys} aria-label="TableView with controlled selection" width="100%" height="100%" onSelectionChange={setSelectedKeys}> | |
| <TableHeader columns={columns}> | |
| {column => <Column>{column.name}</Column>} | |
| </TableHeader> | |
| <TableBody items={items}> | |
| {item => | |
| (<Row key={item.foo}> | |
| {key => <Cell>{item[key]}</Cell>} | |
| </Row>) | |
| } | |
| </TableBody> | |
| </TableView> | |
| </Flex> | |
| ); | |
| } | |
| export function TableWithBreadcrumbs() { | |
| const fs = [ | |
| {key: 'a', name: 'Folder A', type: 'folder'}, | |
| {key: 'b', name: 'File B', value: '10 MB'}, | |
| {key: 'c', name: 'File C', value: '10 MB', parent: 'a'}, | |
| {key: 'd', name: 'File D', value: '10 MB', parent: 'a'} | |
| ]; | |
| const [loadingState, setLoadingState] = useState<LoadingState>('idle' as 'idle'); | |
| const [selection, setSelection] = useState<'all' | Iterable<Key>>(new Set([])); | |
| const [items, setItems] = useState(() => fs.filter(item => !item.parent)); | |
| const changeFolder = (folder) => { | |
| setItems([]); | |
| setLoadingState('loading' as 'loading'); | |
| // mimic loading behavior | |
| setTimeout(() => { | |
| setLoadingState('idle'); | |
| setItems(fs.filter(item => folder ? item.parent === folder : !item.parent)); | |
| }, 700); | |
| setSelection(new Set([])); | |
| }; | |
| return ( | |
| <Flex direction="column" width="400px"> | |
| <div>The TableView should not error if row selection changes due to items changes from external navigation (breadcrumbs).</div> | |
| <Breadcrumbs | |
| onAction={item => { | |
| if (item === 'root') { | |
| changeFolder(''); | |
| } | |
| }}> | |
| <Item key="root">root</Item> | |
| <Item>a</Item> | |
| </Breadcrumbs> | |
| <TableView | |
| width="400px" | |
| aria-label="table" | |
| selectedKeys={selection} | |
| selectionMode="multiple" | |
| onSelectionChange={(sel) => setSelection(sel)}> | |
| <TableHeader> | |
| <Column key="name" isRowHeader>Name</Column> | |
| <Column key="value">Value</Column> | |
| </TableHeader> | |
| <TableBody items={items} loadingState={loadingState}> | |
| {(item) => ( | |
| <Row key={item.key}> | |
| {(column) => { | |
| if (item.type === 'folder' && column === 'name') { | |
| return ( | |
| <Cell textValue={item[column]}> | |
| <Link onPress={() => changeFolder(item.key)}> | |
| {item[column]} | |
| </Link> | |
| </Cell> | |
| ); | |
| } | |
| return <Cell>{item[column]}</Cell>; | |
| }} | |
| </Row> | |
| )} | |
| </TableBody> | |
| </TableView> | |
| <ActionButton onPress={() => setSelection(items.some(item => item.key === 'd') ? new Set(['d']) : new Set([]))}>Select D</ActionButton> | |
| </Flex> | |
| ); | |
| } |