-
Notifications
You must be signed in to change notification settings - Fork 189
feat: TreeView #3477
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
feat: TreeView #3477
Changes from all commits
Commits
Show all changes
63 commits
Select commit
Hold shift + click to select a range
0047299
feat: initial commit
cansuaa 5f8ee38
fix a11y test findings
cansuaa f8148f4
update test utils snapshots
cansuaa a52e05f
test fixes
cansuaa 1863bbe
test fix
cansuaa 25a97ab
updates
cansuaa e8d8042
Merge branch 'main' into cansua/treeview
cansuaa debfb65
updates
cansuaa e0336ce
Merge branch 'main' into cansua/treeview
cansuaa b66fdcd
update snapshot
cansuaa 887fc01
updates to interface, dev pages and added tests
cansuaa 7cb3e78
change name from Treeview to TreeView
cansuaa 60f1d79
Merge branch 'main' into cansua/treeview
cansuaa ea2bb74
change page folder name and add a11y tests
cansuaa 03e7bd1
display grid and structured item transition
cansuaa 93bf261
test utils update
cansuaa 7dad45f
renderItemToggleIcon
cansuaa 93a709e
Merge branch 'main' into cansua/treeview
cansuaa 5aeac36
test util update
cansuaa b721cca
remove connector lines
cansuaa d63cc11
adjust focus offset of toggle
cansuaa 20520ce
use internal ExpandToggle in table and update snapshots
cansuaa 4faa0de
Merge branch 'main' into cansua/treeview
cansuaa 457bef8
cleanup
cansuaa 68ce238
remove role group from ul
cansuaa 01e8b39
rename dev page
cansuaa e21a09d
update API and test util descriptions
cansuaa 45f6025
addressed comments
cansuaa 13fd9a0
address comments and add tree-view to the required props list
cansuaa d17ad91
i18n translations
cansuaa 4414810
move dev page items to a different folder
cansuaa 61f01ca
add a permutations dev page
cansuaa 3d956e3
update structured item
cansuaa d7d054f
update treeitem styles and move them to a separate file
cansuaa f150c78
enable renderItemToggleIcon for both core and console
cansuaa 36b320a
make texts longer in permutation page
cansuaa 6abdd5b
change icon for the custom icon with slow animation toggle icon type
cansuaa d16d67a
update heading in permutations dev page
cansuaa 6ed1222
Merge branch 'main' into cansua/treeview
cansuaa aa44c4c
add integ test
cansuaa b4a7192
remove with-motion mixin from dev page styles and move rule to only a…
cansuaa cefe3b9
Revert "i18n translations" to add them to its own PR
cansuaa 60c2773
updates in dev pages
cansuaa 432e32c
Merge branch 'main' into cansua/treeview
cansuaa d68c485
remove duplicate tree-view entry in messages-types
cansuaa 54f0836
add aria labels to the icon buttons
cansuaa 4c558ca
move padding to structured item wrapper from li
cansuaa 0911ee9
add announcementLabel for labeling toggle button
cansuaa 30776fb
add unique aria labels to action buttons
cansuaa 07def34
align toggle
cansuaa ee4d91f
Merge branch 'main' into cansua/treeview
cansuaa 53dc91b
add a dev page with 9 levels of nesting and horizontal scroll
cansuaa 17a9110
Merge branch 'main' into cansua/treeview
cansuaa fd6bc6c
remove empty structured item tests
cansuaa db02662
remove tabindex from horizontal scroll page and other small changes
cansuaa d5644fd
disable actions wrapping for tree item and fix a11y test failure
cansuaa 82ba63a
re-add removed line
cansuaa 3916e45
addressed comments
cansuaa 1b66895
update items description, and announcement label
cansuaa d9560a5
fix renderItemToggleIcon documentation issue
cansuaa 4a8c042
remove hasLargeFocusOffset
cansuaa c164f3e
Merge branch 'main' into cansua/treeview
cansuaa 11cd9ed
update horizontal scrolling and make wrapper focusable
cansuaa 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
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,112 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
import React, { useState } from 'react'; | ||
import clsx from 'clsx'; | ||
|
||
import Box from '~components/box'; | ||
import Container from '~components/container'; | ||
import FormField from '~components/form-field'; | ||
import Grid from '~components/grid'; | ||
import Icon from '~components/icon'; | ||
import Select, { SelectProps } from '~components/select'; | ||
import TreeView, { TreeViewProps } from '~components/tree-view'; | ||
|
||
import ScreenshotArea from '../utils/screenshot-area'; | ||
import { Actions } from './common'; | ||
import { items } from './items/basic-page-items'; | ||
|
||
import styles from './styles.scss'; | ||
|
||
export default function BasicTreeView() { | ||
const [expandedItems, setExpandedItems] = useState<Array<string>>(['1', '4.1']); | ||
const [toggleIconType, setToggleIconType] = useState<SelectProps.Option>({ | ||
label: 'Default', | ||
value: 'default', | ||
}); | ||
|
||
const renderItemToggleIcon = ({ expanded }: TreeViewProps.ItemToggleRenderIconData) => { | ||
if (toggleIconType.value === 'custom') { | ||
return <Icon size="small" name={expanded ? 'treeview-collapse' : 'treeview-expand'} ariaLabel="Toggle" />; | ||
} | ||
|
||
if (toggleIconType.value === 'custom-with-slow-animation') { | ||
return ( | ||
<Icon | ||
size="small" | ||
name="angle-down" | ||
className={clsx(styles.animation, expanded && styles['animation-expanded'])} | ||
/> | ||
); | ||
} | ||
}; | ||
|
||
return ( | ||
<ScreenshotArea> | ||
<h1>Basic tree view</h1> | ||
|
||
<Grid gridDefinition={[{ colspan: { m: 7, xs: 12 } }]}> | ||
<div> | ||
<FormField label="Toggle icon" stretch={true}> | ||
<Select | ||
selectedOption={toggleIconType} | ||
onChange={({ detail }) => setToggleIconType(detail.selectedOption)} | ||
options={[ | ||
{ | ||
label: 'Default', | ||
value: 'default', | ||
}, | ||
{ | ||
label: 'Custom', | ||
value: 'custom', | ||
}, | ||
{ | ||
label: 'Custom with slow animation', | ||
value: 'custom-with-slow-animation', | ||
}, | ||
]} | ||
/> | ||
</FormField> | ||
|
||
<br /> | ||
|
||
<Container> | ||
<TreeView | ||
ariaLabel="Basic tree view" | ||
items={items} | ||
renderItem={item => { | ||
return { | ||
icon: item.hideIcon ? undefined : ( | ||
<Icon name={expandedItems.includes(item.id) ? 'folder-open' : 'folder'} ariaLabel="folder" /> | ||
), | ||
content: item.content, | ||
secondaryContent: item.details && <Box color="text-status-inactive">{item.details}</Box>, | ||
actions: item.hasActions ? ( | ||
<Actions | ||
actionType="inline-button-dropdown" | ||
itemLabel={item.announcementLabel ?? (item.content as string)} | ||
/> | ||
) : undefined, | ||
}; | ||
}} | ||
getItemId={item => item.id} | ||
getItemChildren={item => item.children} | ||
onItemToggle={({ detail }: any) => | ||
setExpandedItems(prev => | ||
detail.expanded ? [...prev, detail.item.id] : prev.filter(id => id !== detail.item.id) | ||
) | ||
} | ||
expandedItems={expandedItems} | ||
i18nStrings={{ | ||
expandButtonLabel: () => 'Expand item', | ||
collapseButtonLabel: () => 'Collapse item', | ||
}} | ||
renderItemToggleIcon={renderItemToggleIcon} | ||
/> | ||
</Container> | ||
</div> | ||
</Grid> | ||
|
||
<div style={{ marginTop: '10px' }}>Expanded items: {expandedItems.map(id => `Item ${id}`).join(', ')}</div> | ||
</ScreenshotArea> | ||
); | ||
} |
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,134 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
import React, { useState } from 'react'; | ||
|
||
import { SpaceBetween } from '~components'; | ||
import Box from '~components/box'; | ||
import Button from '~components/button'; | ||
import ButtonDropdown from '~components/button-dropdown'; | ||
import ButtonGroup from '~components/button-group'; | ||
import StatusIndicator from '~components/status-indicator/internal'; | ||
|
||
import { Item } from './items/dynamic-items'; | ||
|
||
export function Content(item: Item) { | ||
if (item.status) { | ||
return <StatusIndicator type={item.status}>{item.name}</StatusIndicator>; | ||
} | ||
|
||
return ( | ||
<div style={{ display: 'flex', gap: '10px' }}> | ||
{item.name} | ||
|
||
{item.errorCount && item.errorCount > 0 && <StatusIndicator type="error">{item.errorCount}</StatusIndicator>} | ||
{item.warningCount && item.warningCount > 0 && ( | ||
<StatusIndicator type="warning">{item.warningCount}</StatusIndicator> | ||
)} | ||
{item.successCount && item.successCount > 0 && ( | ||
<StatusIndicator type="success">{item.successCount}</StatusIndicator> | ||
)} | ||
</div> | ||
); | ||
} | ||
|
||
export function Actions( | ||
{ | ||
actionType, | ||
itemLabel, | ||
}: { | ||
actionType?: 'button-group' | 'button-dropdown' | 'inline-button-dropdown' | 'text' | 'custom-inline-button-group'; | ||
itemLabel?: string; | ||
} = { | ||
actionType: 'inline-button-dropdown', | ||
} | ||
) { | ||
const [markedAsFavorite, setMarkedAsFavorite] = useState(false); | ||
|
||
if (actionType === 'text') { | ||
return <Box color="text-status-inactive">Some metadata</Box>; | ||
} | ||
|
||
if (actionType === 'button-group') { | ||
return ( | ||
<ButtonGroup | ||
variant="icon" | ||
items={[ | ||
{ | ||
id: 'settings', | ||
iconName: 'settings', | ||
type: 'icon-button', | ||
text: 'Settings', | ||
}, | ||
{ | ||
type: 'icon-toggle-button', | ||
id: 'favorite', | ||
text: 'Favorite', | ||
pressed: markedAsFavorite, | ||
iconName: 'star', | ||
pressedIconName: 'star-filled', | ||
}, | ||
{ | ||
id: 'menu', | ||
type: 'menu-dropdown', | ||
text: 'Menu', | ||
items: [ | ||
{ id: 'start', text: 'Start' }, | ||
{ id: 'stop', text: 'Stop', disabled: true }, | ||
{ | ||
id: 'hibernate', | ||
text: 'Hibernate', | ||
disabled: true, | ||
}, | ||
{ id: 'reboot', text: 'Reboot', disabled: true }, | ||
{ id: 'terminate', text: 'Terminate' }, | ||
], | ||
}, | ||
]} | ||
onItemClick={({ detail }) => { | ||
if (detail.id === 'favorite') { | ||
setMarkedAsFavorite(!markedAsFavorite); | ||
} | ||
}} | ||
ariaLabel={itemLabel} | ||
/> | ||
); | ||
} | ||
|
||
const buttonDropdownItems = [ | ||
{ id: 'start', text: 'Start' }, | ||
{ id: 'stop', text: 'Stop', disabled: true }, | ||
{ | ||
id: 'hibernate', | ||
text: 'Hibernate', | ||
disabled: true, | ||
}, | ||
{ id: 'reboot', text: 'Reboot', disabled: true }, | ||
{ id: 'terminate', text: 'Terminate' }, | ||
]; | ||
|
||
if (actionType === 'custom-inline-button-group') { | ||
return ( | ||
<SpaceBetween direction="horizontal" size="s"> | ||
<Button variant="inline-icon" iconName="settings" ariaLabel={`Settings for ${itemLabel}`} /> | ||
<Button variant="inline-icon" iconName="star" ariaLabel={`Favorite ${itemLabel}`} /> | ||
<ButtonDropdown | ||
items={buttonDropdownItems} | ||
ariaLabel={`Control instance for ${itemLabel}`} | ||
variant="inline-icon" | ||
/> | ||
</SpaceBetween> | ||
); | ||
} | ||
|
||
if (actionType === 'inline-button-dropdown') { | ||
return ( | ||
<ButtonDropdown | ||
items={buttonDropdownItems} | ||
ariaLabel={`Control instance for ${itemLabel}`} | ||
variant="inline-icon" | ||
/> | ||
); | ||
} | ||
|
||
return <ButtonDropdown items={buttonDropdownItems} ariaLabel={`Control instance for ${itemLabel}`} variant="icon" />; | ||
} |
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,87 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
import React, { useState } from 'react'; | ||
|
||
import { Button, SpaceBetween } from '~components'; | ||
import Box from '~components/box'; | ||
import Icon from '~components/icon'; | ||
import TreeView from '~components/tree-view'; | ||
|
||
import { Actions, Content } from './common'; | ||
import { allItems, items } from './items/dynamic-items'; | ||
|
||
const allExpandableItemIds = allItems.filter(item => item.children && item.children.length > 0).map(item => item.id); | ||
|
||
export default function DynamicItemsPage() { | ||
const [expandedItems, setExpandedItems] = useState<Array<string>>([]); | ||
|
||
return ( | ||
<> | ||
<h1>Dynamic items page</h1> | ||
|
||
<SpaceBetween size="s"> | ||
<Button | ||
onClick={() => { | ||
setExpandedItems(allExpandableItemIds); | ||
console.time('expand-all'); | ||
requestAnimationFrame(() => console.timeEnd('expand-all')); | ||
cansuaa marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}} | ||
> | ||
Expand all | ||
</Button> | ||
|
||
<Button | ||
onClick={() => { | ||
setExpandedItems([]); | ||
console.time('collapse-all'); | ||
requestAnimationFrame(() => console.timeEnd('collapse-all')); | ||
}} | ||
> | ||
Collapse all | ||
</Button> | ||
</SpaceBetween> | ||
|
||
<Box padding="xl"> | ||
<TreeView | ||
items={items} | ||
renderItem={item => { | ||
const isExpanded = expandedItems.includes(item.id); | ||
return { | ||
icon: <Icon name={isExpanded ? 'folder-open' : 'folder'} />, | ||
content: <Content {...item} />, | ||
actions: item.hasActions ? <Actions actionType="button-group" itemLabel={item.name} /> : undefined, | ||
secondaryContent: item.tagName ? ( | ||
<Box color="text-status-inactive"> | ||
<SpaceBetween size="xxs" direction="horizontal"> | ||
<Icon name="ticket" /> | ||
<span>{item.tagName}</span> | ||
</SpaceBetween> | ||
</Box> | ||
) : undefined, | ||
}; | ||
}} | ||
getItemId={item => item.id} | ||
getItemChildren={item => item.children} | ||
onItemToggle={({ detail }) => { | ||
if (detail.expanded) { | ||
const logName = `expand-item-${detail.item.name}`; | ||
console.time(logName); | ||
requestAnimationFrame(() => console.timeEnd(logName)); | ||
return setExpandedItems(prev => [...prev, detail.id]); | ||
} else { | ||
const logName = `collapse-item-${detail.item.name}`; | ||
console.time(logName); | ||
requestAnimationFrame(() => console.timeEnd(logName)); | ||
return setExpandedItems(prev => prev.filter(id => id !== detail.id)); | ||
} | ||
}} | ||
expandedItems={expandedItems} | ||
i18nStrings={{ | ||
expandButtonLabel: () => 'Expand item', | ||
collapseButtonLabel: () => 'Collapse item', | ||
}} | ||
/> | ||
</Box> | ||
</> | ||
); | ||
} |
Oops, something went wrong.
Oops, something went wrong.
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.