Created
August 5, 2020 22:21
-
-
Save tomsontom/8e850a1f7197494a33770e255e4aa9d3 to your computer and use it in GitHub Desktop.
ListBox.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
/* | |
* 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 AlignCenter from '@spectrum-icons/workflow/AlignCenter'; | |
import AlignLeft from '@spectrum-icons/workflow/AlignLeft'; | |
import AlignRight from '@spectrum-icons/workflow/AlignRight'; | |
import Blower from '@spectrum-icons/workflow/Blower'; | |
import Book from '@spectrum-icons/workflow/Book'; | |
import Copy from '@spectrum-icons/workflow/Copy'; | |
import Cut from '@spectrum-icons/workflow/Cut'; | |
import {Item, ListBox, Section} from '../'; | |
import {Label} from '@react-spectrum/label'; | |
import Paste from '@spectrum-icons/workflow/Paste'; | |
import React, {useEffect} from 'react'; | |
import {storiesOf} from '@storybook/react'; | |
import {Text} from '@react-spectrum/text'; | |
import {useAsyncList} from '@react-stately/data'; | |
import { Provider } from '@react-spectrum/provider'; | |
import { defaultTheme, Flex, Button } from '@adobe/react-spectrum'; | |
let iconMap = { | |
AlignCenter, | |
AlignLeft, | |
AlignRight, | |
Blower, | |
Book, | |
Copy, | |
Cut, | |
Paste | |
}; | |
let hardModeProgrammatic = [ | |
{name: 'Section 1', children: [ | |
{name: 'Copy', icon: 'Copy'}, | |
{name: 'Cut', icon: 'Cut'}, | |
{name: 'Paste', icon: 'Paste'} | |
]}, | |
{name: 'Section 2', children: [ | |
{name: 'Puppy', icon: 'AlignLeft'}, | |
{name: 'Doggo', icon: 'AlignCenter'}, | |
{name: 'Floof', icon: 'AlignRight'} | |
]} | |
]; | |
let flatOptions = [ | |
{name: 'Aardvark'}, | |
{name: 'Kangaroo'}, | |
{name: 'Snake'}, | |
{name: 'Danni'}, | |
{name: 'Devon'}, | |
{name: 'Ross'}, | |
{name: 'Puppy'}, | |
{name: 'Doggo'}, | |
{name: 'Floof'} | |
]; | |
let withSection = [ | |
{name: 'Animals', children: [ | |
{name: 'Aardvark'}, | |
{name: 'Kangaroo'}, | |
{name: 'Snake'} | |
]}, | |
{name: 'People', children: [ | |
{name: 'Danni'}, | |
{name: 'Devon'}, | |
{name: 'Ross'} | |
]} | |
]; | |
let lotsOfSections: any[] = []; | |
for (let i = 0; i < 50; i++) { | |
let children = []; | |
for (let j = 0; j < 50; j++) { | |
children.push({name: `Section ${i}, Item ${j}`}); | |
} | |
lotsOfSections.push({name: 'Section ' + i, children}); | |
} | |
storiesOf('ListBox', module) | |
.addDecorator(story => ( | |
<div style={{display: 'flex', flexDirection: 'column'}}> | |
<Label id="label">Choose an item</Label> | |
<div style={{display: 'flex', background: 'var(--spectrum-global-color-gray-50)', border: '1px solid lightgray', maxHeight: 300}}> | |
{story()} | |
</div> | |
</div> | |
)) | |
.add( | |
'Default ListBox', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" items={flatOptions}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</ListBox> | |
) | |
) | |
.add( | |
'ListBox w/ sections', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" items={withSection}> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'ListBox w/ many sections and selection', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" selectionMode="multiple" items={lotsOfSections} onSelectionChange={action('onSelectionChange')}> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{(item: any) => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'ListBox w/ sections and no title', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" items={withSection}> | |
{item => ( | |
<Section key={item.name} items={item.children} aria-label={item.name}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'Static', | |
() => ( | |
<ListBox width={200} aria-labelledby="label"> | |
<Item>One</Item> | |
<Item>Two</Item> | |
<Item>Three</Item> | |
</ListBox> | |
) | |
) | |
.add( | |
'Static with sections and selection', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" selectionMode="multiple"> | |
<Section title="Section 1"> | |
<Item>One</Item> | |
<Item>Two</Item> | |
<Item>Three</Item> | |
</Section> | |
<Section title="Section 2"> | |
<Item>One</Item> | |
<Item>Two</Item> | |
<Item>Three</Item> | |
</Section> | |
</ListBox> | |
) | |
) | |
.add( | |
'Static with sections and no title', | |
() => ( | |
<ListBox width={200} aria-labelledby="label"> | |
<Section aria-label="Section 1"> | |
<Item>One</Item> | |
<Item>Two</Item> | |
<Item>Three</Item> | |
</Section> | |
<Section aria-label="Section 2"> | |
<Item>One</Item> | |
<Item>Two</Item> | |
<Item>Three</Item> | |
</Section> | |
</ListBox> | |
) | |
) | |
.add( | |
'with default selected option', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" selectionMode="multiple" onSelectionChange={action('onSelectionChange')} items={withSection} defaultSelectedKeys={['Kangaroo']}> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'single selection with default selected option', | |
() => ( | |
<ListBox selectionMode="single" onSelectionChange={action('onSelectionChange')} width={200} aria-labelledby="label" items={flatOptions} defaultSelectedKeys={['Kangaroo']}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</ListBox> | |
) | |
) | |
.add( | |
'static with default selected options', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" selectionMode="multiple" onSelectionChange={action('onSelectionChange')} defaultSelectedKeys={['2', '3']}> | |
<Section title="Section 1"> | |
<Item key="1"> | |
One | |
</Item> | |
<Item key="2"> | |
Two | |
</Item> | |
<Item key="3"> | |
Three | |
</Item> | |
</Section> | |
<Section title="Section 2"> | |
<Item key="4"> | |
Four | |
</Item> | |
<Item key="5"> | |
Five | |
</Item> | |
<Item key="6"> | |
Six | |
</Item> | |
<Item key="7"> | |
Seven | |
</Item> | |
</Section> | |
</ListBox> | |
) | |
) | |
.add( | |
'with selected options (controlled)', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" selectionMode="multiple" onSelectionChange={action('onSelectionChange')} items={withSection} selectedKeys={['Kangaroo']}> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'static with selected options (controlled)', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" selectionMode="multiple" onSelectionChange={action('onSelectionChange')} selectedKeys={['2']}> | |
<Section title="Section 1"> | |
<Item key="1"> | |
One | |
</Item> | |
<Item key="2"> | |
Two | |
</Item> | |
<Item key="3"> | |
Three | |
</Item> | |
</Section> | |
<Section title="Section 2"> | |
<Item key="4"> | |
Four | |
</Item> | |
<Item key="5"> | |
Five | |
</Item> | |
<Item key="6"> | |
Six | |
</Item> | |
<Item key="7"> | |
Seven | |
</Item> | |
</Section> | |
</ListBox> | |
) | |
) | |
.add( | |
'with disabled options', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" items={withSection} disabledKeys={['Kangaroo', 'Ross']}> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'static with disabled options', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" disabledKeys={['3', '5']}> | |
<Section title="Section 1"> | |
<Item key="1"> | |
One | |
</Item> | |
<Item key="2"> | |
Two | |
</Item> | |
<Item key="3"> | |
Three | |
</Item> | |
</Section> | |
<Section title="Section 2"> | |
<Item key="4"> | |
Four | |
</Item> | |
<Item key="5"> | |
Five | |
</Item> | |
<Item key="6"> | |
Six | |
</Item> | |
<Item key="7"> | |
Seven | |
</Item> | |
</Section> | |
</ListBox> | |
) | |
) | |
.add( | |
'Multiple selection', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" items={withSection} onSelectionChange={action('onSelectionChange')} selectionMode="multiple" defaultSelectedKeys={['Aardvark', 'Snake']} disabledKeys={['Kangaroo', 'Ross']}> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'Multiple selection, static', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" onSelectionChange={action('onSelectionChange')} selectionMode="multiple" defaultSelectedKeys={['2', '5']} disabledKeys={['1', '3']}> | |
<Section title="Section 1"> | |
<Item key="1"> | |
One | |
</Item> | |
<Item key="2"> | |
Two | |
</Item> | |
<Item key="3"> | |
Three | |
</Item> | |
</Section> | |
<Section title="Section 2"> | |
<Item key="4"> | |
Four | |
</Item> | |
<Item key="5"> | |
Five | |
</Item> | |
<Item key="6"> | |
Six | |
</Item> | |
</Section> | |
</ListBox> | |
) | |
) | |
.add( | |
'No selection allowed', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" items={withSection}> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'No selection allowed, static', | |
() => ( | |
<ListBox width={200} aria-labelledby="label"> | |
<Section title="Section 1"> | |
<Item>One</Item> | |
<Item>Two</Item> | |
<Item>Three</Item> | |
</Section> | |
<Section title="Section 2"> | |
<Item>Four</Item> | |
<Item>Five</Item> | |
<Item>Six</Item> | |
</Section> | |
</ListBox> | |
) | |
) | |
.add( | |
'ListBox with autoFocus=true', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" items={withSection} autoFocus> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'ListBox with autoFocus=true, selectionMode=single, default selected key (uncontrolled)', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" items={withSection} autoFocus defaultSelectedKeys={['Snake']} selectionMode="single"> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'ListBox with autoFocus="first"', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" items={withSection} selectionMode="multiple" onSelectionChange={action('onSelectionChange')} autoFocus="first"> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'ListBox with autoFocus="last"', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" items={withSection} selectionMode="multiple" onSelectionChange={action('onSelectionChange')} autoFocus="last"> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'ListBox with keyboard selection wrapping', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" items={withSection} selectionMode="multiple" onSelectionChange={action('onSelectionChange')} shouldFocusWrap> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'with semantic elements (static)', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" selectionMode="multiple" onSelectionChange={action('onSelectionChange')}> | |
<Section title="Section 1"> | |
<Item textValue="Copy"> | |
<Copy size="S" /> | |
<Text>Copy</Text> | |
</Item> | |
<Item textValue="Cut"> | |
<Cut size="S" /> | |
<Text>Cut</Text> | |
</Item> | |
<Item textValue="Paste"> | |
<Paste size="S" /> | |
<Text>Paste</Text> | |
</Item> | |
</Section> | |
<Section title="Section 2"> | |
<Item textValue="Puppy"> | |
<AlignLeft size="S" /> | |
<Text>Puppy</Text> | |
<Text slot="description">Puppy description super long as well geez</Text> | |
</Item> | |
<Item textValue="Doggo with really really really long long long text"> | |
<AlignCenter size="S" /> | |
<Text>Doggo with really really really long long long text</Text> | |
</Item> | |
<Item textValue="Floof"> | |
<AlignRight size="S" /> | |
<Text>Floof</Text> | |
</Item> | |
<Item> | |
Basic Item | |
</Item> | |
</Section> | |
</ListBox> | |
) | |
) | |
.add( | |
'with semantic elements (generative), multiple selection', | |
() => ( | |
<ListBox width={200} aria-labelledby="label" items={hardModeProgrammatic} onSelectionChange={action('onSelectionChange')} selectionMode="multiple"> | |
{item => ( | |
<Section key={item.name} items={item.children} title={item.name}> | |
{item => customOption(item)} | |
</Section> | |
)} | |
</ListBox> | |
) | |
) | |
.add( | |
'isLoading', | |
() => ( | |
<ListBox aria-labelledby="label" width={200} items={[]} isLoading> | |
{item => <Item>{item.name}</Item>} | |
</ListBox> | |
) | |
) | |
.add( | |
'isLoading more', | |
() => ( | |
<ListBox aria-labelledby="label" width={200} items={flatOptions} isLoading> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</ListBox> | |
) | |
) | |
.add( | |
'async loading', | |
() => ( | |
<AsyncLoadingExample /> | |
) | |
).add( | |
'bug', | |
() => (<App />) | |
); | |
let customOption = (item) => { | |
let Icon = iconMap[item.icon]; | |
return ( | |
<Item textValue={item.name} key={item.name}> | |
{item.icon && <Icon size="S" />} | |
<Text>{item.name}</Text> | |
</Item> | |
); | |
}; | |
function AsyncLoadingExample() { | |
interface Pokemon { | |
name: string, | |
url: string | |
} | |
let list = useAsyncList<Pokemon>({ | |
async load({signal, cursor}) { | |
let res = await fetch(cursor || 'https://pokeapi.co/api/v2/pokemon', {signal}); | |
let json = await res.json(); | |
return { | |
items: json.results, | |
cursor: json.next | |
}; | |
} | |
}); | |
return ( | |
<ListBox aria-labelledby="label" width={200} items={list.items} isLoading={list.isLoading} onLoadMore={list.loadMore}> | |
{item => <Item key={item.name}>{item.name}</Item>} | |
</ListBox> | |
); | |
} | |
let itemsForDemo = Array.from(new Array(100)).map((val, index) => ({val, index})); | |
function App() { | |
const containerRef = React.createRef<HTMLDivElement>() | |
const listContainerRef = React.createRef(); | |
const toggleSize = () => { | |
console.log("Toggle it", containerRef.current?.style.height); | |
if( containerRef.current ) { | |
if( containerRef.current.style.height === "700px" ) { | |
containerRef.current.style.height = "300px"; | |
console.log("SMALLER") | |
} else { | |
containerRef.current.style.height = "700px"; | |
} | |
} | |
} | |
return ( | |
<Provider theme={defaultTheme}> | |
<div ref={containerRef} style={{ display: "flex", height: "700px", borderStyle: "solid", borderWidth: "2px", borderColor: "black", padding: "10px", overflow: "hidden"}}> | |
<Flex maxHeight="300px"> | |
<Text>Max-Height: 300px</Text> | |
<ListBox minWidth="150px" items={itemsForDemo}> | |
{ item => { | |
console.log(item); | |
return ( | |
<Item key={item.index}> | |
<Text>IDX: {item.index}</Text> | |
</Item> | |
); | |
}} | |
</ListBox> | |
</Flex> | |
<Flex> | |
<Text>None</Text> | |
<ListBox minWidth="150px" items={itemsForDemo}> | |
{ item => { | |
return ( | |
<Item key={item.index}> | |
<Text>IDX: {item.index}</Text> | |
</Item> | |
); | |
}} | |
</ListBox> | |
</Flex> | |
<Flex maxHeight="700px"> | |
<Text>Max-Height: 700px</Text> | |
<ListBox minWidth="150px" items={itemsForDemo}> | |
{ item => { | |
return ( | |
<Item key={item.index}> | |
<Text>IDX: {item.index}</Text> | |
</Item> | |
); | |
}} | |
</ListBox> | |
</Flex> | |
<Flex maxHeight="100%"> | |
<Text>MaxHeight: 100%</Text> | |
<ListBox minWidth="150px" items={itemsForDemo}> | |
{ item => { | |
return ( | |
<Item key={item.index}> | |
<Text>IDX: {item.index}</Text> | |
</Item> | |
); | |
}} | |
</ListBox> | |
</Flex> | |
<Flex height="700px"> | |
<Text>Height: 700px</Text> | |
<ListBox minWidth="150px" items={itemsForDemo}> | |
{ item => { | |
return ( | |
<Item key={item.index}> | |
<Text>IDX: {item.index}</Text> | |
</Item> | |
); | |
}} | |
</ListBox> | |
</Flex> | |
<Flex height="100%"> | |
<Text>Height: 100%</Text> | |
<ListBox minWidth="150px" items={itemsForDemo}> | |
{ item => { | |
return ( | |
<Item key={item.index}> | |
<Text>IDX: {item.index}</Text> | |
</Item> | |
); | |
}} | |
</ListBox> | |
</Flex> | |
{/* <Flex ref={listContainerRef}> | |
<Text>ObserverAdjusted</Text> | |
<ListBox minWidth="150px" items={itemsForDemo}> | |
{ item => { | |
return ( | |
<Item key={item.index}> | |
<Text>IDX: {item.index}</Text> | |
</Item> | |
); | |
}} | |
</ListBox> | |
</Flex> */} | |
</div> | |
<Button variant="primary" onPress={toggleSize}> Toggle Size</Button> | |
</Provider> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment