@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
@@ -11,9 +11,6 @@ describe('OverlayHeader', () => {
11
11
  logo: <img alt="logo_desktop" src="img_desktop" width="138" height="24" />,
12
12
  onClose: jest.fn(),
13
13
  };
14
- it('renders as expected', () => {
15
- expect(render(<OverlayHeader {...props} />).container).toMatchSnapshot();
16
- });
17
14
 
18
15
  it('renders separator only if avatar and onClose are provided', () => {
19
16
  const { container } = render(<OverlayHeader {...props} />);
@@ -22,19 +22,6 @@ describe('Popover', () => {
22
22
  let rerender;
23
23
 
24
24
  describe('on desktop', () => {
25
- it('renders when is open', async () => {
26
- render(
27
- <Popover {...props}>
28
- <button type="button">Open</button>
29
- </Popover>,
30
- );
31
-
32
- await userEvent.click(screen.getByText('Open'));
33
-
34
- await waitForPanel();
35
- expect(getPanel()).toMatchSnapshot();
36
- });
37
-
38
25
  describe('title', () => {
39
26
  it('renders title', async () => {
40
27
  ({ container, rerender } = render(
@@ -122,18 +109,6 @@ describe('Popover', () => {
122
109
  global.innerWidth = Breakpoint.SMALL - 1;
123
110
  });
124
111
 
125
- it('renders when is open', async () => {
126
- ({ container } = render(
127
- <Popover {...props}>
128
- <button type="button">Open</button>
129
- </Popover>,
130
- ));
131
-
132
- await userEvent.click(screen.getByText('Open'));
133
-
134
- expect(container).toMatchSnapshot();
135
- });
136
-
137
112
  it('renders BottomSheet onClick', async () => {
138
113
  render(
139
114
  <Popover {...props}>
@@ -11,9 +11,7 @@ export type PrimitiveAnchorElementRef = React.Ref<HTMLAnchorElement>;
11
11
  * Properties for the anchor component.
12
12
  */
13
13
  export interface PrimitiveAnchorProps
14
- extends BasePrimitiveProps,
15
- StyleProp,
16
- Omit<PrimitiveAnchorAttributes, 'role' | 'type'> {
14
+ extends BasePrimitiveProps, StyleProp, Omit<PrimitiveAnchorAttributes, 'role' | 'type'> {
17
15
  /** Content of the anchor */
18
16
  children?: ReactNode;
19
17
 
@@ -11,9 +11,7 @@ export type PrimitiveButtonElementRef = React.Ref<HTMLButtonElement>;
11
11
  * Properties for the Button component.
12
12
  */
13
13
  export interface PrimitiveButtonProps
14
- extends BasePrimitiveProps,
15
- StyleProp,
16
- PrimitiveButtonAttributes {
14
+ extends BasePrimitiveProps, StyleProp, PrimitiveButtonAttributes {
17
15
  /** Content of the button */
18
16
  children?: ReactNode;
19
17
 
@@ -26,12 +26,6 @@ describe('PromoCard', () => {
26
26
  };
27
27
  });
28
28
 
29
- it('matches snapshot', () => {
30
- const { container } = render(<PromoCard {...defaultProps} id="test-promo-card" />);
31
-
32
- expect(container.firstChild).toMatchSnapshot();
33
- });
34
-
35
29
  it('renders', () => {
36
30
  const props = {
37
31
  ...defaultProps,
@@ -41,11 +41,6 @@ describe('PromoCardGroup', () => {
41
41
  };
42
42
  });
43
43
 
44
- it('matches snapshot', () => {
45
- const { container } = render(<PromoCardGroup {...defaultProps} />);
46
- expect(container.firstChild).toMatchSnapshot();
47
- });
48
-
49
44
  it('renders', () => {
50
45
  const props = {
51
46
  ...defaultProps,
@@ -0,0 +1,65 @@
1
+ import { Meta, Canvas, Source } from '@storybook/addon-docs/blocks';
2
+
3
+ <Meta title="Prompts/ActionPrompt/Accessibility" />
4
+
5
+ # Accessibility
6
+
7
+ Under the hood, `ActionPrompt` is marked as `role="region"` for a section of content that users might want to navigate to directly.
8
+
9
+ By default, it's labelled by the `media` asset and `title`, as well as described by the `description` if one is provided.
10
+
11
+ ## Announcement Behaviour
12
+
13
+ `ActionPrompt` should be treated as a hint or suggestion as part of the product UX. Even the negative variant still serves as a non-critical message. Therefore, it shouldn't be announced via screen reader—neither assertively (`role="alert"`) nor politely (`role="status"`).
14
+
15
+ **Note:** For immediate user feedback, use [InfoPrompt](?path=/docs/prompts-infoprompt--docs) or [InlinePrompt](https://storybook.wise.design/?path=/docs/prompts-inlineprompt--docs) instead.
16
+
17
+ If you want to provide a custom label for screen readers, you can use the `aria-label` prop. Make sure to include all necessary information in it, because when provided, the default labelling will be removed.
18
+
19
+ <Source
20
+ dark
21
+ code={`
22
+ <ActionPrompt
23
+ aria-label="JFYI: Henry requested 30 USD for lunch"
24
+ media={{ avatar: { imgSrc: 'profile-photo.webp' } }}
25
+ title="Henry requested 30 USD"
26
+ description="Lunch date"
27
+ ...
28
+ />
29
+ `}
30
+ />
31
+
32
+ ## Media
33
+
34
+ You can use the `aria-hidden` attribute on media assets to hide them from screen readers if they're not providing any additional information. This is useful when the media is purely decorative or when its content is already conveyed through other means (e.g., text).
35
+
36
+ <Source
37
+ dark
38
+ code={`
39
+ <ActionPrompt
40
+ media={{
41
+ 'aria-hidden': true,
42
+ avatar: { asset: <People /> },
43
+ }}
44
+ title="Sync contacts for a faster experience"
45
+ ...
46
+ />
47
+ `}
48
+ />
49
+
50
+ You can also use `aria-label` on media assets to provide a custom label for screen readers.
51
+
52
+ <Source
53
+ dark
54
+ code={`
55
+ <ActionPrompt
56
+ media={{
57
+ 'aria-label': 'Henry Adams photo',
58
+ avatar: { imgSrc: 'profile-photo.webp' },
59
+ }}
60
+ title="Henry requested 30 USD"
61
+ description="Lunch date"
62
+ ...
63
+ />
64
+ `}
65
+ />
@@ -20,4 +20,4 @@
20
20
  }
21
21
  }
22
22
  }
23
- }
23
+ }
@@ -1,9 +1,12 @@
1
1
  import { ReactElement, useState } from 'react';
2
2
  import { Meta, StoryObj } from '@storybook/react-webpack5';
3
3
  import { fn } from 'storybook/test';
4
- import { Bank } from '@transferwise/icons';
4
+ import { Bank, Freeze, People } from '@transferwise/icons';
5
5
  import { ActionPrompt } from './ActionPrompt';
6
6
  import { lorem10 } from '../../test-utils';
7
+ import Body from '../../body';
8
+ import { action } from 'storybook/actions';
9
+ import Title from '../../title';
7
10
  import { withVariantConfig } from '../../../.storybook/helpers';
8
11
 
9
12
  const meta: Meta<typeof ActionPrompt> = {
@@ -0,0 +1,147 @@
1
+ import { Freeze, People } from '@transferwise/icons';
2
+ import { action } from 'storybook/actions';
3
+ import ActionPrompt from './ActionPrompt';
4
+ import { Body, Title } from '../..';
5
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
6
+
7
+ export default {
8
+ title: 'Prompts/ActionPrompt/Tests',
9
+ component: ActionPrompt,
10
+ tags: ['!manifest', '!autodocs'],
11
+ };
12
+
13
+ type Story = StoryObj<typeof ActionPrompt>;
14
+
15
+ export const VariousA11yFeatures: Story = {
16
+ name: 'Various a11y features',
17
+ render: () => {
18
+ return (
19
+ <Body>
20
+ <Title type="title-body">Neutral Prompt with Avatar Image and Custom label for media</Title>
21
+ <ActionPrompt
22
+ className="m-b-2"
23
+ sentiment="neutral"
24
+ media={{
25
+ 'aria-label': 'Henry Profile Photo',
26
+ avatar: { imgSrc: '../../avatar-square-dude.webp' },
27
+ }}
28
+ title="Henry requested 30 USD"
29
+ description="Lunch date"
30
+ action={{
31
+ label: 'Sent',
32
+ onClick: () => {
33
+ action('send');
34
+ },
35
+ }}
36
+ actionSecondary={{
37
+ label: 'Decline',
38
+ onClick: () => {
39
+ action('decline');
40
+ },
41
+ }}
42
+ onDismiss={() => action('dismiss')}
43
+ />
44
+
45
+ <Title type="title-body">Warning Prompt</Title>
46
+ <ActionPrompt
47
+ className="m-b-2"
48
+ sentiment="warning"
49
+ media={{
50
+ 'aria-label': 'Image of beautiful Business Wise Card',
51
+ imgSrc: '../../wise-card.svg',
52
+ }}
53
+ title="Your Wise Card expires soon"
54
+ description="Renew your card to keep spending"
55
+ action={{
56
+ label: 'Renew card',
57
+ onClick: () => {
58
+ action('renew');
59
+ },
60
+ }}
61
+ actionSecondary={{
62
+ label: 'Notify later',
63
+ onClick: () => {
64
+ action('notifyLater');
65
+ },
66
+ }}
67
+ onDismiss={() => action('dismiss')}
68
+ />
69
+
70
+ <Title type="title-body">Warning Prompt Avatar Icon</Title>
71
+ <ActionPrompt
72
+ className="m-b-2"
73
+ sentiment="warning"
74
+ media={{ avatar: { asset: <Freeze /> } }}
75
+ title="Your Wise Card expires soon"
76
+ description="Renew your card to keep spending"
77
+ action={{
78
+ label: 'Renew card',
79
+ onClick: () => {
80
+ action('renew');
81
+ },
82
+ }}
83
+ onDismiss={() => {
84
+ action('dismiss');
85
+ }}
86
+ />
87
+
88
+ <Title type="title-body">Proposition Prompt Avatar Icon</Title>
89
+ <ActionPrompt
90
+ className="m-b-2"
91
+ sentiment="proposition"
92
+ media={{ avatar: { asset: <People /> } }}
93
+ title="Sync contacts for a faster experience"
94
+ description="Find contacts on Wise — it’s simple, secure and you pick who you add."
95
+ action={{
96
+ label: 'Sync contacts',
97
+ onClick: () => {
98
+ action('sync');
99
+ },
100
+ }}
101
+ onDismiss={() => {
102
+ action('dismiss');
103
+ }}
104
+ />
105
+
106
+ <Title type="title-body">Negative Prompt Avatar Icon + muted media</Title>
107
+ <ActionPrompt
108
+ className="m-b-2"
109
+ sentiment="negative"
110
+ media={{ avatar: { asset: <People /> }, 'aria-hidden': true }}
111
+ title="Sync contacts for a faster experience"
112
+ description="Find contacts on Wise — it’s simple, secure and you pick who you add."
113
+ action={{
114
+ label: 'Sync contacts',
115
+ onClick: () => {
116
+ action('sync');
117
+ },
118
+ }}
119
+ onDismiss={() => {
120
+ action('dismiss');
121
+ }}
122
+ />
123
+
124
+ <Title type="title-body">
125
+ Negative Prompt + override content with custom message via aria-label
126
+ </Title>
127
+ <ActionPrompt
128
+ className="m-b-2"
129
+ aria-label="hey customer, here is custom message"
130
+ sentiment="negative"
131
+ media={{ avatar: { asset: <People /> } }}
132
+ title="Sync contacts for a faster experience"
133
+ description="Find contacts on Wise — it’s simple, secure and you pick who you add."
134
+ action={{
135
+ label: 'Sync contacts',
136
+ onClick: () => {
137
+ action('sync');
138
+ },
139
+ }}
140
+ onDismiss={() => {
141
+ action('dismiss');
142
+ }}
143
+ />
144
+ </Body>
145
+ );
146
+ },
147
+ };
@@ -56,9 +56,7 @@ describe('ActionPrompt', () => {
56
56
  it('calls primary action onClick when clicked', async () => {
57
57
  const onClick = jest.fn();
58
58
  const user = userEvent.setup();
59
- render(
60
- <ActionPrompt {...defaultProps} action={{ label: 'Primary Action', onClick }} />,
61
- );
59
+ render(<ActionPrompt {...defaultProps} action={{ label: 'Primary Action', onClick }} />);
62
60
  const button = screen.getByRole('button', { name: 'Primary Action' });
63
61
  await user.click(button);
64
62
  expect(onClick).toHaveBeenCalledTimes(1);
@@ -66,10 +64,7 @@ describe('ActionPrompt', () => {
66
64
 
67
65
  it('renders primary action as link when href is provided', () => {
68
66
  render(
69
- <ActionPrompt
70
- {...defaultProps}
71
- action={{ label: 'Primary Action', href: '/test-url' }}
72
- />,
67
+ <ActionPrompt {...defaultProps} action={{ label: 'Primary Action', href: '/test-url' }} />,
73
68
  );
74
69
  const link = screen.getByRole('link', { name: 'Primary Action' });
75
70
  expect(link).toHaveAttribute('href', '/test-url');
@@ -1,4 +1,4 @@
1
- import { ReactNode } from 'react';
1
+ import { AriaAttributes, ReactNode, useId } from 'react';
2
2
  import { clsx } from 'clsx';
3
3
 
4
4
  import StatusIcon from '../../statusIcon';
@@ -23,6 +23,7 @@ export type ActionPromptProps = {
23
23
  badge?: Pick<BadgeAssetsProps, 'flagCode'>;
24
24
  };
25
25
  'aria-label'?: string;
26
+ 'aria-hidden'?: boolean;
26
27
  };
27
28
  action: Pick<ButtonProps, 'onClick' | 'href' | 'target'> & {
28
29
  label: ButtonProps['children'];
@@ -30,6 +31,7 @@ export type ActionPromptProps = {
30
31
  actionSecondary?: Pick<ButtonProps, 'onClick' | 'href' | 'target'> & {
31
32
  label: ButtonProps['children'];
32
33
  };
34
+ 'aria-label'?: AriaAttributes['aria-label'];
33
35
  } & Pick<PrimitivePromptProps, 'id' | 'className' | 'data-testid' | 'sentiment' | 'onDismiss'>;
34
36
 
35
37
  export const ActionPrompt = ({
@@ -37,19 +39,32 @@ export const ActionPrompt = ({
37
39
  title,
38
40
  description,
39
41
  onDismiss,
40
- media,
42
+ media = {},
41
43
  action,
42
44
  actionSecondary,
43
45
  id,
44
46
  className,
45
47
  'data-testid': testId,
48
+ 'aria-label': ariaLabel,
46
49
  }: ActionPromptProps) => {
47
50
  const isMobile = !useScreenSize(Breakpoint.MEDIUM);
48
51
 
52
+ const mediaId = useId();
53
+ const titleId = useId();
54
+ const descId = useId();
55
+
56
+ const ariaLabelledByIds = [
57
+ media['aria-hidden'] ? undefined : mediaId,
58
+ Boolean(ariaLabel) ? undefined : titleId,
59
+ ]
60
+ .filter(Boolean)
61
+ .join(' ');
62
+
49
63
  const renderMedia = () => {
50
64
  if (media?.imgSrc) {
51
65
  return (
52
66
  <Image
67
+ id={mediaId}
53
68
  src={media.imgSrc}
54
69
  className="wds-action-prompt--media-image"
55
70
  alt={media['aria-label'] ?? ''}
@@ -63,17 +78,34 @@ export const ActionPrompt = ({
63
78
  ? {}
64
79
  : { status: sentiment };
65
80
  return (
66
- <AvatarView {...media.avatar} badge={badge} size={48}>
81
+ <AvatarView
82
+ {...media.avatar}
83
+ badge={badge}
84
+ aria-label={media['aria-label']}
85
+ aria-hidden={media['aria-hidden']}
86
+ id={mediaId}
87
+ size={48}
88
+ >
67
89
  {media.avatar.asset}
68
90
  </AvatarView>
69
91
  );
70
92
  }
71
93
  return sentiment === 'proposition' ? (
72
- <AvatarView size={48}>
94
+ <AvatarView
95
+ id={mediaId}
96
+ size={48}
97
+ aria-label={media['aria-label']}
98
+ aria-hidden={media['aria-hidden']}
99
+ >
73
100
  <GiftBox />
74
101
  </AvatarView>
75
102
  ) : (
76
- <StatusIcon size={48} sentiment={sentiment} />
103
+ <StatusIcon
104
+ id={mediaId}
105
+ size={48}
106
+ sentiment={sentiment}
107
+ iconLabel={Boolean(media['aria-hidden']) ? null : media['aria-label']}
108
+ />
77
109
  );
78
110
  };
79
111
 
@@ -117,10 +149,19 @@ export const ActionPrompt = ({
117
149
  </>
118
150
  }
119
151
  onDismiss={onDismiss}
152
+ role="region"
153
+ {...(Boolean(ariaLabel)
154
+ ? { 'aria-label': ariaLabel }
155
+ : {
156
+ 'aria-labelledby': ariaLabelledByIds,
157
+ 'aria-describedby': descId,
158
+ })}
120
159
  >
121
160
  <div className={clsx('d-flex', 'flex-column', 'justify-content-center')}>
122
- <Body type={Typography.BODY_LARGE_BOLD}>{title}</Body>
123
- {description && <Body>{description}</Body>}
161
+ <Body id={titleId} type={Typography.BODY_LARGE_BOLD}>
162
+ {title}
163
+ </Body>
164
+ {description && <Body id={descId}>{description}</Body>}
124
165
  </div>
125
166
  </PrimitivePrompt>
126
167
  );
@@ -0,0 +1,31 @@
1
+ .wds-info-prompt {
2
+ --Prompt-gap: var(--size-8);
3
+ --Prompt-padding: 12px;
4
+ }
5
+ .wds-info-prompt__content {
6
+ display: flex;
7
+ flex-direction: column;
8
+ justify-content: center;
9
+ }
10
+ .wds-info-prompt__content:has(.wds-info-prompt__title) {
11
+ justify-content: flex-start;
12
+ /* Top align when title exists */
13
+ }
14
+ .wds-info-prompt__title,
15
+ .wds-info-prompt__description {
16
+ display: block;
17
+ color: var(--color-sentiment-primary);
18
+ }
19
+ .wds-info-prompt__action {
20
+ margin-top: var(--Prompt-gap);
21
+ }
22
+ .wds-info-prompt__media {
23
+ display: flex;
24
+ }
25
+ .wds-info-prompt__media svg {
26
+ width: 24px;
27
+ height: 24px;
28
+ }
29
+ .wds-info-prompt .wds-prompt__media-wrapper {
30
+ padding: 0;
31
+ }
@@ -0,0 +1,37 @@
1
+ .wds-info-prompt {
2
+ --Prompt-gap: var(--size-8);
3
+ --Prompt-padding: 12px;
4
+
5
+ &__content {
6
+ display: flex;
7
+ flex-direction: column;
8
+ justify-content: center;
9
+
10
+ &:has(.wds-info-prompt__title) {
11
+ justify-content: flex-start; /* Top align when title exists */
12
+ }
13
+ }
14
+
15
+ &__title,
16
+ &__description {
17
+ display: block;
18
+ color: var(--color-sentiment-primary);
19
+ }
20
+
21
+ &__action {
22
+ margin-top: var(--Prompt-gap);
23
+ }
24
+
25
+ &__media {
26
+ display: flex;
27
+
28
+ svg {
29
+ width: 24px;
30
+ height: 24px;
31
+ }
32
+ }
33
+
34
+ .wds-prompt__media-wrapper {
35
+ padding: 0;
36
+ }
37
+ }