@transferwise/components 46.131.2 → 46.132.1

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 (121) hide show
  1. package/build/actionOption/ActionOption.js.map +1 -1
  2. package/build/actionOption/ActionOption.mjs.map +1 -1
  3. package/build/alert/Alert.js +1 -1
  4. package/build/alert/Alert.js.map +1 -1
  5. package/build/alert/Alert.mjs +1 -1
  6. package/build/alert/Alert.mjs.map +1 -1
  7. package/build/checkboxOption/CheckboxOption.js.map +1 -1
  8. package/build/checkboxOption/CheckboxOption.mjs.map +1 -1
  9. package/build/common/Option/Option.js.map +1 -1
  10. package/build/common/Option/Option.mjs.map +1 -1
  11. package/build/common/liveRegion/LiveRegion.js +46 -7
  12. package/build/common/liveRegion/LiveRegion.js.map +1 -1
  13. package/build/common/liveRegion/LiveRegion.mjs +46 -7
  14. package/build/common/liveRegion/LiveRegion.mjs.map +1 -1
  15. package/build/flowNavigation/FlowNavigation.js +1 -0
  16. package/build/flowNavigation/FlowNavigation.js.map +1 -1
  17. package/build/flowNavigation/FlowNavigation.mjs +1 -0
  18. package/build/flowNavigation/FlowNavigation.mjs.map +1 -1
  19. package/build/legacylistItem/LegacyListItem.js.map +1 -1
  20. package/build/legacylistItem/LegacyListItem.mjs.map +1 -1
  21. package/build/main.css +52 -1
  22. package/build/navigationOption/NavigationOption.js.map +1 -1
  23. package/build/navigationOption/NavigationOption.mjs.map +1 -1
  24. package/build/overlayHeader/OverlayHeader.js +1 -0
  25. package/build/overlayHeader/OverlayHeader.js.map +1 -1
  26. package/build/overlayHeader/OverlayHeader.mjs +1 -0
  27. package/build/overlayHeader/OverlayHeader.mjs.map +1 -1
  28. package/build/prompt/InfoPrompt/InfoPrompt.js +2 -0
  29. package/build/prompt/InfoPrompt/InfoPrompt.js.map +1 -1
  30. package/build/prompt/InfoPrompt/InfoPrompt.mjs +2 -0
  31. package/build/prompt/InfoPrompt/InfoPrompt.mjs.map +1 -1
  32. package/build/radioOption/RadioOption.js.map +1 -1
  33. package/build/radioOption/RadioOption.mjs.map +1 -1
  34. package/build/styles/common/liveRegion/LiveRegion.css +3 -0
  35. package/build/styles/css/neptune.css +48 -1
  36. package/build/styles/main.css +52 -1
  37. package/build/styles/styles/less/neptune.css +48 -1
  38. package/build/summary/Summary.js +1 -1
  39. package/build/summary/Summary.js.map +1 -1
  40. package/build/summary/Summary.mjs +1 -1
  41. package/build/summary/Summary.mjs.map +1 -1
  42. package/build/switchOption/SwitchOption.js +1 -1
  43. package/build/switchOption/SwitchOption.js.map +1 -1
  44. package/build/switchOption/SwitchOption.mjs +1 -1
  45. package/build/switchOption/SwitchOption.mjs.map +1 -1
  46. package/build/types/actionOption/ActionOption.d.ts +1 -1
  47. package/build/types/alert/Alert.d.ts +1 -1
  48. package/build/types/checkboxOption/CheckboxOption.d.ts +1 -1
  49. package/build/types/common/Option/Option.d.ts +3 -0
  50. package/build/types/common/Option/Option.d.ts.map +1 -1
  51. package/build/types/common/liveRegion/LiveRegion.d.ts +5 -2
  52. package/build/types/common/liveRegion/LiveRegion.d.ts.map +1 -1
  53. package/build/types/legacylistItem/LegacyListItem.d.ts +1 -1
  54. package/build/types/navigationOption/NavigationOption.d.ts +1 -1
  55. package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts +2 -2
  56. package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts.map +1 -1
  57. package/build/types/radioOption/RadioOption.d.ts +1 -1
  58. package/build/types/summary/Summary.d.ts +1 -1
  59. package/build/types/switchOption/SwitchOption.d.ts +1 -1
  60. package/package.json +2 -2
  61. package/src/actionOption/ActionOption.story.tsx +2 -1
  62. package/src/actionOption/ActionOption.tsx +1 -1
  63. package/src/alert/Alert.story.tsx +1 -7
  64. package/src/alert/Alert.tsx +1 -1
  65. package/src/button/_stories/Button.story.tsx +0 -5
  66. package/src/checkboxButton/CheckboxButton.story.tsx +0 -1
  67. package/src/checkboxOption/CheckboxOption.story.tsx +2 -1
  68. package/src/checkboxOption/CheckboxOption.tsx +1 -1
  69. package/src/circularButton/CircularButton.story.tsx +0 -1
  70. package/src/common/Option/Option.tsx +3 -0
  71. package/src/common/liveRegion/LiveRegion.css +3 -0
  72. package/src/common/liveRegion/LiveRegion.less +3 -0
  73. package/src/common/liveRegion/LiveRegion.test.tsx +69 -2
  74. package/src/common/liveRegion/LiveRegion.tsx +77 -8
  75. package/src/display/Display.story.tsx +15 -1
  76. package/src/expressiveMoneyInput/ExpressiveMoneyInput.story.tsx +0 -1
  77. package/src/header/Header.story.tsx +0 -5
  78. package/src/inputWithDisplayFormat/InputWithDisplayFormat.story.tsx +0 -1
  79. package/src/inputs/SelectInput/_stories/SelectInput.docs.mdx +62 -0
  80. package/src/inputs/SelectInput/_stories/SelectInput.story.tsx +796 -220
  81. package/src/inputs/SelectInput/_stories/SelectInput.test.story.tsx +433 -4
  82. package/src/legacylistItem/LegacyListItem.story.tsx +2 -1
  83. package/src/legacylistItem/LegacyListItem.tsx +1 -1
  84. package/src/listItem/AdditionalInfo/ListItemAdditionalInfo.story.tsx +0 -5
  85. package/src/listItem/AvatarLayout/ListItemAvatarLayout.story.tsx +0 -5
  86. package/src/listItem/AvatarView/ListItemAvatarView.story.tsx +0 -5
  87. package/src/listItem/Button/ListItemButton.story.tsx +0 -5
  88. package/src/listItem/Checkbox/ListItemCheckbox.story.tsx +0 -5
  89. package/src/listItem/IconButton/ListItemIconButton.story.tsx +0 -5
  90. package/src/listItem/Image/ListItemImage.story.tsx +0 -5
  91. package/src/listItem/Navigation/ListItemNavigation.story.tsx +0 -5
  92. package/src/listItem/Prompt/ListItemPrompt.story.tsx +1 -5
  93. package/src/listItem/Radio/ListItemRadio.story.tsx +0 -5
  94. package/src/listItem/Switch/ListItemSwitch.story.tsx +0 -5
  95. package/src/listItem/_stories/ListItem.disabled.story.tsx +0 -1
  96. package/src/listItem/_stories/ListItem.scenarios.story.tsx +0 -1
  97. package/src/listItem/_stories/ListItem.story.tsx +1 -6
  98. package/src/main.css +52 -1
  99. package/src/main.less +1 -0
  100. package/src/modal/Modal.story.tsx +0 -1
  101. package/src/navigationOption/NavigationOption.story.tsx +2 -1
  102. package/src/navigationOption/NavigationOption.tsx +1 -1
  103. package/src/popover/Popover.story.tsx +0 -1
  104. package/src/prompt/ActionPrompt/ActionPrompt.story.tsx +0 -5
  105. package/src/prompt/InfoPrompt/InfoPrompt.story.tsx +0 -5
  106. package/src/prompt/InfoPrompt/InfoPrompt.test.story.tsx +142 -5
  107. package/src/prompt/InfoPrompt/InfoPrompt.test.tsx +11 -6
  108. package/src/prompt/InfoPrompt/InfoPrompt.tsx +4 -3
  109. package/src/prompt/InlinePrompt/InlinePrompt.story.tsx +0 -5
  110. package/src/provider/theme/ThemeProvider.story.tsx +8 -0
  111. package/src/radioOption/RadioOption.story.tsx +2 -1
  112. package/src/radioOption/RadioOption.tsx +1 -1
  113. package/src/sentimentSurface/SentimentSurface.story.tsx +0 -5
  114. package/src/sticky/Sticky.story.tsx +0 -1
  115. package/src/styles/less/core/_typography.less +15 -2
  116. package/src/styles/less/neptune.css +48 -1
  117. package/src/summary/Summary.story.tsx +1 -1
  118. package/src/summary/Summary.tsx +1 -1
  119. package/src/switchOption/SwitchOption.story.tsx +2 -1
  120. package/src/switchOption/SwitchOption.tsx +1 -1
  121. package/src/tokens/tokens.story.tsx +1 -1
