Skip to content

Commit b0e6186

Browse files
authored
Merge branch 'main' into feat/widgetize-app-layout-skeleton-event-base
2 parents 39140f6 + b1e909b commit b0e6186

39 files changed

+2521
-22
lines changed

.stylelintrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
]
4242
}
4343
],
44-
"@cloudscape-design/no-motion-outside-of-mixin": [true],
4544
"@cloudscape-design/z-index-value-constraint": [true],
4645
"plugin/no-unsupported-browser-features": [
4746
true,
@@ -58,7 +57,8 @@
5857
],
5958
"rules": {
6059
"property-disallowed-list": ["border", "border-radius", "border-style", "margin", "padding"],
61-
"csstools/use-logical": "always"
60+
"csstools/use-logical": "always",
61+
"@cloudscape-design/no-motion-outside-of-mixin": [true]
6262
}
6363
}
6464
],

build-tools/utils/pluralize.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const pluralizationMap = {
8080
ToggleButton: 'ToggleButtons',
8181
TokenGroup: 'TokenGroups',
8282
TopNavigation: 'TopNavigations',
83+
TreeView: 'TreeViews',
8384
TutorialPanel: 'TutorialPanels',
8485
Wizard: 'Wizards',
8586
};

pages/tree-view/basic.page.tsx

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import React, { useState } from 'react';
4+
import clsx from 'clsx';
5+
6+
import Box from '~components/box';
7+
import Container from '~components/container';
8+
import FormField from '~components/form-field';
9+
import Grid from '~components/grid';
10+
import Icon from '~components/icon';
11+
import Select, { SelectProps } from '~components/select';
12+
import TreeView, { TreeViewProps } from '~components/tree-view';
13+
14+
import ScreenshotArea from '../utils/screenshot-area';
15+
import { Actions } from './common';
16+
import { items } from './items/basic-page-items';
17+
18+
import styles from './styles.scss';
19+
20+
export default function BasicTreeView() {
21+
const [expandedItems, setExpandedItems] = useState<Array<string>>(['1', '4.1']);
22+
const [toggleIconType, setToggleIconType] = useState<SelectProps.Option>({
23+
label: 'Default',
24+
value: 'default',
25+
});
26+
27+
const renderItemToggleIcon = ({ expanded }: TreeViewProps.ItemToggleRenderIconData) => {
28+
if (toggleIconType.value === 'custom') {
29+
return <Icon size="small" name={expanded ? 'treeview-collapse' : 'treeview-expand'} ariaLabel="Toggle" />;
30+
}
31+
32+
if (toggleIconType.value === 'custom-with-slow-animation') {
33+
return (
34+
<Icon
35+
size="small"
36+
name="angle-down"
37+
className={clsx(styles.animation, expanded && styles['animation-expanded'])}
38+
/>
39+
);
40+
}
41+
};
42+
43+
return (
44+
<ScreenshotArea>
45+
<h1>Basic tree view</h1>
46+
47+
<Grid gridDefinition={[{ colspan: { m: 7, xs: 12 } }]}>
48+
<div>
49+
<FormField label="Toggle icon" stretch={true}>
50+
<Select
51+
selectedOption={toggleIconType}
52+
onChange={({ detail }) => setToggleIconType(detail.selectedOption)}
53+
options={[
54+
{
55+
label: 'Default',
56+
value: 'default',
57+
},
58+
{
59+
label: 'Custom',
60+
value: 'custom',
61+
},
62+
{
63+
label: 'Custom with slow animation',
64+
value: 'custom-with-slow-animation',
65+
},
66+
]}
67+
/>
68+
</FormField>
69+
70+
<br />
71+
72+
<Container>
73+
<TreeView
74+
ariaLabel="Basic tree view"
75+
items={items}
76+
renderItem={item => {
77+
return {
78+
icon: item.hideIcon ? undefined : (
79+
<Icon name={expandedItems.includes(item.id) ? 'folder-open' : 'folder'} ariaLabel="folder" />
80+
),
81+
content: item.content,
82+
secondaryContent: item.details && <Box color="text-status-inactive">{item.details}</Box>,
83+
actions: item.hasActions ? (
84+
<Actions
85+
actionType="inline-button-dropdown"
86+
itemLabel={item.announcementLabel ?? (item.content as string)}
87+
/>
88+
) : undefined,
89+
};
90+
}}
91+
getItemId={item => item.id}
92+
getItemChildren={item => item.children}
93+
onItemToggle={({ detail }: any) =>
94+
setExpandedItems(prev =>
95+
detail.expanded ? [...prev, detail.item.id] : prev.filter(id => id !== detail.item.id)
96+
)
97+
}
98+
expandedItems={expandedItems}
99+
i18nStrings={{
100+
expandButtonLabel: () => 'Expand item',
101+
collapseButtonLabel: () => 'Collapse item',
102+
}}
103+
renderItemToggleIcon={renderItemToggleIcon}
104+
/>
105+
</Container>
106+
</div>
107+
</Grid>
108+
109+
<div style={{ marginTop: '10px' }}>Expanded items: {expandedItems.map(id => `Item ${id}`).join(', ')}</div>
110+
</ScreenshotArea>
111+
);
112+
}

