@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.
Files changed (36) hide show
  1. package/package.json +1 -1
  2. package/src/alert/Alert.story.tsx +6 -0
  3. package/src/button/_stories/Button.story.tsx +5 -0
  4. package/src/checkboxButton/CheckboxButton.story.tsx +117 -40
  5. package/src/checkboxButton/CheckboxButton.test.story.tsx +191 -0
  6. package/src/chips/Chips.story.tsx +177 -88
  7. package/src/chips/Chips.test.story.tsx +147 -0
  8. package/src/circularButton/CircularButton.story.tsx +23 -4
  9. package/src/circularButton/CircularButton.test.story.tsx +119 -1
  10. package/src/expressiveMoneyInput/ExpressiveMoneyInput.story.tsx +1 -0
  11. package/src/header/Header.story.tsx +5 -0
  12. package/src/iconButton/IconButton.story.tsx +45 -1
  13. package/src/iconButton/IconButton.test.story.tsx +163 -2
  14. package/src/inputs/SelectInput/_stories/SelectInput.story.tsx +1 -0
  15. package/src/listItem/AdditionalInfo/ListItemAdditionalInfo.story.tsx +5 -1
  16. package/src/listItem/AvatarLayout/ListItemAvatarLayout.story.tsx +5 -1
  17. package/src/listItem/AvatarView/ListItemAvatarView.story.tsx +5 -1
  18. package/src/listItem/Button/ListItemButton.story.tsx +5 -1
  19. package/src/listItem/Checkbox/ListItemCheckbox.story.tsx +5 -1
  20. package/src/listItem/IconButton/ListItemIconButton.story.tsx +5 -1
  21. package/src/listItem/Image/ListItemImage.story.tsx +5 -1
  22. package/src/listItem/Navigation/ListItemNavigation.story.tsx +5 -1
  23. package/src/listItem/Prompt/ListItemPrompt.story.tsx +5 -1
  24. package/src/listItem/Radio/ListItemRadio.story.tsx +5 -1
  25. package/src/listItem/Switch/ListItemSwitch.story.tsx +5 -1
  26. package/src/listItem/_stories/ListItem.disabled.story.tsx +1 -0
  27. package/src/listItem/_stories/ListItem.scenarios.story.tsx +1 -0
  28. package/src/listItem/_stories/ListItem.story.tsx +5 -1
  29. package/src/prompt/ActionPrompt/ActionPrompt.story.tsx +5 -0
  30. package/src/prompt/InfoPrompt/InfoPrompt.story.tsx +5 -0
  31. package/src/prompt/InlinePrompt/InlinePrompt.story.tsx +5 -0
  32. package/src/provider/theme/ThemeProvider.story.tsx +0 -8
  33. package/src/sentimentSurface/SentimentSurface.story.tsx +5 -0
  34. package/src/switch/Switch.story.tsx +45 -25
  35. package/src/switch/Switch.test.story.tsx +101 -0
  36. package/src/tokens/tokens.story.tsx +1 -1