@@ -1,13 +1,29 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
- import { fireEvent, fn, type Mock, screen, userEvent, within } from 'storybook/test';
3
- import { useState } from 'react';
2
+ import {
3
+ expect,
4
+ fireEvent,
5
+ fn,
6
+ type Mock,
7
+ screen,
8
+ userEvent,
9
+ waitFor,
10
+ within,
11
+ } from 'storybook/test';
12
+ import { Flag } from '@wise/art';
13
+ import { useEffect, useRef, useState } from 'react';
4
14
 
5
15
  import { allModes } from '../../../../.storybook/modes';
16
+ import { withVariantConfig } from '../../../../.storybook/helpers';
6
17
  import Body from '../../../body';
7
18
  import { Field } from '../../../field/Field';
8
19
  import { lorem5, lorem500 } from '../../../test-utils';
9
20
  import { sortByRelevance } from '../SelectInput.utils';
10
- import { SelectInput, SelectInputOptionContent, type SelectInputProps } from '..';
21
+ import {
22
+ SelectInput,
23
+ type SelectInputItem,
24
+ SelectInputOptionContent,
25
+ type SelectInputProps,
26
+ } from '..';
11
27
 
12
28
  const meta = {
13
29
  title: 'Forms/SelectInput/Tests',
@@ -18,12 +34,141 @@ const meta = {
18
34
  onClose: fn() satisfies Mock,
19
35
  onOpen: fn() satisfies Mock,
20
36
  },
21
- tags: ['!autodocs'],
37
+ tags: ['!autodocs', '!manifest'],
22
38
  } satisfies Meta<typeof SelectInput>;
