@transferwise/components 0.0.0-experimental-ff96697 → 0.0.0-experimental-ffbd2fc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/alert/Alert.story.tsx +6 -0
- package/src/button/_stories/Button.story.tsx +5 -0
- package/src/checkboxButton/CheckboxButton.story.tsx +117 -40
- package/src/checkboxButton/CheckboxButton.test.story.tsx +191 -0
- package/src/chips/Chips.story.tsx +177 -88
- package/src/chips/Chips.test.story.tsx +147 -0
- package/src/circularButton/CircularButton.story.tsx +23 -4
- package/src/circularButton/CircularButton.test.story.tsx +119 -1
- package/src/expressiveMoneyInput/ExpressiveMoneyInput.story.tsx +1 -0
- package/src/header/Header.story.tsx +5 -0
- package/src/iconButton/IconButton.story.tsx +45 -1
- package/src/iconButton/IconButton.test.story.tsx +163 -2
- package/src/inputs/SelectInput/_stories/SelectInput.story.tsx +1 -0
- package/src/listItem/AdditionalInfo/ListItemAdditionalInfo.story.tsx +5 -1
- package/src/listItem/AvatarLayout/ListItemAvatarLayout.story.tsx +5 -1
- package/src/listItem/AvatarView/ListItemAvatarView.story.tsx +5 -1
- package/src/listItem/Button/ListItemButton.story.tsx +5 -1
- package/src/listItem/Checkbox/ListItemCheckbox.story.tsx +5 -1
- package/src/listItem/IconButton/ListItemIconButton.story.tsx +5 -1
- package/src/listItem/Image/ListItemImage.story.tsx +5 -1
- package/src/listItem/Navigation/ListItemNavigation.story.tsx +5 -1
- package/src/listItem/Prompt/ListItemPrompt.story.tsx +5 -1
- package/src/listItem/Radio/ListItemRadio.story.tsx +5 -1
- package/src/listItem/Switch/ListItemSwitch.story.tsx +5 -1
- package/src/listItem/_stories/ListItem.disabled.story.tsx +1 -0
- package/src/listItem/_stories/ListItem.scenarios.story.tsx +1 -0
- package/src/listItem/_stories/ListItem.story.tsx +5 -1
- package/src/prompt/ActionPrompt/ActionPrompt.story.tsx +5 -0
- package/src/prompt/InfoPrompt/InfoPrompt.story.tsx +5 -0
- package/src/prompt/InlinePrompt/InlinePrompt.story.tsx +5 -0
- package/src/provider/theme/ThemeProvider.story.tsx +0 -8
- package/src/sentimentSurface/SentimentSurface.story.tsx +5 -0
- package/src/switch/Switch.story.tsx +45 -25
- package/src/switch/Switch.test.story.tsx +101 -0
- package/src/tokens/tokens.story.tsx +1 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
import { userEvent } from 'storybook/test';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
import { withVariantConfig } from '../../.storybook/helpers';
|
|
6
|
+
import { allModes } from '../../.storybook/modes';
|
|
7
|
+
import Chips, { type ChipValue } from './Chips';
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
component: Chips,
|
|
11
|
+
title: 'Actions/Chips/Tests',
|
|
12
|
+
tags: ['!autodocs', '!manifest'],
|
|
13
|
+
} satisfies Meta<typeof Chips>;
|
|
14
|
+
|
|
15
|
+
type Story = StoryObj<typeof Chips>;
|
|
16
|
+
|
|
17
|
+
const wait = async (ms: number) =>
|
|
18
|
+
new Promise<void>((resolve) => {
|
|
19
|
+
setTimeout(resolve, ms);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const filterChips = [
|
|
23
|
+
{ value: 'all', label: 'All' },
|
|
24
|
+
{ value: 'accounting', label: 'Accounting' },
|
|
25
|
+
{ value: 'payroll', label: 'Payroll' },
|
|
26
|
+
{ value: 'payments', label: 'Payments' },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const choiceChips = [
|
|
30
|
+
{ value: 100, label: '100 GBP' },
|
|
31
|
+
{ value: 200, label: '200 GBP' },
|
|
32
|
+
{ value: 300, label: '300 GBP' },
|
|
33
|
+
{ value: 500, label: '500 GBP+' },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
/** Filter and choice chips across all themes. */
|
|
37
|
+
export const Variants: Story = {
|
|
38
|
+
render: function Render() {
|
|
39
|
+
const [filterSelected, setFilterSelected] = useState<readonly ChipValue[]>([
|
|
40
|
+
'accounting',
|
|
41
|
+
'payments',
|
|
42
|
+
]);
|
|
43
|
+
const [choiceSelected, setChoiceSelected] = useState<ChipValue>(300);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
|
|
47
|
+
<Chips
|
|
48
|
+
chips={filterChips}
|
|
49
|
+
multiple
|
|
50
|
+
selected={filterSelected}
|
|
51
|
+
aria-label="Category filter"
|
|
52
|
+
onChange={({ selectedValue, isEnabled }) => {
|
|
53
|
+
setFilterSelected((prev) =>
|
|
54
|
+
isEnabled ? [...prev, selectedValue] : prev.filter((v) => v !== selectedValue),
|
|
55
|
+
);
|
|
56
|
+
}}
|
|
57
|
+
/>
|
|
58
|
+
<Chips
|
|
59
|
+
chips={choiceChips}
|
|
60
|
+
selected={choiceSelected}
|
|
61
|
+
aria-label="Transfer amount"
|
|
62
|
+
onChange={({ selectedValue }) => setChoiceSelected(selectedValue)}
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
parameters: {
|
|
68
|
+
variants: ['default', 'dark', 'bright-green', 'forest-green'],
|
|
69
|
+
chromatic: {
|
|
70
|
+
dark: allModes.dark,
|
|
71
|
+
brightGreen: allModes.brightGreen,
|
|
72
|
+
forestGreen: allModes.forestGreen,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** Filter chips at 400% zoom for accessibility testing. */
|
|
78
|
+
export const Zoom400: Story = {
|
|
79
|
+
render: function Render() {
|
|
80
|
+
const [selected, setSelected] = useState<readonly ChipValue[]>(['accounting']);
|
|
81
|
+
return (
|
|
82
|
+
<Chips
|
|
83
|
+
chips={filterChips}
|
|
84
|
+
multiple
|
|
85
|
+
selected={selected}
|
|
86
|
+
aria-label="Category filter"
|
|
87
|
+
onChange={({ selectedValue, isEnabled }) => {
|
|
88
|
+
setSelected((prev) =>
|
|
89
|
+
isEnabled ? [...prev, selectedValue] : prev.filter((v) => v !== selectedValue),
|
|
90
|
+
);
|
|
91
|
+
}}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
94
|
+
},
|
|
95
|
+
...withVariantConfig(['400%']),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/** Tab through choice chips and select one with Enter. */
|
|
99
|
+
export const ChoiceKeyboardInteraction: Story = {
|
|
100
|
+
render: function Render() {
|
|
101
|
+
const [selected, setSelected] = useState<ChipValue>(300);
|
|
102
|
+
return (
|
|
103
|
+
<Chips
|
|
104
|
+
chips={choiceChips}
|
|
105
|
+
selected={selected}
|
|
106
|
+
aria-label="Transfer amount"
|
|
107
|
+
onChange={({ selectedValue }) => setSelected(selectedValue)}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
},
|
|
111
|
+
play: async () => {
|
|
112
|
+
await userEvent.tab();
|
|
113
|
+
await wait(400);
|
|
114
|
+
await userEvent.tab();
|
|
115
|
+
await wait(400);
|
|
116
|
+
await userEvent.keyboard('{Enter}');
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/** Tab through filter chips and select one with Enter. */
|
|
121
|
+
export const FilterKeyboardInteraction: Story = {
|
|
122
|
+
render: function Render() {
|
|
123
|
+
const [selected, setSelected] = useState<readonly ChipValue[]>(['accounting']);
|
|
124
|
+
return (
|
|
125
|
+
<Chips
|
|
126
|
+
chips={filterChips}
|
|
127
|
+
multiple
|
|
128
|
+
selected={selected}
|
|
129
|
+
aria-label="Category filter"
|
|
130
|
+
onChange={({ selectedValue, isEnabled }) => {
|
|
131
|
+
setSelected((prev) =>
|
|
132
|
+
isEnabled ? [...prev, selectedValue] : prev.filter((v) => v !== selectedValue),
|
|
133
|
+
);
|
|
134
|
+
}}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
},
|
|
138
|
+
play: async () => {
|
|
139
|
+
await userEvent.tab();
|
|
140
|
+
await wait(400);
|
|
141
|
+
await userEvent.tab();
|
|
142
|
+
await wait(400);
|
|
143
|
+
await userEvent.tab();
|
|
144
|
+
await wait(400);
|
|
145
|
+
await userEvent.keyboard('{Enter}');
|
|
146
|
+
},
|
|
147
|
+
};
|
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
import * as Icons from '@transferwise/icons';
|
|
2
2
|
|
|
3
|
-
import { ControlType, Priority } from '../common';
|
|
4
|
-
|
|
5
3
|
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
6
4
|
import CircularButton from './CircularButton';
|
|
7
5
|
import Title from '../title';
|
|
8
6
|
import Body from '../body';
|
|
9
7
|
|
|
8
|
+
/**
|
|
9
|
+
* A circular icon button with a text label below it. Use when the user can perform
|
|
10
|
+
* multiple equally important actions related to an entity on screen — e.g. "Send",
|
|
11
|
+
* "Add funds", "Convert". Always display in a set; don't use a single circular
|
|
12
|
+
* button on its own.
|
|
13
|
+
*
|
|
14
|
+
* Supports `primary` and `secondary` priorities combined with `default` and
|
|
15
|
+
* `negative` types. The icon shrinks automatically at 400% zoom (56px → 32px).
|
|
16
|
+
*
|
|
17
|
+
* Screen readers announce the text label and ignore the icon, so keep labels
|
|
18
|
+
* contextual and action-oriented (start with a verb, 1–2 words).
|
|
19
|
+
*
|
|
20
|
+
* Keyboard: `Space` or `Enter` activates the button when focused.
|
|
21
|
+
*
|
|
22
|
+
* **Design guide**: [wise.design/components/circular-button](https://wise.design/components/circular-button)
|
|
23
|
+
*/
|
|
10
24
|
export default {
|
|
11
25
|
component: CircularButton,
|
|
12
26
|
title: 'Actions/CircularButton',
|
|
@@ -23,11 +37,15 @@ export default {
|
|
|
23
37
|
),
|
|
24
38
|
},
|
|
25
39
|
},
|
|
40
|
+
parameters: {
|
|
41
|
+
docs: { toc: true },
|
|
42
|
+
},
|
|
26
43
|
} satisfies Meta<typeof CircularButton>;
|
|
27
44
|
|
|
28
45
|
type Story = StoryObj<typeof CircularButton>;
|
|
29
46
|
|
|
30
|
-
|
|
47
|
+
/** Explore all props via the controls panel. */
|
|
48
|
+
export const Playground: Story = {
|
|
31
49
|
args: {
|
|
32
50
|
priority: 'primary',
|
|
33
51
|
type: 'default',
|
|
@@ -35,7 +53,8 @@ export const Basic: Story = {
|
|
|
35
53
|
},
|
|
36
54
|
};
|
|
37
55
|
|
|
38
|
-
|
|
56
|
+
/** All priority × type combinations, including deprecated types. */
|
|
57
|
+
export const AllTypes: Story = {
|
|
39
58
|
args: {
|
|
40
59
|
className: 'm-r-2',
|
|
41
60
|
},
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import { Freeze } from '@transferwise/icons';
|
|
1
|
+
import { Freeze, Plus, Send, Convert, Card } from '@transferwise/icons';
|
|
2
2
|
|
|
3
3
|
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
4
|
+
import { expect, fn, userEvent, within } from 'storybook/test';
|
|
4
5
|
import CircularButton from './CircularButton';
|
|
5
6
|
import Title from '../title';
|
|
6
7
|
import Body from '../body';
|
|
7
8
|
import { withVariantConfig } from '../../.storybook/helpers';
|
|
8
9
|
|
|
10
|
+
import { allModes } from '../../.storybook/modes';
|
|
11
|
+
|
|
12
|
+
const wait = async (ms: number) =>
|
|
13
|
+
new Promise<void>((resolve) => {
|
|
14
|
+
setTimeout(resolve, ms);
|
|
15
|
+
});
|
|
16
|
+
|
|
9
17
|
export default {
|
|
10
18
|
component: CircularButton,
|
|
11
19
|
title: 'Actions/CircularButton/Tests',
|
|
@@ -14,6 +22,116 @@ export default {
|
|
|
14
22
|
|
|
15
23
|
type Story = StoryObj<typeof CircularButton>;
|
|
16
24
|
|
|
25
|
+
/** All priorities, types, and states across all themes. */
|
|
26
|
+
export const Variants: Story = {
|
|
27
|
+
render: () => (
|
|
28
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '24px', padding: '16px' }}>
|
|
29
|
+
<CircularButton priority="primary" type="default" icon={<Send />}>
|
|
30
|
+
Send
|
|
31
|
+
</CircularButton>
|
|
32
|
+
<CircularButton priority="secondary" type="default" icon={<Plus />}>
|
|
33
|
+
Add funds
|
|
34
|
+
</CircularButton>
|
|
35
|
+
<CircularButton priority="primary" type="negative" icon={<Convert />}>
|
|
36
|
+
Convert
|
|
37
|
+
</CircularButton>
|
|
38
|
+
<CircularButton priority="primary" type="default" icon={<Card />} disabled>
|
|
39
|
+
Disabled
|
|
40
|
+
</CircularButton>
|
|
41
|
+
</div>
|
|
42
|
+
),
|
|
43
|
+
parameters: {
|
|
44
|
+
variants: ['default', 'dark', 'bright-green', 'forest-green'],
|
|
45
|
+
chromatic: {
|
|
46
|
+
dark: allModes.dark,
|
|
47
|
+
brightGreen: allModes.brightGreen,
|
|
48
|
+
forestGreen: allModes.forestGreen,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** CircularButton at 400% zoom — icon shrinks from 56px to 32px. */
|
|
54
|
+
export const Zoom400: Story = {
|
|
55
|
+
render: () => (
|
|
56
|
+
<div style={{ display: 'flex', gap: '24px', padding: '16px' }}>
|
|
57
|
+
<CircularButton priority="primary" type="default" icon={<Send />}>
|
|
58
|
+
Send
|
|
59
|
+
</CircularButton>
|
|
60
|
+
<CircularButton priority="secondary" type="default" icon={<Plus />}>
|
|
61
|
+
Add funds
|
|
62
|
+
</CircularButton>
|
|
63
|
+
</div>
|
|
64
|
+
),
|
|
65
|
+
...withVariantConfig(['400%']),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** Tab through all variants and activate each with Space. */
|
|
69
|
+
export const KeyboardInteraction: Story = {
|
|
70
|
+
render: () => {
|
|
71
|
+
const onClick = fn();
|
|
72
|
+
return (
|
|
73
|
+
<div style={{ display: 'flex', gap: '24px', padding: '16px' }}>
|
|
74
|
+
<CircularButton priority="primary" type="default" icon={<Send />} onClick={onClick}>
|
|
75
|
+
Send
|
|
76
|
+
</CircularButton>
|
|
77
|
+
<CircularButton priority="secondary" type="default" icon={<Plus />} onClick={onClick}>
|
|
78
|
+
Add funds
|
|
79
|
+
</CircularButton>
|
|
80
|
+
<CircularButton priority="primary" type="negative" icon={<Convert />} onClick={onClick}>
|
|
81
|
+
Convert
|
|
82
|
+
</CircularButton>
|
|
83
|
+
<CircularButton
|
|
84
|
+
priority="primary"
|
|
85
|
+
type="default"
|
|
86
|
+
icon={<Card />}
|
|
87
|
+
disabled
|
|
88
|
+
onClick={onClick}
|
|
89
|
+
>
|
|
90
|
+
Disabled
|
|
91
|
+
</CircularButton>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
},
|
|
95
|
+
play: async ({ canvasElement, step }) => {
|
|
96
|
+
const canvas = within(canvasElement);
|
|
97
|
+
const buttons = canvas.getAllByRole('button');
|
|
98
|
+
|
|
99
|
+
await step('tab to Send and press Space', async () => {
|
|
100
|
+
await userEvent.tab();
|
|
101
|
+
await expect(buttons[0]).toHaveFocus();
|
|
102
|
+
await wait(400);
|
|
103
|
+
await userEvent.keyboard(' ');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
await wait(400);
|
|
107
|
+
|
|
108
|
+
await step('tab to Add funds and press Space', async () => {
|
|
109
|
+
await userEvent.tab();
|
|
110
|
+
await expect(buttons[1]).toHaveFocus();
|
|
111
|
+
await wait(400);
|
|
112
|
+
await userEvent.keyboard(' ');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
await wait(400);
|
|
116
|
+
|
|
117
|
+
await step('tab to Convert and press Space', async () => {
|
|
118
|
+
await userEvent.tab();
|
|
119
|
+
await expect(buttons[2]).toHaveFocus();
|
|
120
|
+
await wait(400);
|
|
121
|
+
await userEvent.keyboard(' ');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
await wait(400);
|
|
125
|
+
|
|
126
|
+
await step('tab skips Disabled button', async () => {
|
|
127
|
+
await userEvent.tab();
|
|
128
|
+
// Disabled button should be skipped, focus leaves the component
|
|
129
|
+
await expect(buttons[3]).not.toHaveFocus();
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/** Long labels are centered beneath the icon. */
|
|
17
135
|
export const CenteredText: Story = {
|
|
18
136
|
render: () => {
|
|
19
137
|
return (
|
|
@@ -18,9 +18,34 @@ import { action } from 'storybook/actions';
|
|
|
18
18
|
import Body from '../body';
|
|
19
19
|
import SentimentSurface from '../sentimentSurface';
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* A circular icon-only button available in 7 sizes (16–72px), 4 priorities, and 3
|
|
23
|
+
* types (`default`, `negative`, `filled`). Requires an accessible name via
|
|
24
|
+
* `aria-label` since there is no visible text label.
|
|
25
|
+
*
|
|
26
|
+
* - **Primary**: most important action to move forward, acknowledge, or finish a task
|
|
27
|
+
* - **Secondary**: alternatives to the primary action
|
|
28
|
+
* - **Tertiary**: controls and views (e.g. back button, currency selection)
|
|
29
|
+
* - **Minimal**: dismissive or supplementary actions (dismiss, copy, tooltips)
|
|
30
|
+
*
|
|
31
|
+
* Only use icon buttons when the icon is recognisable without accompanying text.
|
|
32
|
+
* Sentiment-aware: automatically adjusts colours inside a
|
|
33
|
+
* [SentimentSurface](?path=/docs/foundations-sentimentsurface--docs).
|
|
34
|
+
*
|
|
35
|
+
* Keyboard: `Space` or `Enter` activates the button when focused.
|
|
36
|
+
*
|
|
37
|
+
* **Design guide**: [wise.design/components/icon-button](https://wise.design/components/icon-button)
|
|
38
|
+
*/
|
|
21
39
|
export default {
|
|
22
40
|
title: 'Actions/IconButton',
|
|
23
41
|
component: IconButton,
|
|
42
|
+
args: {
|
|
43
|
+
onClick: action('button click'),
|
|
44
|
+
'aria-label': 'Action',
|
|
45
|
+
},
|
|
46
|
+
parameters: {
|
|
47
|
+
docs: { toc: true },
|
|
48
|
+
},
|
|
24
49
|
} satisfies Meta<typeof IconButton>;
|
|
25
50
|
|
|
26
51
|
type Story = StoryObj<typeof IconButton>;
|
|
@@ -94,7 +119,26 @@ const Template = ({ size, disabled }: Props) => {
|
|
|
94
119
|
);
|
|
95
120
|
};
|
|
96
121
|
|
|
97
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Explore size, priority, type, and disabled state via the controls panel.
|
|
124
|
+
* Use `aria-label` to provide an accessible name.
|
|
125
|
+
*/
|
|
126
|
+
export const Playground: Story = {
|
|
127
|
+
args: {
|
|
128
|
+
size: 48,
|
|
129
|
+
priority: 'primary',
|
|
130
|
+
type: 'default',
|
|
131
|
+
disabled: false,
|
|
132
|
+
},
|
|
133
|
+
render: (args) => (
|
|
134
|
+
<IconButton {...args}>
|
|
135
|
+
<Plus />
|
|
136
|
+
</IconButton>
|
|
137
|
+
),
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/** All sizes, priorities, and types in a grid. */
|
|
141
|
+
export const AllTypes: Story = {
|
|
98
142
|
render: () => {
|
|
99
143
|
return (
|
|
100
144
|
<div
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
2
|
import { Menu, Plus, Settings, Star, Travel } from '@transferwise/icons';
|
|
3
3
|
import { action } from 'storybook/actions';
|
|
4
|
+
import { expect, userEvent, within } from 'storybook/test';
|
|
4
5
|
import IconButton from './IconButton';
|
|
5
6
|
import SentimentSurface from '../sentimentSurface';
|
|
7
|
+
import { withVariantConfig } from '../../.storybook/helpers';
|
|
6
8
|
import { allModes } from '../../.storybook/modes';
|
|
7
9
|
|
|
10
|
+
const wait = async (ms: number) =>
|
|
11
|
+
new Promise<void>((resolve) => {
|
|
12
|
+
setTimeout(resolve, ms);
|
|
13
|
+
});
|
|
14
|
+
|
|
8
15
|
export default {
|
|
9
16
|
title: 'Actions/IconButton/Tests',
|
|
10
17
|
component: IconButton,
|
|
@@ -16,11 +23,10 @@ type Story = StoryObj<typeof IconButton>;
|
|
|
16
23
|
/**
|
|
17
24
|
* IconButton displayed across all themes and sentiments for visual regression testing.
|
|
18
25
|
*/
|
|
19
|
-
export const
|
|
26
|
+
export const Variants: Story = {
|
|
20
27
|
render: () => {
|
|
21
28
|
const priorities = ['primary', 'secondary', 'tertiary', 'minimal', 'disabled'] as const;
|
|
22
29
|
const sentiments = ['negative', 'warning', 'neutral', 'success', 'proposition'] as const;
|
|
23
|
-
const icons = [Plus, Settings, Star, Travel];
|
|
24
30
|
|
|
25
31
|
return (
|
|
26
32
|
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
|
@@ -192,3 +198,158 @@ export const AllThemesAndSentiments: Story = {
|
|
|
192
198
|
},
|
|
193
199
|
},
|
|
194
200
|
};
|
|
201
|
+
|
|
202
|
+
/** IconButton at 400% zoom for accessibility testing. */
|
|
203
|
+
export const Zoom400: Story = {
|
|
204
|
+
render: () => (
|
|
205
|
+
<div style={{ display: 'flex', gap: '8px', padding: '16px', flexWrap: 'wrap' }}>
|
|
206
|
+
<IconButton
|
|
207
|
+
size={40}
|
|
208
|
+
aria-label="Primary"
|
|
209
|
+
priority="primary"
|
|
210
|
+
type="default"
|
|
211
|
+
onClick={action('click')}
|
|
212
|
+
>
|
|
213
|
+
<Plus />
|
|
214
|
+
</IconButton>
|
|
215
|
+
<IconButton
|
|
216
|
+
size={40}
|
|
217
|
+
aria-label="Secondary"
|
|
218
|
+
priority="secondary"
|
|
219
|
+
type="default"
|
|
220
|
+
onClick={action('click')}
|
|
221
|
+
>
|
|
222
|
+
<Settings />
|
|
223
|
+
</IconButton>
|
|
224
|
+
<IconButton
|
|
225
|
+
size={40}
|
|
226
|
+
aria-label="Tertiary"
|
|
227
|
+
priority="tertiary"
|
|
228
|
+
type="default"
|
|
229
|
+
onClick={action('click')}
|
|
230
|
+
>
|
|
231
|
+
<Star />
|
|
232
|
+
</IconButton>
|
|
233
|
+
<IconButton
|
|
234
|
+
size={40}
|
|
235
|
+
aria-label="Minimal"
|
|
236
|
+
priority="minimal"
|
|
237
|
+
type="default"
|
|
238
|
+
onClick={action('click')}
|
|
239
|
+
>
|
|
240
|
+
<Travel />
|
|
241
|
+
</IconButton>
|
|
242
|
+
<IconButton
|
|
243
|
+
size={40}
|
|
244
|
+
aria-label="Disabled"
|
|
245
|
+
priority="primary"
|
|
246
|
+
type="default"
|
|
247
|
+
disabled
|
|
248
|
+
onClick={action('click')}
|
|
249
|
+
>
|
|
250
|
+
<Menu />
|
|
251
|
+
</IconButton>
|
|
252
|
+
</div>
|
|
253
|
+
),
|
|
254
|
+
...withVariantConfig(['400%']),
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/** Tab through each priority variant and activate with Space. */
|
|
258
|
+
export const KeyboardInteraction: Story = {
|
|
259
|
+
render: () => (
|
|
260
|
+
<div style={{ display: 'flex', gap: '12px', padding: '16px' }}>
|
|
261
|
+
<IconButton
|
|
262
|
+
size={48}
|
|
263
|
+
aria-label="Primary"
|
|
264
|
+
priority="primary"
|
|
265
|
+
type="default"
|
|
266
|
+
onClick={action('click')}
|
|
267
|
+
>
|
|
268
|
+
<Plus />
|
|
269
|
+
</IconButton>
|
|
270
|
+
<IconButton
|
|
271
|
+
size={48}
|
|
272
|
+
aria-label="Secondary"
|
|
273
|
+
priority="secondary"
|
|
274
|
+
type="default"
|
|
275
|
+
onClick={action('click')}
|
|
276
|
+
>
|
|
277
|
+
<Settings />
|
|
278
|
+
</IconButton>
|
|
279
|
+
<IconButton
|
|
280
|
+
size={48}
|
|
281
|
+
aria-label="Tertiary"
|
|
282
|
+
priority="tertiary"
|
|
283
|
+
type="default"
|
|
284
|
+
onClick={action('click')}
|
|
285
|
+
>
|
|
286
|
+
<Star />
|
|
287
|
+
</IconButton>
|
|
288
|
+
<IconButton
|
|
289
|
+
size={48}
|
|
290
|
+
aria-label="Minimal"
|
|
291
|
+
priority="minimal"
|
|
292
|
+
type="default"
|
|
293
|
+
onClick={action('click')}
|
|
294
|
+
>
|
|
295
|
+
<Travel />
|
|
296
|
+
</IconButton>
|
|
297
|
+
<IconButton
|
|
298
|
+
size={48}
|
|
299
|
+
aria-label="Disabled"
|
|
300
|
+
priority="primary"
|
|
301
|
+
type="default"
|
|
302
|
+
disabled
|
|
303
|
+
onClick={action('click')}
|
|
304
|
+
>
|
|
305
|
+
<Menu />
|
|
306
|
+
</IconButton>
|
|
307
|
+
</div>
|
|
308
|
+
),
|
|
309
|
+
play: async ({ canvasElement, step }) => {
|
|
310
|
+
const canvas = within(canvasElement);
|
|
311
|
+
const buttons = canvas.getAllByRole('button');
|
|
312
|
+
|
|
313
|
+
await step('tab to Primary and press Space', async () => {
|
|
314
|
+
await userEvent.tab();
|
|
315
|
+
await expect(buttons[0]).toHaveFocus();
|
|
316
|
+
await wait(400);
|
|
317
|
+
await userEvent.keyboard(' ');
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
await wait(400);
|
|
321
|
+
|
|
322
|
+
await step('tab to Secondary and press Space', async () => {
|
|
323
|
+
await userEvent.tab();
|
|
324
|
+
await expect(buttons[1]).toHaveFocus();
|
|
325
|
+
await wait(400);
|
|
326
|
+
await userEvent.keyboard(' ');
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
await wait(400);
|
|
330
|
+
|
|
331
|
+
await step('tab to Tertiary and press Space', async () => {
|
|
332
|
+
await userEvent.tab();
|
|
333
|
+
await expect(buttons[2]).toHaveFocus();
|
|
334
|
+
await wait(400);
|
|
335
|
+
await userEvent.keyboard(' ');
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
await wait(400);
|
|
339
|
+
|
|
340
|
+
await step('tab to Minimal and press Space', async () => {
|
|
341
|
+
await userEvent.tab();
|
|
342
|
+
await expect(buttons[3]).toHaveFocus();
|
|
343
|
+
await wait(400);
|
|
344
|
+
await userEvent.keyboard(' ');
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
await wait(400);
|
|
348
|
+
|
|
349
|
+
await step('tab skips Disabled button', async () => {
|
|
350
|
+
await userEvent.tab();
|
|
351
|
+
// Disabled button should be skipped
|
|
352
|
+
await expect(buttons[4]).not.toHaveFocus();
|
|
353
|
+
});
|
|
354
|
+
},
|
|
355
|
+
};
|
|
@@ -19,7 +19,11 @@ import type { ListItemAdditionalInfoProps } from './ListItemAdditionalInfo';
|
|
|
19
19
|
export default {
|
|
20
20
|
component: ListItem.AdditionalInfo,
|
|
21
21
|
title: 'Content/ListItem/ListItem.AdditionalInfo',
|
|
22
|
-
|
|
22
|
+
parameters: {
|
|
23
|
+
docs: {
|
|
24
|
+
toc: true,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
23
27
|
args: {
|
|
24
28
|
children: lorem20,
|
|
25
29
|
},
|
|
@@ -13,7 +13,11 @@ import type { ListItemAvatarLayoutProps } from './ListItemAvatarLayout';
|
|
|
13
13
|
export default {
|
|
14
14
|
component: ListItem.AvatarLayout,
|
|
15
15
|
title: 'Content/ListItem/ListItem.AvatarLayout',
|
|
16
|
-
|
|
16
|
+
parameters: {
|
|
17
|
+
docs: {
|
|
18
|
+
toc: true,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
17
21
|
args: {
|
|
18
22
|
avatars: [{ asset: <Flag code="gb" /> }, { asset: <Flag code="eur" /> }],
|
|
19
23
|
orientation: 'horizontal',
|
|
@@ -77,7 +77,11 @@ const getPropsForPreview = (args: PreviewStoryArgs) => {
|
|
|
77
77
|
const meta: Meta<typeof ListItem.Button> = {
|
|
78
78
|
component: ListItem.Button,
|
|
79
79
|
title: 'Content/ListItem/ListItem.Button',
|
|
80
|
-
|
|
80
|
+
parameters: {
|
|
81
|
+
docs: {
|
|
82
|
+
toc: true,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
81
85
|
args: {
|
|
82
86
|
partiallyInteractive: false,
|
|
83
87
|
priority: 'secondary-neutral',
|
|
@@ -12,7 +12,11 @@ import { fn } from 'storybook/test';
|
|
|
12
12
|
const meta: Meta<ListItemCheckboxProps> = {
|
|
13
13
|
component: ListItem.Checkbox,
|
|
14
14
|
title: 'Content/ListItem/ListItem.Checkbox',
|
|
15
|
-
|
|
15
|
+
parameters: {
|
|
16
|
+
docs: {
|
|
17
|
+
toc: true,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
16
20
|
args: {
|
|
17
21
|
checked: false,
|
|
18
22
|
indeterminate: false,
|