@@ -27,7 +27,11 @@ const hideControls = disableControls([
27
27
  const meta: Meta<ListItemIconButtonProps> = {
28
28
  component: ListItem.IconButton,
29
29
  title: 'Content/ListItem/ListItem.IconButton',
30
-
30
+ parameters: {
31
+ docs: {
32
+ toc: true,
33
+ },
34
+ },
31
35
  args: {
32
36
  partiallyInteractive: false,
33
37
  priority: undefined,
@@ -12,7 +12,11 @@ import type { ListItemImageProps } from './ListItemImage';
12
12
  export default {
13
13
  component: ListItem.Image,
14
14
  title: 'Content/ListItem/ListItem.Image',
15
-
15
+ parameters: {
16
+ docs: {
17
+ toc: true,
18
+ },
19
+ },
16
20
  args: {
17
21
  size: 48,
18
22
  loading: undefined,
@@ -13,7 +13,11 @@ import type { ListItemNavigationProps } from './ListItemNavigation';
13
13
  const meta: Meta<ListItemNavigationProps> = {
14
14
  component: ListItem.Navigation,
15
15
  title: 'Content/ListItem/ListItem.Navigation',
16
-
16
+ parameters: {
17
+ docs: {
18
+ toc: true,
19
+ },
20
+ },
17
21
  args: {
18
22
  href: 'https://wise.com',
19
23
  onClick: fn(),
@@ -16,7 +16,11 @@ import { Clock } from '@transferwise/icons';
16
16
  const meta: Meta<ListItemPromptProps> = {
17
17
  component: Prompt,
18
18
  title: 'Content/ListItem/ListItem.Prompt',
19
-
19
+ parameters: {
20
+ docs: {
21
+ toc: true,
22
+ },
23
+ },
20
24
  decorators: [withoutKey],
21
25
  args: {
22
26
  sentiment: undefined,
@@ -13,7 +13,11 @@ import { ProfileType } from '../../common';
13
13
  const meta: Meta<ListItemRadioProps> = {
14
14
  component: ListItem.Radio,
15
15
  title: 'Content/ListItem/ListItem.Radio',
16
-
16
+ parameters: {
17
+ docs: {
18
+ toc: true,
19
+ },
20
+ },
17
21
  args: {
18
22
  name: 'radio-group',
19
23
  value: 'option1',
@@ -12,7 +12,11 @@ import type { ListItemSwitchProps } from './ListItemSwitch';
12
12
  const meta: Meta<ListItemSwitchProps> = {
13
13
  component: ListItem.Switch,
14
14
  title: 'Content/ListItem/ListItem.Switch',
15
-
15
+ parameters: {
16
+ docs: {
17
+ toc: true,
18
+ },
19
+ },
16
20
  args: {
17
21
  checked: false,
18
22
  onClick: fn(),
@@ -62,6 +62,7 @@ export default {
62
62
  canvas: {
63
63
  sourceState: 'hidden',
64
64
  },
65
+ toc: true,
65
66
  page: () => (
66
67
  <>
67
68
  <Title>Disabled state</Title>
@@ -43,6 +43,7 @@ export default {
43
43
  },
44
44
  parameters: {
45
45
  docs: {
46
+ toc: true,
46
47
  page: () => (
47
48
  <>
48
49
  <Title>Common scenarios</Title>
@@ -47,7 +47,11 @@ export default {
47
47
  'ListItem.Image': ListItem.Image,
48
48
  },
49
49
  title: 'Content/ListItem',
50
-
50
+ parameters: {
51
+ docs: {
52
+ toc: true,
53
+ },
54
+ },
51
55
  args: {
52
56
  title: 'List item title',
53
57
  subtitle: 'Subtitle goes here',
@@ -82,6 +82,11 @@ const meta: Meta<typeof ActionPrompt> = {
82
82
  },
83
83
  },
84
84
  },
85
+ parameters: {
86
+ docs: {
87
+ toc: true,
88
+ },
89
+ },
85
90
  };
86
91
 
87
92
  export default meta;
@@ -66,6 +66,11 @@ export default {
66
66
  },
67
67
  },
68
68
  },
69
+ parameters: {
70
+ docs: {
71
+ toc: true,
72
+ },
73
+ },
69
74
  } satisfies Meta<InfoPromptProps>;
70
75
 
71
76
  /**
@@ -53,6 +53,11 @@ export default {
53
53
  },
54
54
  },
55
55
  },
56
+ parameters: {
57
+ docs: {
58
+ toc: true,
59
+ },
60
+ },
56
61
  } satisfies Meta<InlinePromptProps>;
57
62
 
58
63
  /**
@@ -24,14 +24,6 @@ export default {
24
24
  component: ThemeProvider,
25
25
  title: 'Foundations/ThemeProvider',
26
26
  tags: ['!manifest'],
27
- parameters: {
28
- docs: {
29
- description: {
30
- component:
31
- 'Components require `ThemeProvider` from `@wise/components-theming` to be wrapped around your application for theming support. Available themes include: `personal`, `business`, `platform`, and variants with different color schemes.',
32
- },
33
- },
34
- },
35
27
  } satisfies Meta<typeof ThemeProvider>;
36
28
 
37
29
  type Story = StoryObj<typeof ThemeProvider>;
@@ -102,6 +102,11 @@ const meta: Meta<typeof SentimentSurface> = {
102
102
  'data-testid': undefined,
103
103
  children: 'This is a sentiment surface',
104
104
  },
105
+ parameters: {
106
+ docs: {
107
+ toc: true,
108
+ },
109
+ },
105
110
  };
106
111
 
107
112
  export default meta;
@@ -1,4 +1,4 @@
1
- import { useState } from 'react';
1
+ import React, { useState } from 'react';
2
2
  import { fn } from 'storybook/test';
3
3
 
4
4
  import Switch, { SwitchProps } from './Switch';
@@ -6,6 +6,17 @@ import { Field } from '../field/Field';
6
6
  import { Meta, StoryObj } from '@storybook/react-webpack5';
7
7
  import { Label } from '../label';
8
8
 
9
+ /**
10
+ * A toggle switch representing an on/off state. Semantically a `role="switch"` button.
11
+ *
12
+ * Always pair with an accessible label using `Field`, a `<label>` element, or
13
+ * `aria-labelledby`. The deprecated `aria-label` prop is supported but prefer an
14
+ * external label.
15
+ *
16
+ * Keyboard: `Space` toggles the switch when focused.
17
+ *
18
+ * **Design guide**: [wise.design/components/switch](https://wise.design/components/switch)
19
+ */
9
20
  const meta: Meta<typeof Switch> = {
10
21
  component: Switch,
11
22
  title: 'Actions/Switch',
@@ -18,41 +29,46 @@ const meta: Meta<typeof Switch> = {
18
29
  'aria-label': undefined,
19
30
  onClick: fn(),
20
31
  },
32
+ parameters: {
33
+ docs: { toc: true },
34
+ },
21
35
  } satisfies Meta<typeof Switch>;
22
36
 
23
37
  export default meta;
24
38
  type Story = StoryObj<typeof Switch>;
25
39
 
26
- export const Basic: Story = {
27
- tags: ['!autodocs', '!dev'],
28
- };
29
-
40
+ /** Interactive playground toggle checked and disabled via controls. */
30
41
  export const Playground: Story = {
31
- tags: ['!autodocs'],
42
+ render: function Render(args) {
43
+ const [checked, setChecked] = useState(args.checked ?? false);
44
+ return (
45
+ <Field id="playground-field" label="Switch label">
46
+ <Switch {...args} checked={checked} onClick={() => setChecked((v) => !v)} />
47
+ </Field>
48
+ );
49
+ },
32
50
  argTypes: {
33
51
  onClick: { table: { disable: true } },
52
+ checked: { control: false },
34
53
  },
35
54
  };
36
55
 
37
- export const Interactivity: Story = {
56
+ /** Three labelling patterns: Field, Label, and aria-labelledby. */
57
+ export const LabelPatterns: Story = {
38
58
  render: function Render({ disabled }: SwitchProps) {
39
- const [checked1, setCheck1] = useState(false);
40
- const [checked2, setCheck2] = useState(true);
41
- const [checked3, setCheck3] = useState(false);
59
+ const [checked1, setCheck1] = useState(true);
60
+ const [checked2, setCheck2] = useState(false);
61
+ const [checked3, setCheck3] = useState(true);
42
62
 
43
63
  return (
44
64
  <>
45
- <Field id="fieldId" label="Using Field component">
46
- <Switch checked={checked1} disabled={disabled} onClick={() => setCheck1(!checked1)} />
47
- </Field>
48
-
49
65
  <div>
50
66
  <Label htmlFor="labelId">Using Label component</Label>
51
67
  <Switch
52
68
  id="labelId"
53
- checked={checked2}
69
+ checked={checked1}
54
70
  disabled={disabled}
55
- onClick={() => setCheck2(!checked2)}
71
+ onClick={() => setCheck1(!checked1)}
56
72
  />
57
73
  </div>
58
74
 
@@ -62,11 +78,15 @@ export const Interactivity: Story = {
62
78
  </strong>
63
79
  <Switch
64
80
  aria-labelledby="ariaId"
65
- checked={checked3}
81
+ checked={checked2}
66
82
  disabled={disabled}
67
- onClick={() => setCheck3(!checked3)}
83
+ onClick={() => setCheck2(!checked2)}
68
84
  />
69
85
  </div>
86
+
87
+ <Field id="fieldId" label="Using Field component">
88
+ <Switch checked={checked3} disabled={disabled} onClick={() => setCheck3(!checked3)} />
89
+ </Field>
70
90
  </>
71
91
  );
72
92
  },
@@ -112,7 +132,7 @@ function Render() {
112
132
  },
113
133
  },
114
134
  decorators: [
115
- (Story: any) => (
135
+ (Story: React.FC) => (
116
136
  <div className="d-flex flex-column" style={{ gap: '1rem' }}>
117
137
  <Story />
118
138
  </div>
@@ -126,17 +146,17 @@ function Render() {
126
146
  };
127
147
 
128
148
  export const Disabled: Story = {
129
- render: function Render(args) {
149
+ render: function Render() {
130
150
  const [checked1, setCheck1] = useState(false);
131
151
  const [checked2, setCheck2] = useState(true);
132
152
 
133
153
  return (
134
154
  <>
135
- <Field label="Switch label">
155
+ <Field label="Disabled (off)">
136
156
  <Switch checked={checked1} disabled onClick={() => setCheck1(!checked1)} />
137
157
  </Field>
138
158
 
139
- <Field label="Switch label">
159
+ <Field label="Disabled (on)">
140
160
  <Switch checked={checked2} disabled onClick={() => setCheck2(!checked2)} />
141
161
  </Field>
142
162
  </>
@@ -152,14 +172,14 @@ function Render() {
152
172
 
153
173
  return (
154
174
  <>
155
- <Field label="Switch label">
175
+ <Field label="Disabled (off)">
156
176
  <Switch
157
177
  checked={checked1}
158
178
  disabled
159
179
  onClick={() => setCheck1(!checked1)}
160
180
  />
161
- </Fie
162
- <Field label="Switch label">
181
+ </Field>
182
+ <Field label="Disabled (on)">
163
183
  <Switch
164
184
  checked={checked2}
165
185
  disabled
@@ -0,0 +1,101 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import { expect, userEvent, within } from 'storybook/test';
3
+ import { useState } from 'react';
4
+
5
+ import { withVariantConfig } from '../../.storybook/helpers';
6
+ import { allModes } from '../../.storybook/modes';
7
+ import { Field } from '../field/Field';
8
+ import Switch from './Switch';
9
+
10
+ export default {
11
+ component: Switch,
12
+ title: 'Actions/Switch/Tests',
13
+ tags: ['!autodocs', '!manifest'],
14
+ } satisfies Meta<typeof Switch>;
15
+
16
+ type Story = StoryObj<typeof Switch>;
17
+
18
+ /** All switch states across all themes. */
19
+ export const Variants: Story = {
20
+ render: function Render() {
21
+ const [unchecked, setUnchecked] = useState(false);
22
+ const [checked, setChecked] = useState(true);
23
+
24
+ return (
25
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px' }}>
26
+ <Field label="Unchecked">
27
+ <Switch checked={unchecked} onClick={() => setUnchecked((v) => !v)} />
28
+ </Field>
29
+ <Field label="Checked">
30
+ <Switch checked={checked} onClick={() => setChecked((v) => !v)} />
31
+ </Field>
32
+ <Field label="Disabled (off)">
33
+ <Switch checked={false} disabled onClick={() => {}} />
34
+ </Field>
35
+ <Field label="Disabled (on)">
36
+ <Switch checked disabled onClick={() => {}} />
37
+ </Field>
38
+ </div>
39
+ );
40
+ },
41
+ parameters: {
42
+ variants: ['default', 'dark', 'bright-green', 'forest-green'],
43
+ chromatic: {
44
+ dark: allModes.dark,
45
+ brightGreen: allModes.brightGreen,
46
+ forestGreen: allModes.forestGreen,
47
+ },
48
+ },
49
+ };
50
+
51
+ /** Switch at 400% zoom for accessibility testing. */
52
+ export const Zoom400: Story = {
53
+ render: function Render() {
54
+ const [checked, setChecked] = useState(false);
55
+ return (
56
+ <Field label="Email notifications">
57
+ <Switch checked={checked} onClick={() => setChecked((v) => !v)} />
58
+ </Field>
59
+ );
60
+ },
61
+ ...withVariantConfig(['400%']),
62
+ };
63
+
64
+ /** Tab to the switch and use Space to toggle it. */
65
+ export const KeyboardInteraction: Story = {
66
+ render: function Render() {
67
+ const [checked, setChecked] = useState(false);
68
+ return (
69
+ <Field id="notif-field" label="Email notifications">
70
+ <Switch checked={checked} onClick={() => setChecked((v) => !v)} />
71
+ </Field>
72
+ );
73
+ },
74
+ play: async ({ canvasElement, step }) => {
75
+ const wait = async (ms: number) =>
76
+ new Promise<void>((resolve) => {
77
+ setTimeout(resolve, ms);
78
+ });
79
+ const canvas = within(canvasElement);
80
+ const switchEl = canvas.getByRole('switch');
81
+
82
+ await step('tab focuses the switch', async () => {
83
+ await userEvent.tab();
84
+ await expect(switchEl).toHaveFocus();
85
+ });
86
+
87
+ await wait(400);
88
+
89
+ await step('space toggles the switch on', async () => {
90
+ await userEvent.keyboard(' ');
91
+ await expect(switchEl).toHaveAttribute('aria-checked', 'true');
92
+ });
93
+
94
+ await wait(400);
95
+
96
+ await step('space toggles the switch off', async () => {
97
+ await userEvent.keyboard(' ');
98
+ await expect(switchEl).toHaveAttribute('aria-checked', 'false');
99
+ });
100
+ },
101
+ };
@@ -7,7 +7,7 @@ import IconButton from '../iconButton';
7
7
  import Body from '../body';
8
8
 
9
9
  const meta: Meta = {
10
- title: 'Foundations/Tokens',
10
+ title: 'Foundations/tokens',
11
11
  tags: ['!autodocs', '!manifest'],
12
12
  };
13
13