23
39
  export default meta;
24
40
 
25
41
  type Story<T, M extends boolean = false> = StoryObj<SelectInputProps<T, M>>;
26
42
 
43
+ interface TestMonth {
44
+ id: number;
45
+ name: string;
46
+ }
47
+
48
+ const testMonths: TestMonth[] = [
49
+ 'January',
50
+ 'February',
51
+ 'March',
52
+ 'April',
53
+ 'May',
54
+ 'June',
55
+ 'July',
56
+ 'August',
57
+ 'September',
58
+ 'October',
59
+ 'November',
60
+ 'December',
61
+ ].map((name, index) => ({ id: index + 1, name }));
62
+
63
+ interface TestCurrency {
64
+ code: string;
65
+ name: string;
66
+ countries?: string[];
67
+ }
68
+
69
+ const testPopularCurrencies: TestCurrency[] = [
70
+ { code: 'USD', name: 'United States Dollar', countries: ['Hong Kong', 'Saudi Arabia'] },
71
+ { code: 'EUR', name: 'Euro', countries: ['Spain', 'Germany', 'France', 'Austria', 'Estonia'] },
72
+ { code: 'GBP', name: 'British pound', countries: ['England', 'Scotland', 'Wales'] },
73
+ ];
74
+
75
+ const testAllCurrencies: TestCurrency[] = [
76
+ ...testPopularCurrencies,
77
+ { code: 'AUD', name: 'Australian dollar' },
78
+ { code: 'CAD', name: 'Canadian dollar', countries: ['Canada'] },
79
+ { code: 'ÅLD', name: 'Ålandian peso', countries: ['Ålandia'] },
80
+ ].sort((a, b) => a.code.localeCompare(b.code));
81
+
82
+ function testCurrencyOption(currency: TestCurrency) {
83
+ return {
84
+ type: 'option',
85
+ value: currency,
86
+ filterMatchers: [currency.code, currency.name, ...(currency.countries ?? [])],
87
+ } satisfies SelectInputItem<TestCurrency>;
88
+ }
89
+
90
+ /**
91
+ * Visual regression story: all three sizes rendered across every theme.
92
+ * - `sm` — opened, simple (months) list
93
+ * - `md` — closed
94
+ * - `lg` — opened, grouped currencies list
95
+ */
96
+ export const Variants: Story<TestCurrency> = {
97
+ render: function Render() {
98
+ const smRef = useRef<HTMLButtonElement | null>(null);
99
+ const lgRef = useRef<HTMLButtonElement | null>(null);
100
+
101
+ const simpleItems: SelectInputItem<TestMonth>[] = testMonths.map((month) => ({
102
+ type: 'option',
103
+ value: month,
104
+ }));
105
+
106
+ const groupedItems: SelectInputItem<TestCurrency>[] = [
107
+ {
108
+ type: 'group',
109
+ label: 'Popular currencies',
110
+ options: testPopularCurrencies.map(testCurrencyOption),
111
+ },
112
+ {
113
+ type: 'group',
114
+ label: 'All currencies',
115
+ options: testAllCurrencies.map(testCurrencyOption),
116
+ },
117
+ ];
118
+
119
+ useEffect(() => {
120
+ const timer = setTimeout(() => {
121
+ smRef.current?.click();
122
+ setTimeout(() => {
123
+ lgRef.current?.click();
124
+ }, 100);
125
+ }, 200);
126
+ return () => clearTimeout(timer);
127
+ }, []);
128
+
129
+ return (
130
+ <div style={{ display: 'flex', gap: '2rem' }}>
131
+ <div style={{ minHeight: '550px', width: '280px' }}>
132
+ <p className="np-text-body-small m-b-1">Small (opened)</p>
133
+ <SelectInput<TestMonth>
134
+ size="sm"
135
+ placeholder="Month"
136
+ items={simpleItems}
137
+ triggerRef={smRef}
138
+ renderValue={(month) => <SelectInputOptionContent title={month.name} />}
139
+ />
140
+ </div>
141
+ <div style={{ width: '280px' }}>
142
+ <p className="np-text-body-small m-b-1">Medium</p>
143
+ <SelectInput<TestMonth>
144
+ size="md"
145
+ placeholder="Month"
146
+ items={simpleItems}
147
+ renderValue={(month) => <SelectInputOptionContent title={month.name} />}
148
+ />
149
+ </div>
150
+ <div style={{ minHeight: '550px', width: '280px' }}>
151
+ <p className="np-text-body-small m-b-1">Large (opened)</p>
152
+ <SelectInput<TestCurrency>
153
+ size="lg"
154
+ placeholder="Currency"
155
+ items={groupedItems}
156
+ triggerRef={lgRef}
157
+ renderValue={(currency) => (
158
+ <SelectInputOptionContent
159
+ title={currency.code}
160
+ note={currency.name}
161
+ icon={<Flag code={currency.code} intrinsicSize={24} />}
162
+ />
163
+ )}
164
+ />
165
+ </div>
166
+ </div>
167
+ );
168
+ },
169
+ ...withVariantConfig(['default', 'dark', 'bright-green', 'forest-green', 'rtl']),
170
+ };
171
+
27
172
  /**
28
173
  * Test story with a large list of countries where almost all have "United States dollar"
29
174
  * in their metadata. This reproduces the bug where searching "united" would scroll to
@@ -1308,6 +1453,250 @@ const wait = async (duration = 500) =>
1308
1453
  setTimeout(resolve, duration);
1309
1454
  });
1310
1455
 
1456
+ const currencyInteractionArgs = {
1457
+ items: [
1458
+ {
1459
+ type: 'group',
1460
+ label: 'Popular currencies',
1461
+ options: testPopularCurrencies.map(testCurrencyOption),
1462
+ },
1463
+ {
1464
+ type: 'group',
1465
+ label: 'All currencies',
1466
+ options: testAllCurrencies.map(testCurrencyOption),
1467
+ },
1468
+ ],
1469
+ defaultValue: testPopularCurrencies[0],
1470
+ renderValue: (currency: TestCurrency) => (
1471
+ <SelectInputOptionContent title={currency.code} note={currency.name} />
1472
+ ),
1473
+ renderFooter: ({
1474
+ resultsEmpty,
1475
+ queryNormalized,
1476
+ }: {
1477
+ resultsEmpty: boolean;
1478
+ queryNormalized: string | null | undefined;
1479
+ }) =>
1480
+ resultsEmpty && queryNormalized != null && /^[a-z]{3}$/u.test(queryNormalized) ? (
1481
+ <>
1482
+ It is not possible to use {queryNormalized.toUpperCase()} yet.{' '}
1483
+ <a href="#_" onClick={(e) => e.preventDefault()}>
1484
+ Email me when it is available.
1485
+ </a>
1486
+ </>
1487
+ ) : (
1488
+ <>
1489
+ Cannot find it?{' '}
1490
+ <a href="#_" onClick={(e) => e.preventDefault()}>
1491
+ Request the currency you need,
1492
+ </a>{' '}
1493
+ and we will notify you once it is available.
1494
+ </>
1495
+ ),
1496
+ filterable: true,
1497
+ filterPlaceholder: 'Type a currency / country',
1498
+ size: 'lg',
1499
+ } satisfies Story<TestCurrency>['args'];
1500
+
1501
+ export const BasicInteraction: Story<TestMonth | null> = {
1502
+ args: {
1503
+ placeholder: 'Month',
1504
+ items: testMonths.map((month) => ({ type: 'option', value: month })),
1505
+ renderValue: (month) => <SelectInputOptionContent title={month.name} />,
1506
+ },
1507
+ render: function Render({ onChange, onClear, ...args }) {
1508
+ const [selectedMonth, setSelectedMonth] = useState<TestMonth | null>(null);
1509
+ return (
1510
+ <SelectInput
1511
+ {...args}
1512
+ value={selectedMonth}
1513
+ onChange={(month) => {
1514
+ setSelectedMonth(month);
1515
+ onChange?.(month);
1516
+ }}
1517
+ onClear={() => {
1518
+ setSelectedMonth(null);
1519
+ onClear?.();
1520
+ }}
1521
+ />
1522
+ );
1523
+ },
1524
+ play: async ({ canvasElement, step }) => {
1525
+ const canvas = within(canvasElement);
1526
+
1527
+ await step('renders placeholder', async () => {
1528
+ const triggerButton = canvas.getByRole('combobox');
1529
+ await waitFor(async () => expect(triggerButton).toHaveTextContent('Month'));
1530
+ });
1531
+
1532
+ await step('selects option via mouse', async () => {
1533
+ const triggerButton = canvas.getByRole('combobox');
1534
+ await userEvent.click(triggerButton);
1535
+ await userEvent.unhover(triggerButton);
1536
+ const option = within(screen.getByRole('listbox')).getByRole('option', { name: 'May' });
1537
+ await userEvent.click(option);
1538
+ await waitFor(async () => expect(triggerButton).toHaveTextContent('May'));
1539
+ });
1540
+ },
1541
+ };
1542
+
1543
+ export const KeyboardInteraction: Story<TestCurrency> = {
1544
+ args: currencyInteractionArgs,
1545
+ play: async ({ step }) => {
1546
+ await step('Tab to the combobox', async () => {
1547
+ await userEvent.tab();
1548
+ });
1549
+
1550
+ await step('Open the combobox', async () => {
1551
+ await userEvent.keyboard(' ');
1552
+ });
1553
+
1554
+ await step('Check if options are displayed', async () => {
1555
+ await waitFor(async () => expect(await screen.findAllByRole('option')).toHaveLength(9));
1556
+ await waitFor(async () => expect(screen.getByText(/^Cannot find it\?/u)).toBeInTheDocument());
1557
+ });
1558
+
1559
+ await step('Type "huf" in the combobox', async () => {
1560
+ // this is workaround for Chromatic: Firefox and Edge tests failing.
1561
+ // Unclear on a proper solution
1562
+ await wait(750);
1563
+ const input = await screen.findByRole('combobox');
1564
+ await userEvent.type(input, 'huf');
1565
+ });
1566
+
1567
+ await step('Check if no options are displayed', async () => {
1568
+ // this is workaround for Chromatic: Firefox and Edge tests failing.
1569
+ // Unclear on a proper solution
1570
+ await wait(750);
1571
+ await expect(await screen.findByText(/^No results found/u)).toBeInTheDocument();
1572
+ await expect(
1573
+ await screen.findByText(/^It is not possible to use HUF yet\./u),
1574
+ ).toBeInTheDocument();
1575
+ await waitFor(
1576
+ async () => {
1577
+ await expect(screen.queryAllByRole('option')).toHaveLength(0);
1578
+ },
1579
+ { timeout: 1500 },
1580
+ );
1581
+ });
1582
+
1583
+ await step('Remove last two characters', async () => {
1584
+ await wait(500);
1585
+ const input = await screen.findByRole('combobox');
1586
+ await userEvent.type(input, '{Backspace}{Backspace}');
1587
+ });
1588
+
1589
+ await step('Check if two options are displayed', async () => {
1590
+ await waitFor(async () =>
1591
+ expect(within(screen.getByRole('listbox')).queryAllByRole('option')).toHaveLength(2),
1592
+ );
1593
+ });
1594
+
1595
+ await step('Type "e" in the combobox', async () => {
1596
+ const input = await screen.findByRole('combobox');
1597
+ await userEvent.type(input, '{Backspace}e');
1598
+ });
1599
+
1600
+ await step('Check if aria-activedescendant is set', async () => {
1601
+ const input = screen.getByRole('combobox');
1602
+ await waitFor(async () => expect(input).toHaveAttribute('aria-activedescendant'));
1603
+ });
1604
+ },
1605
+ };
1606
+
1607
+ export const MultiSelectInteraction: Story<TestCurrency, true> = {
1608
+ args: {
1609
+ ...currencyInteractionArgs,
1610
+ multiple: true,
1611
+ placeholder: 'Choose currencies…',
1612
+ defaultValue: [testPopularCurrencies[0]],
1613
+ renderValue: (currency, withinTrigger) =>
1614
+ withinTrigger ? (
1615
+ currency.code
1616
+ ) : (
1617
+ <SelectInputOptionContent title={currency.code} note={currency.name} />
1618
+ ),
1619
+ },
1620
+ play: async ({ canvasElement, step, args }) => {
1621
+ const canvas = within(canvasElement);
1622
+
1623
+ await step('Open the combobox', async () => {
1624
+ const triggerButton = canvas.getByRole('combobox');
1625
+ await userEvent.click(triggerButton);
1626
+ await wait(500);
1627
+ await userEvent.unhover(triggerButton);
1628
+ await expect(args.onOpen).toHaveBeenCalledOnce();
1629
+ });
1630
+
1631
+ await step('Select EUR option', async () => {
1632
+ const option = within(screen.getByRole('listbox')).getAllByRole('option', {
1633
+ name: /^EUR/u,
1634
+ })[0];
1635
+ await wait(500);
1636
+ await userEvent.click(option);
1637
+ });
1638
+
1639
+ await step('Check if selected options are displayed in trigger', async () => {
1640
+ const triggerButton = document.querySelector('button[role="combobox"]');
1641
+ await waitFor(async () => expect(triggerButton).toHaveTextContent('USD, EUR'));
1642
+ });
1643
+ },
1644
+ };
1645
+
1646
+ /**
1647
+ * Verifies that currencies with non-ASCII characters (e.g. ÅLD / Ålandian peso)
1648
+ * render correctly in the dropdown and can be found via filter.
1649
+ */
1650
+ export const SpecialCharacterOptions: Story<TestCurrency> = {
1651
+ args: {
1652
+ ...currencyInteractionArgs,
1653
+ filterable: true,
1654
+ filterPlaceholder: 'Type a currency',
1655
+ },
1656
+ play: async ({ canvasElement, step }) => {
1657
+ const canvas = within(canvasElement);
1658
+
1659
+ await step('Open the combobox', async () => {
1660
+ const triggerButton = canvas.getByRole('combobox');
1661
+ await userEvent.click(triggerButton);
1662
+ await userEvent.unhover(triggerButton);
1663
+ });
1664
+
1665
+ await step('ÅLD option is visible in dropdown', async () => {
1666
+ await waitFor(async () =>
1667
+ expect(
1668
+ within(screen.getByRole('listbox')).getByRole('option', { name: /ÅLD/u }),
1669
+ ).toBeInTheDocument(),
1670
+ );
1671
+ });
1672
+
1673
+ await step('Filter for Ålandian peso', async () => {
1674
+ await wait(500);
1675
+ const input = await screen.findByRole('combobox');
1676
+ await userEvent.type(input, 'åla');
1677
+ });
1678
+
1679
+ await step('Only ÅLD option remains', async () => {
1680
+ await waitFor(async () =>
1681
+ expect(within(screen.getByRole('listbox')).getAllByRole('option')).toHaveLength(1),
1682
+ );
1683
+ });
1684
+ },
1685
+ };
1686
+
1687
+ export const WithClearInteraction: Story<TestCurrency> = {
1688
+ args: {
1689
+ ...currencyInteractionArgs,
1690
+ onClear: fn() satisfies Mock,
1691
+ },
1692
+ play: async ({ step }) => {
1693
+ await step('Has clear button', async () => {
1694
+ const clearBtn = await screen.findByRole('button', { name: 'Clear' });
1695
+ await expect(clearBtn).toBeInTheDocument();
1696
+ });
1697
+ },
1698
+ };
1699
+
1311
1700
  /**
1312
1701
  * This test ensures that when the SelectInput is used within a scrollable page,
1313
1702
  * opening the dropdown does not cause any unwanted scrolling or layout shifts.
@@ -1416,3 +1805,43 @@ export const TriggerWithDescription: Story<OptionWithDescription> = {
1416
1805
  );
1417
1806
  },
1418
1807
  };
1808
+
1809
+ /** Basic months dropdown at 400% zoom for accessibility testing. */
1810
+ export const Zoom400: Story<TestMonth | null> = {
1811
+ render: function Render({ onChange, onClear, ...args }) {
1812
+ const triggerRef = useRef<HTMLButtonElement | null>(null);
1813
+ const [selectedMonth, setSelectedMonth] = useState<TestMonth | null>(null);
1814
+
1815
+ useEffect(() => {
1816
+ const timer = setTimeout(() => {
1817
+ triggerRef.current?.click();
1818
+ }, 200);
1819
+ return () => clearTimeout(timer);
1820
+ }, []);
1821
+
1822
+ return (
1823
+ <SelectInput
1824
+ {...args}
1825
+ triggerRef={triggerRef}
1826
+ value={selectedMonth}
1827
+ onChange={(month) => {
1828
+ setSelectedMonth(month);
1829
+ onChange?.(month);
1830
+ }}
1831
+ onClear={() => {
1832
+ setSelectedMonth(null);
1833
+ onClear?.();
1834
+ }}
1835
+ />
1836
+ );
1837
+ },
1838
+ args: {
1839
+ placeholder: 'Month',
1840
+ items: testMonths.map((month) => ({
1841
+ type: 'option',
1842
+ value: month,
1843
+ })),
1844
+ renderValue: (month) => <SelectInputOptionContent title={month.name} />,
1845
+ },
1846
+ ...withVariantConfig(['400%']),
1847
+ };
@@ -9,7 +9,8 @@ import Title from '../title/Title';
9
9
  import LegacyListItem from '.';