pages/tree-view/common.tsx

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import React, { useState } from 'react';
4+
5+
import { SpaceBetween } from '~components';
6+
import Box from '~components/box';
7+
import Button from '~components/button';
8+
import ButtonDropdown from '~components/button-dropdown';
9+
import ButtonGroup from '~components/button-group';
10+
import StatusIndicator from '~components/status-indicator/internal';
11+
12+
import { Item } from './items/dynamic-items';
13+
14+
export function Content(item: Item) {
15+
if (item.status) {
16+
return <StatusIndicator type={item.status}>{item.name}</StatusIndicator>;
17+
}
18+
19+
return (
20+
<div style={{ display: 'flex', gap: '10px' }}>
21+
{item.name}
22+
23+
{item.errorCount && item.errorCount > 0 && <StatusIndicator type="error">{item.errorCount}</StatusIndicator>}
24+
{item.warningCount && item.warningCount > 0 && (
25+
<StatusIndicator type="warning">{item.warningCount}</StatusIndicator>
26+
)}
27+
{item.successCount && item.successCount > 0 && (
28+
<StatusIndicator type="success">{item.successCount}</StatusIndicator>
29+
)}
30+
</div>
31+
);
32+
}
33+
34+
export function Actions(
35+
{
36+
actionType,
37+
itemLabel,
38+
}: {
39+
actionType?: 'button-group' | 'button-dropdown' | 'inline-button-dropdown' | 'text' | 'custom-inline-button-group';
40+
itemLabel?: string;
41+
} = {
42+
actionType: 'inline-button-dropdown',
43+
}
44+
) {
45+
const [markedAsFavorite, setMarkedAsFavorite] = useState(false);
46+
47+
if (actionType === 'text') {
48+
return <Box color="text-status-inactive">Some metadata</Box>;
49+
}
50+
51+
if (actionType === 'button-group') {
52+
return (
53+
<ButtonGroup
54+
variant="icon"
55+
items={[
56+
{
57+
id: 'settings',
58+
iconName: 'settings',
59+
type: 'icon-button',
60+
text: 'Settings',
61+
},
62+
{
63+
type: 'icon-toggle-button',
64+
id: 'favorite',
65+
text: 'Favorite',
66+
pressed: markedAsFavorite,
67+
iconName: 'star',
68+
pressedIconName: 'star-filled',
69+
},
70+
{
71+
id: 'menu',
72+
type: 'menu-dropdown',
73+
text: 'Menu',
74+
items: [
75+
{ id: 'start', text: 'Start' },
76+
{ id: 'stop', text: 'Stop', disabled: true },
77+
{
78+
id: 'hibernate',
79+
text: 'Hibernate',
80+
disabled: true,
81+
},
82+
{ id: 'reboot', text: 'Reboot', disabled: true },
83+
{ id: 'terminate', text: 'Terminate' },
84+
],
85+
},
86+
]}
87+
onItemClick={({ detail }) => {
88+
if (detail.id === 'favorite') {
89+
setMarkedAsFavorite(!markedAsFavorite);
90+
}
91+
}}
92+
ariaLabel={itemLabel}
93+
/>
94+
);
95+
}
96+
97+
const buttonDropdownItems = [
98+
{ id: 'start', text: 'Start' },
99+
{ id: 'stop', text: 'Stop', disabled: true },
100+
{
101+
id: 'hibernate',
102+
text: 'Hibernate',
103+
disabled: true,
104+
},
105+
{ id: 'reboot', text: 'Reboot', disabled: true },
106+
{ id: 'terminate', text: 'Terminate' },
107+
];
108+
109+
if (actionType === 'custom-inline-button-group') {
110+
return (
111+
<SpaceBetween direction="horizontal" size="s">
112+
<Button variant="inline-icon" iconName="settings" ariaLabel={`Settings for ${itemLabel}`} />
113+
<Button variant="inline-icon" iconName="star" ariaLabel={`Favorite ${itemLabel}`} />
114+
<ButtonDropdown
115+
items={buttonDropdownItems}
116+
ariaLabel={`Control instance for ${itemLabel}`}
117+
variant="inline-icon"
118+
/>
119+
</SpaceBetween>
120+
);
121+
}
122+
123+
if (actionType === 'inline-button-dropdown') {
124+
return (
125+
<ButtonDropdown
126+
items={buttonDropdownItems}
127+
ariaLabel={`Control instance for ${itemLabel}`}
128+
variant="inline-icon"
129+
/>
130+
);
131+
}
132+
133+
return <ButtonDropdown items={buttonDropdownItems} ariaLabel={`Control instance for ${itemLabel}`} variant="icon" />;
134+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import React, { useState } from 'react';
4+
5+
import { Button, SpaceBetween } from '~components';
6+
import Box from '~components/box';
7+
import Icon from '~components/icon';
8+
import TreeView from '~components/tree-view';
9+
10+
import { Actions, Content } from './common';
11+
import { allItems, items } from './items/dynamic-items';
12+
13+
const allExpandableItemIds = allItems.filter(item => item.children && item.children.length > 0).map(item => item.id);
14+
15+
export default function DynamicItemsPage() {
16+
const [expandedItems, setExpandedItems] = useState<Array<string>>([]);
17+
18+
return (
19+
<>
20+
<h1>Dynamic items page</h1>
21+
22+
<SpaceBetween size="s">
23+
<Button
24+
onClick={() => {
25+
setExpandedItems(allExpandableItemIds);
26+
console.time('expand-all');
27+
requestAnimationFrame(() => console.timeEnd('expand-all'));
28+
}}
29+
>
30+
Expand all
31+
</Button>
32+
33+
<Button
34+
onClick={() => {
35+
setExpandedItems([]);
36+
console.time('collapse-all');
37+
requestAnimationFrame(() => console.timeEnd('collapse-all'));
38+
}}
39+
>
40+
Collapse all
41+
</Button>
42+
</SpaceBetween>
43+
44+
<Box padding="xl">
45+
<TreeView
46+
items={items}
47+
renderItem={item => {
48+
const isExpanded = expandedItems.includes(item.id);
49+
return {
50+
icon: <Icon name={isExpanded ? 'folder-open' : 'folder'} />,
51+
content: <Content {...item} />,
52+
actions: item.hasActions ? <Actions actionType="button-group" itemLabel={item.name} /> : undefined,
53+
secondaryContent: item.tagName ? (
54+
<Box color="text-status-inactive">
55+
<SpaceBetween size="xxs" direction="horizontal">
56+
<Icon name="ticket" />
57+
<span>{item.tagName}</span>
58+
</SpaceBetween>
59+
</Box>
60+
) : undefined,
61+
};
62+
}}
63+
getItemId={item => item.id}
64+
getItemChildren={item => item.children}
65+
onItemToggle={({ detail }) => {
66+
if (detail.expanded) {
67+
const logName = `expand-item-${detail.item.name}`;
68+
console.time(logName);
69+
requestAnimationFrame(() => console.timeEnd(logName));
70+
return setExpandedItems(prev => [...prev, detail.id]);
71+
} else {
72+
const logName = `collapse-item-${detail.item.name}`;
73+
console.time(logName);
74+
requestAnimationFrame(() => console.timeEnd(logName));
75+
return setExpandedItems(prev => prev.filter(id => id !== detail.id));
76+
}
77+
}}
78+
expandedItems={expandedItems}
79+
i18nStrings={{
80+
expandButtonLabel: () => 'Expand item',
81+
collapseButtonLabel: () => 'Collapse item',
82+
}}
83+
/>
84+
</Box>
85+
</>
86+
);
87+
}

0 commit comments

Comments
 (0)