@transferwise/components 46.125.0 → 46.127.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 (197) hide show
  1. package/build/avatarView/AvatarView.js.map +1 -1
  2. package/build/avatarView/AvatarView.mjs.map +1 -1
  3. package/build/common/locale/index.js +13 -0
  4. package/build/common/locale/index.js.map +1 -1
  5. package/build/common/locale/index.mjs +13 -1
  6. package/build/common/locale/index.mjs.map +1 -1
  7. package/build/expressiveMoneyInput/currencySelector/CurrencySelector.js +31 -1
  8. package/build/expressiveMoneyInput/currencySelector/CurrencySelector.js.map +1 -1
  9. package/build/expressiveMoneyInput/currencySelector/CurrencySelector.mjs +32 -2
  10. package/build/expressiveMoneyInput/currencySelector/CurrencySelector.mjs.map +1 -1
  11. package/build/field/Field.js +1 -0
  12. package/build/field/Field.js.map +1 -1
  13. package/build/field/Field.mjs +1 -0
  14. package/build/field/Field.mjs.map +1 -1
  15. package/build/index.js +3 -0
  16. package/build/index.js.map +1 -1
  17. package/build/index.mjs +2 -1
  18. package/build/index.mjs.map +1 -1
  19. package/build/inputs/Input.js.map +1 -1
  20. package/build/inputs/Input.mjs.map +1 -1
  21. package/build/inputs/SearchInput.js.map +1 -1
  22. package/build/inputs/SearchInput.mjs.map +1 -1
  23. package/build/inputs/SelectInput.js.map +1 -1
  24. package/build/inputs/SelectInput.mjs.map +1 -1
  25. package/build/inputs/TextArea.js.map +1 -1
  26. package/build/inputs/TextArea.mjs.map +1 -1
  27. package/build/listItem/ListItem.js +2 -2
  28. package/build/listItem/ListItem.js.map +1 -1
  29. package/build/listItem/ListItem.mjs +2 -2
  30. package/build/listItem/ListItem.mjs.map +1 -1
  31. package/build/listItem/Prompt/ListItemPrompt.js +6 -4
  32. package/build/listItem/Prompt/ListItemPrompt.js.map +1 -1
  33. package/build/listItem/Prompt/ListItemPrompt.mjs +7 -2
  34. package/build/listItem/Prompt/ListItemPrompt.mjs.map +1 -1
  35. package/build/main.css +52 -21
  36. package/build/moneyInput/MoneyInput.js +6 -1
  37. package/build/moneyInput/MoneyInput.js.map +1 -1
  38. package/build/moneyInput/MoneyInput.mjs +6 -1
  39. package/build/moneyInput/MoneyInput.mjs.map +1 -1
  40. package/build/prompt/ActionPrompt/ActionPrompt.js +27 -4
  41. package/build/prompt/ActionPrompt/ActionPrompt.js.map +1 -1
  42. package/build/prompt/ActionPrompt/ActionPrompt.mjs +27 -4
  43. package/build/prompt/ActionPrompt/ActionPrompt.mjs.map +1 -1
  44. package/build/prompt/InfoPrompt/InfoPrompt.js +113 -0
  45. package/build/prompt/InfoPrompt/InfoPrompt.js.map +1 -0
  46. package/build/prompt/InfoPrompt/InfoPrompt.mjs +111 -0
  47. package/build/prompt/InfoPrompt/InfoPrompt.mjs.map +1 -0
  48. package/build/prompt/PrimitivePrompt/PrimitivePrompt.js.map +1 -1
  49. package/build/prompt/PrimitivePrompt/PrimitivePrompt.mjs.map +1 -1
  50. package/build/radioOption/RadioOption.js.map +1 -1
  51. package/build/radioOption/RadioOption.mjs.map +1 -1
  52. package/build/slidingPanel/SlidingPanel.js.map +1 -1
  53. package/build/slidingPanel/SlidingPanel.mjs.map +1 -1
  54. package/build/statusIcon/StatusIcon.js +2 -0
  55. package/build/statusIcon/StatusIcon.js.map +1 -1
  56. package/build/statusIcon/StatusIcon.mjs +2 -0
  57. package/build/statusIcon/StatusIcon.mjs.map +1 -1
  58. package/build/styles/main.css +52 -21
  59. package/build/styles/prompt/InfoPrompt/InfoPrompt.css +31 -0
  60. package/build/styles/sentimentSurface/SentimentSurface.css +21 -21
  61. package/build/table/TableCell.js.map +1 -1
  62. package/build/table/TableCell.mjs.map +1 -1
  63. package/build/typeahead/Typeahead.js +1 -0
  64. package/build/typeahead/Typeahead.js.map +1 -1
  65. package/build/typeahead/Typeahead.mjs +1 -0
  66. package/build/typeahead/Typeahead.mjs.map +1 -1
  67. package/build/types/avatarView/AvatarView.d.ts +1 -1
  68. package/build/types/avatarView/AvatarView.d.ts.map +1 -1
  69. package/build/types/common/locale/index.d.ts +8 -0
  70. package/build/types/common/locale/index.d.ts.map +1 -1
  71. package/build/types/expressiveMoneyInput/currencySelector/CurrencySelector.d.ts.map +1 -1
  72. package/build/types/index.d.ts +3 -2
  73. package/build/types/index.d.ts.map +1 -1
  74. package/build/types/inputs/Input.d.ts.map +1 -1
  75. package/build/types/inputs/SearchInput.d.ts.map +1 -1
  76. package/build/types/inputs/SelectInput.d.ts +1 -1
  77. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  78. package/build/types/inputs/TextArea.d.ts.map +1 -1
  79. package/build/types/listItem/ListItem.d.ts +1 -1
  80. package/build/types/listItem/Prompt/ListItemPrompt.d.ts +2 -3
  81. package/build/types/listItem/Prompt/ListItemPrompt.d.ts.map +1 -1
  82. package/build/types/moneyInput/MoneyInput.d.ts.map +1 -1
  83. package/build/types/primitives/PrimitiveAnchor/PrimitiveAnchor.types.d.ts.map +1 -1
  84. package/build/types/primitives/PrimitiveButton/PrimitiveButton.types.d.ts.map +1 -1
  85. package/build/types/prompt/ActionPrompt/ActionPrompt.d.ts +4 -2
  86. package/build/types/prompt/ActionPrompt/ActionPrompt.d.ts.map +1 -1
  87. package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts +56 -0
  88. package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts.map +1 -0
  89. package/build/types/prompt/InfoPrompt/index.d.ts +3 -0
  90. package/build/types/prompt/InfoPrompt/index.d.ts.map +1 -0
  91. package/build/types/prompt/PrimitivePrompt/PrimitivePrompt.d.ts +5 -5
  92. package/build/types/prompt/PrimitivePrompt/PrimitivePrompt.d.ts.map +1 -1
  93. package/build/types/prompt/index.d.ts +2 -0
  94. package/build/types/prompt/index.d.ts.map +1 -1
  95. package/build/types/radioOption/RadioOption.d.ts.map +1 -1
  96. package/build/types/slidingPanel/SlidingPanel.d.ts.map +1 -1
  97. package/build/types/statusIcon/StatusIcon.d.ts +2 -1
  98. package/build/types/statusIcon/StatusIcon.d.ts.map +1 -1
  99. package/build/types/table/TableCell.d.ts.map +1 -1
  100. package/build/types/withDisplayFormat/WithDisplayFormat.d.ts.map +1 -1
  101. package/build/withDisplayFormat/WithDisplayFormat.js.map +1 -1
  102. package/build/withDisplayFormat/WithDisplayFormat.mjs.map +1 -1
  103. package/package.json +3 -3
  104. package/src/accordion/Accordion.test.js +0 -6
  105. package/src/accordion/AccordionItem/AccordionItem.test.js +0 -10
  106. package/src/actionButton/ActionButton.test.tsx +0 -4
  107. package/src/avatarLayout/AvatarLayout.story.tsx +3 -3
  108. package/src/avatarView/AvatarView.story.tsx +29 -24
  109. package/src/avatarView/AvatarView.tsx +1 -1
  110. package/src/avatarWrapper/AvatarWrapper.test.tsx +0 -53
  111. package/src/checkbox/Checkbox.test.tsx +0 -5
  112. package/src/chevron/Chevron.test.tsx +0 -7
  113. package/src/chips/Chips.test.tsx +0 -8
  114. package/src/common/RadioButton/RadioButton.test.tsx +0 -18
  115. package/src/common/bottomSheet/BottomSheet.test.story.tsx +98 -0
  116. package/src/common/bottomSheet/BottomSheet.test.tsx +0 -9
  117. package/src/common/card/Card.test.tsx +0 -6
  118. package/src/common/closeButton/CloseButton.test.tsx +0 -4
  119. package/src/common/locale/index.test.ts +36 -1
  120. package/src/common/locale/index.ts +13 -0
  121. package/src/common/panel/Panel.test.tsx +0 -6
  122. package/src/expressiveMoneyInput/currencySelector/CurrencySelector.tsx +5 -1
  123. package/src/flowNavigation/FlowNavigation.test.js +0 -10
  124. package/src/index.ts +3 -1
  125. package/src/inputs/Input.tsx +8 -9
  126. package/src/inputs/SearchInput.tsx +8 -9
  127. package/src/inputs/SelectInput.test.story.tsx +86 -0
  128. package/src/inputs/SelectInput.tsx +1 -1
  129. package/src/inputs/TextArea.tsx +6 -7
  130. package/src/listItem/ListItem.tsx +2 -2
  131. package/src/listItem/Prompt/ListItemPrompt.story.tsx +71 -9
  132. package/src/listItem/Prompt/ListItemPrompt.test.tsx +31 -0
  133. package/src/listItem/Prompt/ListItemPrompt.tsx +8 -2
  134. package/src/logo/Logo.story.tsx +24 -5
  135. package/src/main.css +52 -21
  136. package/src/main.less +2 -1
  137. package/src/moneyInput/MoneyInput.test.story.tsx +104 -0
  138. package/src/moneyInput/MoneyInput.tsx +20 -2
  139. package/src/overlayHeader/OverlayHeader.test.tsx +0 -3
  140. package/src/popover/Popover.test.tsx +0 -25
  141. package/src/primitives/PrimitiveAnchor/PrimitiveAnchor.types.ts +1 -3
  142. package/src/primitives/PrimitiveButton/PrimitiveButton.types.ts +1 -3
  143. package/src/promoCard/PromoCard.test.tsx +0 -6
  144. package/src/promoCard/PromoCardGroup.test.tsx +0 -5
  145. package/src/prompt/ActionPrompt/ActionPrompt.accessibility.docs.mdx +65 -0
  146. package/src/prompt/ActionPrompt/ActionPrompt.less +1 -1
  147. package/src/prompt/ActionPrompt/ActionPrompt.story.tsx +4 -1
  148. package/src/prompt/ActionPrompt/ActionPrompt.test.story.tsx +147 -0
  149. package/src/prompt/ActionPrompt/ActionPrompt.test.tsx +2 -7
  150. package/src/prompt/ActionPrompt/ActionPrompt.tsx +48 -7
  151. package/src/prompt/InfoPrompt/InfoPrompt.css +31 -0
  152. package/src/prompt/InfoPrompt/InfoPrompt.less +37 -0
  153. package/src/prompt/InfoPrompt/InfoPrompt.story.tsx +312 -0
  154. package/src/prompt/InfoPrompt/InfoPrompt.test.story.tsx +246 -0
  155. package/src/prompt/InfoPrompt/InfoPrompt.test.tsx +224 -0
  156. package/src/prompt/InfoPrompt/InfoPrompt.tsx +148 -0
  157. package/src/prompt/InfoPrompt/index.ts +2 -0
  158. package/src/prompt/InlinePrompt/InlinePrompt.story.tsx +13 -10
  159. package/src/prompt/InlinePrompt/InlinePrompt.test.tsx +13 -1
  160. package/src/prompt/PrimitivePrompt/PrimitivePrompt.less +1 -1
  161. package/src/prompt/PrimitivePrompt/PrimitivePrompt.tsx +5 -5
  162. package/src/prompt/index.ts +5 -0
  163. package/src/radioOption/RadioOption.tsx +2 -1
  164. package/src/sentimentSurface/SentimentSurface.css +21 -21
  165. package/src/sentimentSurface/SentimentSurface.less +13 -13
  166. package/src/sentimentSurface/SentimentSurface.story.tsx +1 -1
  167. package/src/sentimentSurface/SentimentSurface.test.story.tsx +48 -1
  168. package/src/slidingPanel/SlidingPanel.tsx +4 -2
  169. package/src/ssr.test.tsx +2 -0
  170. package/src/statusIcon/StatusIcon.tsx +8 -1
  171. package/src/table/TableCell.tsx +1 -3
  172. package/src/tile/Tile.test.tsx +0 -10
  173. package/src/tooltip/Tooltip.test.tsx +0 -10
  174. package/src/withDisplayFormat/WithDisplayFormat.tsx +13 -14
  175. package/src/accordion/AccordionItem/__snapshots__/AccordionItem.test.js.snap +0 -124
  176. package/src/accordion/__snapshots__/Accordion.test.js.snap +0 -3
  177. package/src/actionButton/__snapshots__/ActionButton.test.tsx.snap +0 -12
  178. package/src/avatarWrapper/__snapshots__/AvatarWrapper.test.tsx.snap +0 -156
  179. package/src/checkbox/__snapshots__/Checkbox.test.tsx.snap +0 -40
  180. package/src/chevron/__snapshots__/Chevron.test.tsx.snap +0 -24
  181. package/src/chips/__snapshots__/Chips.test.tsx.snap +0 -153
  182. package/src/common/RadioButton/__snapshots__/RadioButton.test.tsx.snap +0 -58
  183. package/src/common/bottomSheet/__snapshots__/BottomSheet.test.tsx.snap +0 -80
  184. package/src/common/card/__snapshots__/Card.test.tsx.snap +0 -10
  185. package/src/common/closeButton/__snapshots__/CloseButton.test.tsx.snap +0 -30
  186. package/src/common/flowHeader/FlowHeader.test.tsx +0 -22
  187. package/src/common/flowHeader/__snapshots__/FlowHeader.test.tsx.snap +0 -33
  188. package/src/common/panel/__snapshots__/Panel.test.tsx.snap +0 -3
  189. package/src/flowNavigation/__snapshots__/FlowNavigation.test.js.snap +0 -262
  190. package/src/logo/Logo.test.tsx +0 -55
  191. package/src/logo/__snapshots__/Logo.test.tsx.snap +0 -281
  192. package/src/overlayHeader/__snapshots__/OverlayHeader.test.tsx.snap +0 -65
  193. package/src/popover/__snapshots__/Popover.test.tsx.snap +0 -51
  194. package/src/promoCard/__snapshots__/PromoCard.test.tsx.snap +0 -40
  195. package/src/promoCard/__snapshots__/PromoCardGroup.test.tsx.snap +0 -80
  196. package/src/tile/__snapshots__/Tile.test.tsx.snap +0 -55
  197. package/src/tooltip/__snapshots__/Tooltip.test.tsx.snap +0 -32