10
10
 
11
11
  /**
12
- * > ⚠️ This component is **deprecated** and superseded by the [new ListItem component](?path=/docs/content-listitem--docs).
12
+ * > ⚠️ This component is **deprecated** and superseded by the [new ListItem component](?path=/docs/content-listitem--docs)
13
+ * (run codemod to migrate: **`npx @wise/wds-codemods@latest list-item`**).
13
14
  */
14
15
  export default {
15
16
  component: LegacyListItem,
@@ -14,7 +14,7 @@ export type LegacyListItemProps = {
14
14
  };
15
15
 
16
16
  /**
17
- * @deprecated Use `<ListItem />` instead.
17
+ * @deprecated Use `<ListItem />` instead (run codemod to migrate: **`npx @wise/wds-codemods@latest list-item`**).
18
18
  * @deprecatedSince 46.104.0
19
19
  * @see [Source](../listItem/ListItem.tsx)
20
20
  * @see [Storybook](https://storybook.wise.design/?path=/docs/content-listitem--docs)
@@ -19,11 +19,6 @@ import type { ListItemAdditionalInfoProps } from './ListItemAdditionalInfo';
19
19
  export default {
20
20
  component: ListItem.AdditionalInfo,
21
21
  title: 'Content/ListItem/ListItem.AdditionalInfo',
22
- parameters: {
23
- docs: {
24
- toc: true,
25
- },
26
- },
27
22
  args: {
28
23
  children: lorem20,
29
24
  },
@@ -13,11 +13,6 @@ import type { ListItemAvatarLayoutProps } from './ListItemAvatarLayout';
13
13
  export default {
14
14
  component: ListItem.AvatarLayout,
15
15
  title: 'Content/ListItem/ListItem.AvatarLayout',
16
- parameters: {
17
- docs: {
18
- toc: true,
19
- },
20
- },
21
16
  args: {
22
17
  avatars: [{ asset: <Flag code="gb" /> }, { asset: <Flag code="eur" /> }],
23
18
  orientation: 'horizontal',
@@ -54,11 +54,6 @@ const BADGES = {
54
54
  export default {
55
55
  component: ListItem.AvatarView,
56
56
  title: 'Content/ListItem/ListItem.AvatarView',
57
- parameters: {
58
- docs: {
59
- toc: true,
60
- },
61
- },
62
57
  args: {
63
58
  size: 48,
64
59
  selected: false,
@@ -77,11 +77,6 @@ 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
- parameters: {
81
- docs: {
82
- toc: true,
83
- },
84
- },
85
80
  args: {
86
81
  partiallyInteractive: false,
87
82
  priority: 'secondary-neutral',
@@ -12,11 +12,6 @@ import { fn } from 'storybook/test';
12
12
  const meta: Meta<ListItemCheckboxProps> = {
13
13
  component: ListItem.Checkbox,
14
14
  title: 'Content/ListItem/ListItem.Checkbox',
15
- parameters: {
16
- docs: {
17
- toc: true,
18
- },
19
- },
20
15
  args: {
21
16
  checked: false,
22
17
  indeterminate: false,
@@ -27,11 +27,6 @@ const hideControls = disableControls([
27
27
  const meta: Meta<ListItemIconButtonProps> = {
28
28
  component: ListItem.IconButton,
29
29
  title: 'Content/ListItem/ListItem.IconButton',
30
- parameters: {
31
- docs: {
32
- toc: true,
33
- },
34
- },
35
30
  args: {
36
31
  partiallyInteractive: false,
37
32
  priority: undefined,
@@ -12,11 +12,6 @@ import type { ListItemImageProps } from './ListItemImage';
12
12
  export default {
13
13
  component: ListItem.Image,
14
14
  title: 'Content/ListItem/ListItem.Image',
15
- parameters: {
16
- docs: {
17
- toc: true,
18
- },
19
- },
20
15
  args: {
21
16
  size: 48,
22
17
  loading: undefined,
@@ -13,11 +13,6 @@ import type { ListItemNavigationProps } from './ListItemNavigation';
13
13
  const meta: Meta<ListItemNavigationProps> = {
14
14
  component: ListItem.Navigation,
15
15
  title: 'Content/ListItem/ListItem.Navigation',
16
- parameters: {
17
- docs: {
18
- toc: true,
19
- },
20
- },
21
16
  args: {
22
17
  href: 'https://wise.com',
23
18
  onClick: fn(),
@@ -16,11 +16,7 @@ import { Clock } from '@transferwise/icons';
16
16
  const meta: Meta<ListItemPromptProps> = {
17
17
  component: Prompt,
18
18
  title: 'Content/ListItem/ListItem.Prompt',
19
- parameters: {
20
- docs: {
21
- toc: true,
22
- },
23
- },
19
+
24
20
  decorators: [withoutKey],
25
21
  args: {
26
22
  sentiment: undefined,
@@ -13,11 +13,6 @@ import { ProfileType } from '../../common';
13
13
  const meta: Meta<ListItemRadioProps> = {
14
14
  component: ListItem.Radio,
15
15
  title: 'Content/ListItem/ListItem.Radio',
16
- parameters: {
17
- docs: {
18
- toc: true,
19
- },
20
- },
21
16
  args: {
22
17
  name: 'radio-group',
23
18
  value: 'option1',
@@ -12,11 +12,6 @@ import type { ListItemSwitchProps } from './ListItemSwitch';
12
12
  const meta: Meta<ListItemSwitchProps> = {
13
13
  component: ListItem.Switch,
14
14
  title: 'Content/ListItem/ListItem.Switch',
15
- parameters: {
16
- docs: {
17
- toc: true,
18
- },
19
- },
20
15
  args: {
21
16
  checked: false,
22
17
  onClick: fn(),
@@ -62,7 +62,6 @@ export default {
62
62
  canvas: {
63
63
  sourceState: 'hidden',
64
64
  },
65
- toc: true,
66
65
  page: () => (
67
66
  <>
68
67
  <Title>Disabled state</Title>
@@ -43,7 +43,6 @@ export default {
43
43
  },
44
44
  parameters: {
45
45
  docs: {
46
- toc: true,
47
46
  page: () => (
48
47
  <>
49
48
  <Title>Common scenarios</Title>
@@ -29,7 +29,7 @@ const hideControls = disableControls([
29
29
  * List Items let users review or select options from a dynamic list.<br />
30
30
  * For more details please refer to the [release notes](https://transferwise.atlassian.net/wiki/spaces/DS/pages/3647251055/List+Item+release+notes) and the [design spec](https://wise.design/components/list-item). <br />
31
31
  *
32
- * > This component replaces now deprecated `LegacyListItem`, `Summary` and all `*Option` components
32
+ * > This component replaces now deprecated `LegacyListItem`, `Summary` and all `*Option` components (run codemod to migrate: **`npx @wise/wds-codemods@latest info-prompt`**).
33
33
  */
34
34
  export default {
35
35
  component: ListItem,
@@ -47,11 +47,6 @@ export default {
47
47
  'ListItem.Image': ListItem.Image,
48
48
  },
49
49
  title: 'Content/ListItem',
50
- parameters: {
51
- docs: {
52
- toc: true,
53
- },
54
- },
55
50
  args: {
56
51
  title: 'List item title',
57
52
  subtitle: 'Subtitle goes here',