@@ -0,0 +1,86 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import { fireEvent, fn, type Mock, within } from 'storybook/test';
3
+ import { allModes } from '../../.storybook/modes';
4
+ import { lorem5, lorem500 } from '../test-utils';
5
+ import { Field } from '../field/Field';
6
+ import Body from '../body';
7
+ import { SelectInput, type SelectInputProps } from './SelectInput';
8
+
9
+ const meta = {
10
+ title: 'Forms/SelectInput/tests',
11
+ component: SelectInput,
12
+ args: {
13
+ onFilterChange: fn() satisfies Mock,
14
+ onChange: fn() satisfies Mock,
15
+ onClose: fn() satisfies Mock,
16
+ onOpen: fn() satisfies Mock,
17
+ },
18
+ tags: ['!autodocs'],
19
+ } satisfies Meta<typeof SelectInput>;
20
+ export default meta;
21
+
22
+ type Story<T, M extends boolean = false> = StoryObj<SelectInputProps<T, M>>;
23
+
24
+ const wait = async (duration = 500) =>
25
+ new Promise<void>((resolve) => {
26
+ setTimeout(resolve, duration);
27
+ });
28
+
29
+ /**
30
+ * This test ensures that when the SelectInput is used within a scrollable page,
31
+ * opening the dropdown does not cause any unwanted scrolling or layout shifts.
32
+ * Expected preview should start with green bar at the top, with yellow section
33
+ * not in the viewport. The issue is particularly prominent on iOS Safari.
34
+ *
35
+ * NB: This test is disabled in Chromatic as there's no obvious way to control
36
+ * <html/> element of a snapshot. It's to be primarily used in manual testing
37
+ * on an actual device or a simulator as it cannot be reproduced with mobile
38
+ * emulation modes on desktop browsers.
39
+ */
40
+ export const SmoothScrollReset: Story<string> = {
41
+ args: {
42
+ items: Array.from({ length: 15 }).map((_, id) => ({
43
+ type: 'option',
44
+ value: `option ${id + 1}`,
45
+ })),
46
+ placeholder: 'Select option',
47
+ },
48
+ decorators: [
49
+ (Story) => (
50
+ <>
51
+ <style>{`html { scroll-behavior: smooth; }`}</style>
52
+ <div>
53
+ <div
54
+ className="d-flex align-items-center justify-content-center"
55
+ style={{
56
+ backgroundColor: 'var(--color-bright-yellow)',
57
+ height: 400,
58
+ }}
59
+ >
60
+ This block should not be in the viewport.
61
+ </div>
62
+ <div style={{ height: 10, backgroundColor: 'var(--color-bright-green)' }} />
63
+ <Field id="el1" label={lorem5}>
64
+ <Story />
65
+ </Field>
66
+ <Body as="p">{lorem500}</Body>
67
+ </div>
68
+ </>
69
+ ),
70
+ ],
71
+ play: async ({ canvasElement }) => {
72
+ document.documentElement.scrollTop = 400;
73
+ await wait();
74
+ const canvas = within(canvasElement);
75
+ // cannot use userEvent.click as it crashes on iOS Safari in the simulator
76
+ await fireEvent.click(canvas.getByRole('combobox'));
77
+ },
78
+ globals: {
79
+ viewport: { value: allModes.largeMobile.viewport, isRotated: false },
80
+ },
81
+ parameters: {
82
+ chromatic: {
83
+ disableSnapshot: true,
84
+ },
85
+ },
86
+ };
@@ -1127,7 +1127,7 @@ function SelectInputOption<T = string>({ value, disabled, children }: SelectInpu
1127
1127
  const SelectInputOptionContentWithinTriggerContext = createContext(false);
1128
1128
 
1129
1129
  export interface SelectInputOptionContentProps {
1130
- title: string;
1130
+ title: React.ReactNode;
1131
1131
  note?: string;
1132
1132
  description?: string;
1133
1133
  icon?: React.ReactNode;
@@ -5,13 +5,12 @@ import { Merge } from '../utils';
5
5
  import { inputClassNameBase } from './_common';
6
6
  import { useInputAttributes } from './contexts';
7
7
 
8
- export interface TextAreaProps
9
- extends Merge<
10
- React.ComponentPropsWithRef<'textarea'>,
11
- {
12
- 'aria-invalid'?: boolean;
13
- }
14
- > {}
8
+ export interface TextAreaProps extends Merge<
9
+ React.ComponentPropsWithRef<'textarea'>,
10
+ {
11
+ 'aria-invalid'?: boolean;
12
+ }
13
+ > {}
15
14
 
16
15
  export const TextArea = forwardRef(function TextArea(
17
16
  { className, ...restProps }: TextAreaProps,
@@ -406,7 +406,7 @@ function View({
406
406
  aria-describedby={describedByIds}
407
407
  href={(controlProps as ListItemNavigationProps)?.href}
408
408
  target={(controlProps as ListItemNavigationProps)?.target}
409
- className={clsx('wds-list-item-view d-flex flex-row', {
409
+ className={clsx('wds-list-item-view', {
410
410
  'wds-list-item-control': controlType === 'navigation',
411
411
  fullyInteractive: !isPartiallyInteractive,
412
412
  })}
@@ -424,7 +424,7 @@ function View({
424
424
  if (isPartiallyInteractive || controlType === 'non-interactive') {
425
425
  return (
426
426
  <div className={clsx('wds-list-item-gridWrapper', className)}>
427
- <div className={clsx('wds-list-item-view d-flex flex-row')}>{children}</div>
427
+ <div className="wds-list-item-view">{children}</div>
428
428
 
429
429
  {renderExtras()}
430
430
  </div>
@@ -11,6 +11,7 @@ import {
11
11
  } from '../_stories/subcomponents';
12
12
  import { ListItem } from '../ListItem';
13
13
  import { Prompt, type ListItemPromptProps } from './ListItemPrompt';
14
+ import { Clock } from '@transferwise/icons';
14
15
 
15
16
  const meta: Meta<ListItemPromptProps> = {
16
17
  component: Prompt,
@@ -66,7 +67,7 @@ export const Playground: Story = {
66
67
  subtitle={lorem10}
67
68
  media={MEDIA.avatarSingle}
68
69
  control={CONTROLS.switch}
69
- prompt={<Prompt {...args} />}
70
+ prompt={<ListItem.Prompt {...args} />}
70
71
  />
71
72
  </List>
72
73
  ),
@@ -86,28 +87,89 @@ export const Sentiment: Story = {
86
87
  subtitle={lorem10}
87
88
  media={MEDIA.avatarSingle}
88
89
  control={CONTROLS.switch}
89
- prompt={<Prompt sentiment={Sentiments.NEUTRAL}>This is a neutral prompt.</Prompt>}
90
+ prompt={
91
+ <ListItem.Prompt sentiment={Sentiments.NEUTRAL}>
92
+ This is a neutral prompt.
93
+ </ListItem.Prompt>
94
+ }
90
95
  />
91
96
  <ListItem
92
97
  title={lorem5}
93
98
  subtitle={lorem10}
94
99
  media={MEDIA.avatarSingle}
95
100
  control={CONTROLS.switch}
96
- prompt={<Prompt sentiment={Sentiments.POSITIVE}>This is a positive prompt.</Prompt>}
101
+ prompt={
102
+ <ListItem.Prompt sentiment={Sentiments.POSITIVE}>
103
+ This is a positive prompt.
104
+ </ListItem.Prompt>
105
+ }
97
106
  />
98
107
  <ListItem
99
108
  title={lorem5}
100
109
  subtitle={lorem10}
101
110
  media={MEDIA.avatarSingle}
102
111
  control={CONTROLS.switch}
103
- prompt={<Prompt sentiment={Sentiments.WARNING}>This is a warning prompt.</Prompt>}
112
+ prompt={
113
+ <ListItem.Prompt sentiment={Sentiments.WARNING}>
114
+ This is a warning prompt.
115
+ </ListItem.Prompt>
116
+ }
104
117
  />
105
118
  <ListItem
106
119
  title={lorem5}
107
120
  subtitle={lorem10}
108
121
  media={MEDIA.avatarSingle}
109
122
  control={CONTROLS.switch}
110
- prompt={<Prompt sentiment={Sentiments.NEGATIVE}>This is a negative prompt.</Prompt>}
123
+ prompt={
124
+ <ListItem.Prompt sentiment={Sentiments.NEGATIVE}>
125
+ This is a negative prompt.
126
+ </ListItem.Prompt>
127
+ }
128
+ />
129
+ </List>
130
+ ),
131
+ };
132
+
133
+ /**
134
+ * While all main sentiments (`warning`, `negative`, `positive` and `neutral`) are associated with a
135
+ * default `StatusIcon`s, we also allow for Icon overrides to bring the prompt's visual language
136
+ * closer to the prompt's content. <br /><br />
137
+ * It's also possible to override the default StatusIcon's accessible name announced by screen
138
+ * readers via `mediaLabel` prop, which is especially useful for the `proposition` sentiment.
139
+ * <br /><br />
140
+ * **NB**: If you're setting a label on a custom Icon, the accessible name should be provided via
141
+ * Icon's `title` prop instead.
142
+ */
143
+ export const IconOverrides: Story = {
144
+ parameters: {
145
+ controls: { disable: true },
146
+ actions: { disable: true },
147
+ a11y: { disable: true },
148
+ knobs: { disable: true },
149
+ },
150
+ render: (args) => (
151
+ <List>
152
+ <ListItem
153
+ title={lorem5}
154
+ subtitle={lorem10}
155
+ media={MEDIA.avatarSingle}
156
+ control={CONTROLS.switch}
157
+ prompt={
158
+ <ListItem.Prompt sentiment={Sentiments.WARNING} mediaLabel="Processing: ">
159
+ This prompt uses default Icon, but with a custom label for screen readers.
160
+ </ListItem.Prompt>
161
+ }
162
+ />
163
+ <ListItem
164
+ title={lorem5}
165
+ subtitle={lorem10}
166
+ media={MEDIA.avatarSingle}
167
+ control={CONTROLS.switch}
168
+ prompt={
169
+ <ListItem.Prompt sentiment={Sentiments.WARNING} media={<Clock title="Processing: " />}>
170
+ This prompt uses custom Icon with a custom label for screen readers.
171
+ </ListItem.Prompt>
172
+ }
111
173
  />
112
174
  </List>
113
175
  ),
@@ -135,13 +197,13 @@ export const Interactivity: Story = {
135
197
  media={MEDIA.avatarSingle}
136
198
  control={CONTROLS.switch}
137
199
  prompt={
138
- <Prompt sentiment={args.sentiment}>
200
+ <ListItem.Prompt sentiment={args.sentiment}>
139
201
  This prompt includes a{' '}
140
202
  <Link href="https://wise.com" target="_blank" rel="noreferrer">
141
203
  link to some resource
142
204
  </Link>{' '}
143
205
  to help the user in their journey.
144
- </Prompt>
206
+ </ListItem.Prompt>
145
207
  }
146
208
  />
147
209
 
@@ -151,13 +213,13 @@ export const Interactivity: Story = {
151
213
  media={MEDIA.avatarSingle}
152
214
  control={CONTROLS.switch}
153
215
  prompt={
154
- <Prompt sentiment={args.sentiment}>
216
+ <ListItem.Prompt sentiment={args.sentiment}>
155
217
  {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
156
218
  This prompt includes an <Link onClick={action('inline button')}>
157
219
  inline button
158
220
  </Link>{' '}
159
221
  than can e.g. trigger a modal.
160
- </Prompt>
222
+ </ListItem.Prompt>
161
223
  }
162
224
  />
163
225
  </List>
@@ -1,3 +1,4 @@
1
+ import { Clock } from '@transferwise/icons';
1
2
  import { mockMatchMedia, render, screen } from '../../test-utils';
2
3
  import { Sentiment } from '../../common';
3
4
  import { ListItem } from '../ListItem';
@@ -18,6 +19,8 @@ describe('ListItem.Prompt', () => {
18
19
  });
19
20
 
20
21
  describe('render icon', () => {
22
+ const customLabel = 'Custom icon label';
23
+
21
24
  it.each([
22
25
  [Sentiment.NEUTRAL, 'info-icon'],
23
26
  [Sentiment.POSITIVE, 'check-icon'],
@@ -32,6 +35,34 @@ describe('ListItem.Prompt', () => {
32
35
  );
33
36
  expect(screen.getByTestId(iconId)).toBeInTheDocument();
34
37
  });
38
+
39
+ it('should accept accessible name override for a status icon', () => {
40
+ render(
41
+ <ListItem
42
+ title="Test Title"
43
+ prompt={
44
+ <ListItem.Prompt sentiment={Sentiment.NEGATIVE} mediaLabel={customLabel}>
45
+ Message
46
+ </ListItem.Prompt>
47
+ }
48
+ />,
49
+ );
50
+ expect(screen.getByLabelText(customLabel)).toBeInTheDocument();
51
+ });
52
+
53
+ it('should accept icon accessible name override', () => {
54
+ render(
55
+ <ListItem
56
+ title="Test Title"
57
+ prompt={
58
+ <ListItem.Prompt sentiment={Sentiment.NEGATIVE} media={<Clock title={customLabel} />}>
59
+ Message
60
+ </ListItem.Prompt>
61
+ }
62
+ />,
63
+ );
64
+ expect(screen.getByLabelText(customLabel)).toBeInTheDocument();
65
+ });
35
66
  });
36
67
 
37
68
  describe('muted state', () => {
@@ -3,7 +3,10 @@ import { Sentiment } from '../../common';
3
3
  import { ListItemContext, type ListItemContextData } from '../ListItemContext';
4
4
  import { InlinePrompt, type InlinePromptProps } from '../../prompt';
5
5
 
6
- export type ListItemPromptProps = Pick<InlinePromptProps, 'children' | 'sentiment' | 'mediaLabel'>;
6
+ export type ListItemPromptProps = Pick<
7
+ InlinePromptProps,
8
+ 'children' | 'sentiment' | 'mediaLabel' | 'media' | 'loading'
9
+ >;
7
10
 
8
11
  /**
9
12
  * This component allows for rendering an Inline Prompt. <br />
@@ -15,6 +18,8 @@ export const Prompt = ({
15
18
  sentiment = Sentiment.NEUTRAL,
16
19
  mediaLabel,
17
20
  children,
21
+ media,
22
+ loading,
18
23
  }: ListItemPromptProps) => {
19
24
  const { ids, props } = useContext<ListItemContextData>(ListItemContext);
20
25
  const isLongLivedMuted = props.disabled && Boolean(props.disabledPromptMessage);
@@ -23,6 +28,8 @@ export const Prompt = ({
23
28
  <InlinePrompt
24
29
  id={ids.prompt}
25
30
  sentiment={sentiment}
31
+ media={media}
32
+ loading={loading}
26
33
  mediaLabel={mediaLabel}
27
34
  muted={isLongLivedMuted}
28
35
  className="wds-list-item-prompt"
@@ -33,4 +40,3 @@ export const Prompt = ({
33
40
  };
34
41
 
35
42
  Prompt.displayName = 'ListItem.Prompt';
36
- export default Prompt;
@@ -1,15 +1,29 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
 
3
- import Logo from '.';
3
+ import Logo, { LogoType } from '.';
4
+ import { withVariantConfig } from '../../.storybook/helpers';
4
5
 
5
6
  export default {
6
7
  component: Logo,
7
8
  title: 'Content/Logo',
8
- render: ({ inverse, type }) => {
9
+ render: () => {
9
10
  return (
10
- <div className={`${inverse ? 'bg--dark' : ''} p-a-3`}>
11
- <Logo type={type} inverse={inverse} />
12
- </div>
11
+ <>
12
+ <div>
13
+ {Object.values(LogoType).map((type) => (
14
+ <div key={type} className="m-a-2">
15
+ <Logo type={type} />
16
+ </div>
17
+ ))}
18
+ </div>
19
+ <div className="bg--dark">
20
+ {Object.values(LogoType).map((type) => (
21
+ <div key={type} className="m-a-2">
22
+ <Logo type={type} inverse />
23
+ </div>
24
+ ))}
25
+ </div>
26
+ </>
13
27
  );
14
28
  },
15
29
  } satisfies Meta<typeof Logo>;
@@ -22,3 +36,8 @@ export const Basic: Story = {
22
36
  inverse: false,
23
37
  },
24
38
  };
39
+
40
+ export const Mobile: Story = {
41
+ ...Basic,
42
+ ...withVariantConfig(['mobile']),
43
+ };
package/src/main.css CHANGED
@@ -304,18 +304,18 @@
304
304
  --color-sentiment-interactive-primary: #454745;
305
305
  --color-sentiment-interactive-primary-hover: #353635;
306
306
  --color-sentiment-interactive-primary-active: #232423;
307
- --color-sentiment-interactive-secondary: #F1F1ED;
308
- --color-sentiment-interactive-secondary-hover: #E7E7E1;
309
- --color-sentiment-interactive-secondary-active: #DFDED5;
310
- --color-sentiment-interactive-secondary-neutral: #E4E4DC;
311
- --color-sentiment-interactive-secondary-neutral-hover: #DCDCD2;
312
- --color-sentiment-interactive-secondary-neutral-active: #D3D2C6;
307
+ --color-sentiment-interactive-secondary: rgba(62, 59, 7, 0.07);
308
+ --color-sentiment-interactive-secondary-hover: rgba(62, 59, 7, 0.12);
309
+ --color-sentiment-interactive-secondary-active: rgba(62, 59, 7, 0.17);
310
+ --color-sentiment-interactive-secondary-neutral: rgba(62, 59, 7, 0.07);
311
+ --color-sentiment-interactive-secondary-neutral-hover: rgba(62, 59, 7, 0.12);
312
+ --color-sentiment-interactive-secondary-neutral-active: rgba(62, 59, 7, 0.17);
313
313
  --color-sentiment-interactive-control: #F1F1ED;
314
314
  --color-sentiment-interactive-control-hover: #E7E7E1;
315
315
  --color-sentiment-interactive-control-active: #DFDED5;
316
- --color-sentiment-background-surface: #F1F1ED;
317
- --color-sentiment-background-surface-hover: #E7E7E1;
318
- --color-sentiment-background-surface-active: #DFDED5;
316
+ --color-sentiment-background-surface: rgba(62, 59, 7, 0.07);
317
+ --color-sentiment-background-surface-hover: rgba(62, 59, 7, 0.12);
318
+ --color-sentiment-background-surface-active: rgba(62, 59, 7, 0.17);
319
319
  }
320
320
  .np-theme-personal .wds-sentiment-surface-neutral-elevated,
321
321
  .np-theme-business .wds-sentiment-surface-neutral-elevated,
@@ -331,9 +331,9 @@
331
331
  --color-sentiment-interactive-secondary: #454745;
332
332
  --color-sentiment-interactive-secondary-hover: #353635;
333
333
  --color-sentiment-interactive-secondary-active: #232423;
334
- --color-sentiment-interactive-secondary-neutral: #5A5C5A;
335
- --color-sentiment-interactive-secondary-neutral-hover: #6D6F6D;
336
- --color-sentiment-interactive-secondary-neutral-active: #727472;
334
+ --color-sentiment-interactive-secondary-neutral: #585958;
335
+ --color-sentiment-interactive-secondary-neutral-hover: #6A6C6A;
336
+ --color-sentiment-interactive-secondary-neutral-active: #7D7E7D;
337
337
  --color-sentiment-interactive-control: #454745;
338
338
  --color-sentiment-interactive-control-hover: #353635;
339
339
  --color-sentiment-interactive-control-active: #232423;
@@ -352,18 +352,18 @@
352
352
  --color-sentiment-interactive-primary: #F1F1ED;
353
353
  --color-sentiment-interactive-primary-hover: #E7E7E1;
354
354
  --color-sentiment-interactive-primary-active: #DFDED5;
355
- --color-sentiment-interactive-secondary: #2A2C29;
356
- --color-sentiment-interactive-secondary-hover: #414441;
357
- --color-sentiment-interactive-secondary-active: #595B58;
358
- --color-sentiment-interactive-secondary-neutral: #424441;
359
- --color-sentiment-interactive-secondary-neutral-hover: #4C4E4B;
360
- --color-sentiment-interactive-secondary-neutral-active: #565955;
355
+ --color-sentiment-interactive-secondary: rgba(255, 255, 255, 0.1);
356
+ --color-sentiment-interactive-secondary-hover: rgba(255, 255, 255, 0.2);
357
+ --color-sentiment-interactive-secondary-active: rgba(255, 255, 255, 0.3);
358
+ --color-sentiment-interactive-secondary-neutral: rgba(255, 255, 255, 0.1);
359
+ --color-sentiment-interactive-secondary-neutral-hover: rgba(255, 255, 255, 0.2);
360
+ --color-sentiment-interactive-secondary-neutral-active: rgba(255, 255, 255, 0.3);
361
361
  --color-sentiment-interactive-control: #2A2C29;
362
362
  --color-sentiment-interactive-control-hover: #414441;
363
363
  --color-sentiment-interactive-control-active: #595B58;
364
- --color-sentiment-background-surface: #2A2C29;
365
- --color-sentiment-background-surface-hover: #414441;
366
- --color-sentiment-background-surface-active: #595B58;
364
+ --color-sentiment-background-surface: rgba(255, 255, 255, 0.1);
365
+ --color-sentiment-background-surface-hover: rgba(255, 255, 255, 0.2);
366
+ --color-sentiment-background-surface-active: rgba(255, 255, 255, 0.3);
367
367
  }
368
368
  .np-theme-personal--dark .wds-sentiment-surface-neutral-elevated,
369
369
  .np-theme-business--dark .wds-sentiment-surface-neutral-elevated,
@@ -5441,6 +5441,37 @@ html:not([dir="rtl"]) .np-navigation-option {
5441
5441
  .wds-inline-prompt .wds-inline-prompt-process-indicator .process-circle {
5442
5442
  stroke: currentColor;
5443
5443
  }
5444
+ .wds-info-prompt {
5445
+ --Prompt-gap: var(--size-8);
5446
+ --Prompt-padding: 12px;
5447
+ }
5448
+ .wds-info-prompt__content {
5449
+ display: flex;
5450
+ flex-direction: column;
5451
+ justify-content: center;
5452
+ }
5453
+ .wds-info-prompt__content:has(.wds-info-prompt__title) {
5454
+ justify-content: flex-start;
5455
+ /* Top align when title exists */
5456
+ }
5457
+ .wds-info-prompt__title,
5458
+ .wds-info-prompt__description {
5459
+ display: block;
5460
+ color: var(--color-sentiment-primary);
5461
+ }
5462
+ .wds-info-prompt__action {
5463
+ margin-top: var(--Prompt-gap);
5464
+ }
5465
+ .wds-info-prompt__media {
5466
+ display: flex;
5467
+ }
5468
+ .wds-info-prompt__media svg {
5469
+ width: 24px;
5470
+ height: 24px;
5471
+ }
5472
+ .wds-info-prompt .wds-prompt__media-wrapper {
5473
+ padding: 0;
5474
+ }
5444
5475
  .wds-radio-group .np-radio:last-child label {
5445
5476
  margin-bottom: 0;
5446
5477
  }
package/src/main.less CHANGED
@@ -62,6 +62,7 @@
62
62
  @import "./promoCard/PromoCard.less";
63
63
  @import "./prompt/PrimitivePrompt/PrimitivePrompt.less";
64
64
  @import "./prompt/InlinePrompt/InlinePrompt.less";
65
+ @import "./prompt/InfoPrompt/InfoPrompt.less";
65
66
  @import "./radioGroup/RadioGroup.less";
66
67
  @import "./section/Section.less";
67
68
  @import "./slidingPanel/SlidingPanel.less";
@@ -89,4 +90,4 @@
89
90
  @import "./prompt/ActionPrompt/ActionPrompt.less";
90
91
 
91
92
  // List all less files in src in alphabetical order: find -s src -type f -name '*.less' ! -name 'main.less'
92
- // Make sure you are not referencing main.less itself in this file!
93
+ // Make sure you are not referencing main.less itself in this file!
@@ -0,0 +1,104 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import { within, fireEvent } from 'storybook/test';
3
+ import { allModes } from '../../.storybook/modes';
4
+ import { lorem500 } from '../test-utils';
5
+ import { Field } from '../field/Field';
6
+ import Body from '../body';
7
+ import MoneyInput from './MoneyInput';
8
+
9
+ const meta = {
10
+ title: 'Forms/MoneyInput/tests',
11
+ component: MoneyInput,
12
+ args: {
13
+ amount: 1000,
14
+ id: 'moneyInput',
15
+ currencies: [
16
+ {
17
+ value: 'EUR',
18
+ label: 'EUR',
19
+ note: 'Euro',
20
+ currency: 'eur',
21
+ searchable: 'Spain, Germany, France, Austria',
22
+ },
23
+ {
24
+ value: 'GBP',
25
+ label: 'GBP',
26
+ note: 'British pound',
27
+ currency: 'gbp',
28
+ searchable: 'England, Scotland, Wales',
29
+ },
30
+ ],
31
+ selectedCurrency: {
32
+ value: 'EUR',
33
+ label: 'EUR',
34
+ note: 'Euro',
35
+ currency: 'eur',
36
+ searchable: 'Spain, Germany, France, Austria',
37
+ },
38
+ searchPlaceholder: '',
39
+ onAmountChange: () => {},
40
+ onCurrencyChange: () => {},
41
+ },
42
+ tags: ['!autodocs'],
43
+ } satisfies Meta<typeof MoneyInput>;
44
+ export default meta;
45
+
46
+ type Story = StoryObj<typeof MoneyInput>;
47
+
48
+ const wait = async (duration = 500) =>
49
+ new Promise<void>((resolve) => {
50
+ setTimeout(resolve, duration);
51
+ });
52
+
53
+ /**
54
+ * This test ensures that when the SelectInput is used within a scrollable page,
55
+ * opening the dropdown does not cause any unwanted scrolling or layout shifts.
56
+ * Expected preview should start with green bar at the top, with yellow section
57
+ * not in the viewport. The issue is particularly prominent on iOS Safari.
58
+ *
59
+ * NB: This test is disabled in Chromatic as there's no obvious way to control
60
+ * <html/> element of a snapshot. It's to be primarily used in manual testing
61
+ * on an actual device or a simulator as it cannot be reproduced with mobile
62
+ * emulation modes on desktop browsers.
63
+ */
64
+ export const SmoothScrollReset: Story = {
65
+ decorators: [
66
+ (Story) => (
67
+ <>
68
+ <style>{`html { scroll-behavior: smooth; }`}</style>
69
+ <div>
70
+ <div
71
+ className="d-flex align-items-center justify-content-center"
72
+ style={{
73
+ backgroundColor: 'var(--color-bright-yellow)',
74
+ height: 400,
75
+ }}
76
+ >
77
+ This block should not be in the viewport.
78
+ </div>
79
+ <div style={{ height: 10, backgroundColor: 'var(--color-bright-green)' }} />
80
+ <Field id="el1" label="Select currency">
81
+ <Story />
82
+ </Field>
83
+ <Body as="p">{lorem500}</Body>
84
+ </div>
85
+ </>
86
+ ),
87
+ ],
88
+ play: async ({ canvasElement }) => {
89
+ await wait();
90
+ document.documentElement.scrollTop = 400;
91
+ await wait();
92
+ const canvas = within(canvasElement);
93
+ // cannot use userEvent.click as it crashes on iOS Safari in the simulator
94
+ await fireEvent.click(canvas.getByRole('combobox'));
95
+ },
96
+ globals: {
97
+ viewport: { value: allModes.largeMobile.viewport, isRotated: false },
98
+ },
99
+ parameters: {
100
+ chromatic: {
101
+ disableSnapshot: true,
102
+ },
103
+ },
104
+ };
@@ -4,7 +4,14 @@ import { clsx } from 'clsx';
4
4
  import { Component } from 'react';
5
5
  import { injectIntl, WrappedComponentProps } from 'react-intl';
6
6
 
7
- import { Typography, Size, SizeLarge, SizeMedium, SizeSmall } from '../common';
7
+ import {
8
+ Typography,
9
+ Size,
10
+ SizeLarge,
11
+ SizeMedium,
12
+ SizeSmall,
13
+ getLocaleCurrencyName,
14
+ } from '../common';
8
15
  import { withInputAttributes, WithInputAttributesProps } from '../inputs/contexts';
9
16
  import { Input } from '../inputs/Input';
10
17
  import {
@@ -372,6 +379,7 @@ class MoneyInput extends Component<MoneyInputPropsWithInputAttributes, MoneyInpu
372
379
  as="span"
373
380
  type={Typography.TITLE_SUBSECTION}
374
381
  className={size === 'lg' ? this.style('m-r-1') : ''}
382
+ aria-label={getLocaleCurrencyName(this.props.intl, selectedCurrency.currency)}
375
383
  >
376
384
  {selectedCurrency.currency.toUpperCase()}
377
385
  </Title>
@@ -399,7 +407,17 @@ class MoneyInput extends Component<MoneyInputPropsWithInputAttributes, MoneyInpu
399
407
  renderValue={(currency, withinTrigger) => {
400
408
  return (
401
409
  <SelectInputOptionContent
402
- title={withinTrigger ? currency.currency.toUpperCase() : currency.label}
410
+ title={
411
+ withinTrigger ? (
412
+ <span
413
+ aria-label={getLocaleCurrencyName(this.props.intl, currency.currency)}
414
+ >
415
+ {currency.currency.toUpperCase()}
416
+ </span>
417
+ ) : (
418
+ currency.label
419
+ )
420
+ }
403
421
  note={withinTrigger ? undefined : currency.note}
404
422
  icon={<Flag code={currency.currency} intrinsicSize={24} />}
405
423
  />