@transferwise/components 0.0.0-experimental-276a15d → 0.0.0-experimental-dd4f4a5

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 (34) hide show
  1. package/build/main.css +1 -3
  2. package/build/prompt/ActionPrompt/ActionPrompt.js +4 -4
  3. package/build/prompt/ActionPrompt/ActionPrompt.js.map +1 -1
  4. package/build/prompt/ActionPrompt/ActionPrompt.mjs +4 -4
  5. package/build/prompt/ActionPrompt/ActionPrompt.mjs.map +1 -1
  6. package/build/prompt/InfoPrompt/InfoPrompt.js.map +1 -1
  7. package/build/prompt/InfoPrompt/InfoPrompt.mjs.map +1 -1
  8. package/build/styles/main.css +1 -3
  9. package/build/styles/prompt/InfoPrompt/InfoPrompt.css +1 -3
  10. package/build/types/prompt/ActionPrompt/ActionPrompt.d.ts +0 -7
  11. package/build/types/prompt/ActionPrompt/ActionPrompt.d.ts.map +1 -1
  12. package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts +2 -4
  13. package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts.map +1 -1
  14. package/package.json +1 -1
  15. package/src/iconButton/IconButton.story.tsx +48 -173
  16. package/src/listItem/_stories/ListItem.story.tsx +1 -0
  17. package/src/main.css +1 -3
  18. package/src/prompt/ActionPrompt/ActionPrompt.accessibility.docs.mdx +17 -1
  19. package/src/prompt/ActionPrompt/ActionPrompt.story.tsx +107 -299
  20. package/src/prompt/ActionPrompt/ActionPrompt.test.story.tsx +3 -86
  21. package/src/prompt/ActionPrompt/ActionPrompt.tsx +4 -11
  22. package/src/prompt/InfoPrompt/InfoPrompt.css +1 -3
  23. package/src/prompt/InfoPrompt/InfoPrompt.less +4 -4
  24. package/src/prompt/InfoPrompt/InfoPrompt.story.tsx +86 -93
  25. package/src/prompt/InfoPrompt/InfoPrompt.test.story.tsx +1 -54
  26. package/src/prompt/InfoPrompt/InfoPrompt.tsx +2 -4
  27. package/src/prompt/InlinePrompt/InlinePrompt.story.tsx +29 -24
  28. package/src/sentimentSurface/SentimentSurface.docs.mdx +1 -1
  29. package/src/sentimentSurface/SentimentSurface.story.tsx +1 -1
  30. package/src/sentimentSurface/SentimentSurface.test.story.tsx +1 -1
  31. package/src/iconButton/IconButton.test.story.tsx +0 -194
  32. package/src/prompt/InfoPrompt/InfoPrompt.accessibility.docs.mdx +0 -79
  33. package/src/prompt/InlinePrompt/InlinePrompt.accessibility.docs.mdx +0 -63
  34. package/src/prompt/InlinePrompt/InlinePrompt.test.story.tsx +0 -21
@@ -1,6 +1,6 @@
1
1
  .wds-info-prompt {
2
2
  --Prompt-gap: var(--size-8);
3
- --Prompt-padding: var(--size-12);
3
+ --Prompt-padding: 12px;
4
4
  }
5
5
  .wds-info-prompt__content {
6
6
  display: flex;
@@ -24,9 +24,7 @@
24
24
  }
25
25
  .wds-info-prompt__media svg {
26
26
  width: 24px;
27
- width: var(--size-24);
28
27
  height: 24px;
29
- height: var(--size-24);
30
28
  }
31
29
  .wds-info-prompt .wds-prompt__media-wrapper {
32
30
  padding: 0;
@@ -1,6 +1,6 @@
1
1
  .wds-info-prompt {
2
2
  --Prompt-gap: var(--size-8);
3
- --Prompt-padding: var(--size-12);
3
+ --Prompt-padding: 12px;
4
4
 
5
5
  &__content {
6
6
  display: flex;
@@ -26,12 +26,12 @@
26
26
  display: flex;
27
27
 
28
28
  svg {
29
- width: var(--size-24);
30
- height: var(--size-24);
29
+ width: 24px;
30
+ height: 24px;
31
31
  }
32
32
  }
33
33
 
34
34
  .wds-prompt__media-wrapper {
35
35
  padding: 0;
36
36
  }
37
- }
37
+ }
@@ -1,11 +1,8 @@
1
1
  import type { ReactElement } from 'react';
2
- import { useState } from 'react';
3
2
  import type { Meta, StoryObj } from '@storybook/react-webpack5';
4
3
  import { action } from 'storybook/actions';
5
- import { Confetti, GiftBox, Star, Travel, Briefcase, Plane } from '@transferwise/icons';
6
- import { InfoPrompt, InfoPromptProps, InfoPromptMedia } from './InfoPrompt';
7
- import Button from '../../button';
8
- import Title from '../../title';
4
+ import { Confetti, GiftBox, Star, Travel } from '@transferwise/icons';
5
+ import { InfoPrompt, InfoPromptProps } from './InfoPrompt';
9
6
 
10
7
  const withComponentGrid =
11
8
  ({ maxWidth = 'auto', gap = '1rem' } = {}) =>
@@ -51,19 +48,6 @@ export default {
51
48
  type: { summary: 'ReactNode' },
52
49
  },
53
50
  },
54
- media: {
55
- table: {
56
- type: { summary: 'InfoPromptMedia', detail: '{ asset: ReactNode }' },
57
- },
58
- },
59
- action: {
60
- table: {
61
- type: {
62
- summary: 'InfoPromptAction',
63
- detail: '{ label: string; href?: string; target?: string; onClick?: () => void }',
64
- },
65
- },
66
- },
67
51
  },
68
52
  parameters: {
69
53
  docs: {
@@ -77,7 +61,7 @@ export default {
77
61
  * not otherwise possible via Storybook
78
62
  */
79
63
  type PreviewStoryArgs = InfoPromptProps & {
80
- previewMedia: InfoPromptMedia | undefined;
64
+ previewMedia: boolean;
81
65
  previewOnDismiss: boolean;
82
66
  previewAction: boolean;
83
67
  };
@@ -89,22 +73,10 @@ const previewArgGroup = {
89
73
  },
90
74
  };
91
75
 
92
- const MEDIA_OPTIONS: Record<string, { asset: ReactElement } | undefined> = {
93
- undefined,
94
- star: { asset: <Star title="Starred" /> },
95
- confetti: { asset: <Confetti size={24} title="Celebration" /> },
96
- giftbox: { asset: <GiftBox title="Gift" /> },
97
- travel: { asset: <Travel title="Travel" /> },
98
- briefcase: { asset: <Briefcase title="Business" /> },
99
- plane: { asset: <Plane title="Travel" /> },
100
- };
101
-
102
76
  const previewArgTypes = {
103
77
  previewMedia: {
104
78
  name: 'Preview with `media`',
105
- control: 'select',
106
- options: ['undefined', 'star', 'confetti', 'giftbox', 'travel', 'briefcase', 'plane'],
107
- mapping: MEDIA_OPTIONS,
79
+ control: 'boolean',
108
80
  table: previewArgGroup,
109
81
  },
110
82
  previewOnDismiss: {
@@ -127,7 +99,7 @@ const getPropsForPreview = (
127
99
  return [
128
100
  props,
129
101
  {
130
- media: previewMedia,
102
+ media: previewMedia ? { asset: <Star title="Starred" /> } : undefined,
131
103
  onDismiss: previewOnDismiss ? action('onDismiss') : undefined,
132
104
  action: previewAction
133
105
  ? { label: 'Learn more', onClick: action('action.onClick') }
@@ -137,19 +109,12 @@ const getPropsForPreview = (
137
109
  };
138
110
 
139
111
  export const Playground: StoryObj<PreviewStoryArgs> = {
140
- argTypes: {
141
- sentiment: {
142
- control: 'radio',
143
- options: ['success', 'negative', 'neutral', 'warning', 'proposition'],
144
- },
145
- ...previewArgTypes,
146
- },
112
+ tags: ['!autodocs'],
113
+ argTypes: previewArgTypes,
147
114
  args: {
148
- title: 'Payment successful',
149
- description: 'Your payment is being processed.',
150
- previewMedia: { asset: <Star title="Starred" /> },
151
- previewOnDismiss: true,
152
- previewAction: true,
115
+ previewMedia: false,
116
+ previewOnDismiss: false,
117
+ previewAction: false,
153
118
  },
154
119
  render: (args: PreviewStoryArgs) => {
155
120
  const [props, previewProps] = getPropsForPreview(args);
@@ -175,6 +140,7 @@ export const Sentiments: StoryObj<InfoPromptProps> = {
175
140
  <InfoPrompt {...args} sentiment="proposition" description="Check out our new features!" />
176
141
  </>
177
142
  ),
143
+ decorators: [withComponentGrid()],
178
144
  };
179
145
 
180
146
  /**
@@ -203,6 +169,32 @@ export const WithTitle: StoryObj<InfoPromptProps> = {
203
169
  />
204
170
  </>
205
171
  ),
172
+ decorators: [withComponentGrid()],
173
+ };
174
+
175
+ /**
176
+ * When `onDismiss` is provided, a close button appears allowing users to dismiss the prompt.
177
+ * Note: The component itself is not automatically removed - you must handle state management.
178
+ */
179
+ export const Dismissible: StoryObj<InfoPromptProps> = {
180
+ render: (args: InfoPromptProps) => (
181
+ <>
182
+ <InfoPrompt
183
+ {...args}
184
+ sentiment="success"
185
+ title="Success"
186
+ description="Your payment was successful."
187
+ onDismiss={action('onDismiss')}
188
+ />
189
+ <InfoPrompt
190
+ {...args}
191
+ sentiment="neutral"
192
+ description="This message can be dismissed."
193
+ onDismiss={action('onDismiss')}
194
+ />
195
+ </>
196
+ ),
197
+ decorators: [withComponentGrid()],
206
198
  };
207
199
 
208
200
  /**
@@ -234,86 +226,87 @@ export const WithAction: StoryObj<InfoPromptProps> = {
234
226
  />
235
227
  </>
236
228
  ),
229
+ decorators: [withComponentGrid()],
237
230
  };
238
231
 
239
232
  /**
240
- * When `onDismiss` is provided, a close button appears allowing users to dismiss the prompt.
241
- * Note: The component itself is not automatically removed - you must handle state management.
242
- */
243
- export const Dismissible: StoryObj<InfoPromptProps> = {
244
- render: function Render() {
245
- const [showPrompt, setShowPrompt] = useState(true);
246
-
247
- return (
248
- <>
249
- {showPrompt ? (
250
- <InfoPrompt
251
- sentiment="success"
252
- title="Payment successful"
253
- description="Your payment was successful."
254
- onDismiss={() => setShowPrompt(false)}
255
- />
256
- ) : (
257
- <Button v2 onClick={() => setShowPrompt(true)}>
258
- Show prompt again
259
- </Button>
260
- )}
261
- </>
262
- );
263
- },
264
- };
265
-
266
- /**
267
- * InfoPrompt supports various media types to enhance visual communication.
233
+ * Use the `media` prop to override the default status icon with a custom icon.
234
+ * This is useful for matching the prompt's visual language to its content.
268
235
  *
269
- * **Default**: Each sentiment has a default status icon that appears when no media is provided.
270
- *
271
- * **Icon Overrides**: Replace default icons with custom icons to match the prompt's visual language to its content.
272
236
  * Success and proposition sentiments support both standard checkmark and confetti variations.
273
237
  */
274
- export const MediaTypes: StoryObj<InfoPromptProps> = {
275
- render: (args) => (
238
+ export const CustomMedia: StoryObj<InfoPromptProps> = {
239
+ render: (args: InfoPromptProps) => (
276
240
  <>
277
- <Title type="title-body">Default</Title>
278
- <InfoPrompt
279
- sentiment="success"
280
- description="When no media is provided, the sentiment's default status icon is displayed."
281
- />
282
-
283
- <Title type="title-body">Icon Overrides</Title>
284
241
  <InfoPrompt
242
+ {...args}
285
243
  sentiment="success"
286
244
  description="Saved to favorites!"
287
245
  media={{ asset: <Star title="Starred" /> }}
288
246
  />
289
247
  <InfoPrompt
248
+ {...args}
290
249
  sentiment="success"
291
250
  title="Congratulations!"
292
251
  description="You've completed all the steps."
293
252
  media={{ asset: <Confetti size={24} title="Celebration" /> }}
294
253
  />
295
254
  <InfoPrompt
255
+ {...args}
296
256
  sentiment="proposition"
297
- title="Special offer"
257
+ title="Check out more!"
298
258
  description="New ways to send money with Wise!"
299
259
  media={{ asset: <GiftBox title="Gift" /> }}
300
260
  />
301
261
  <InfoPrompt
262
+ {...args}
302
263
  sentiment="success"
303
264
  description="Your travel account is ready!"
304
265
  media={{ asset: <Travel title="Travel" /> }}
305
266
  />
267
+ </>
268
+ ),
269
+ decorators: [withComponentGrid()],
270
+ };
271
+
272
+ /**
273
+ * InfoPrompt fully supports accessibility attributes through spread props.
274
+ * Use `role="status"` for non-critical updates or `role="alert"` for important messages.
275
+ */
276
+ export const Accessibility: StoryObj<InfoPromptProps> = {
277
+ render: (args: InfoPromptProps) => (
278
+ <>
306
279
  <InfoPrompt
307
- sentiment="warning"
308
- title="Business travel"
309
- description="Book your business trip with Wise."
310
- media={{ asset: <Briefcase title="Business" /> }}
280
+ {...args}
281
+ sentiment="success"
282
+ description="Your profile has been saved"
283
+ role="status"
311
284
  />
312
285
  <InfoPrompt
313
- sentiment="neutral"
314
- description="Your flight is scheduled."
315
- media={{ asset: <Plane title="Flight" /> }}
286
+ {...args}
287
+ sentiment="negative"
288
+ title="Error"
289
+ description="Your session has expired. Please log in again."
290
+ role="alert"
316
291
  />
317
292
  </>
318
293
  ),
294
+ decorators: [withComponentGrid()],
295
+ };
296
+
297
+ /**
298
+ * Complete example showing all features combined:
299
+ * sentiment, title, description, action, onDismiss, and custom media.
300
+ */
301
+ export const CompleteExample: StoryObj<InfoPromptProps> = {
302
+ render: () => (
303
+ <InfoPrompt
304
+ sentiment="proposition"
305
+ title="Invite Friends & Earn"
306
+ description="Share Wise with friends and get £50 for each friend who makes their first transfer."
307
+ media={{ asset: <GiftBox title="Gift" /> }}
308
+ action={{ label: 'Invite friends', onClick: action('invite') }}
309
+ onDismiss={action('onDismiss')}
310
+ />
311
+ ),
319
312
  };
@@ -1,19 +1,7 @@
1
- import { useState, MouseEvent, ReactElement } from 'react';
1
+ import { useState, MouseEvent } from 'react';
2
2
  import { userEvent, within, expect, waitFor } from 'storybook/test';
3
3
  import { Meta, StoryObj } from '@storybook/react-webpack5';
4
- import { action } from 'storybook/actions';
5
- import { Star } from '@transferwise/icons';
6
4
  import { InfoPrompt, type InfoPromptProps } from './InfoPrompt';
7
- import { allModes } from '../../../.storybook/modes';
8
- import { withVariantConfig } from '../../../.storybook/helpers';
9
-
10
- const withComponentGrid =
11
- ({ gap = '1rem' } = {}) =>
12
- (Story: () => ReactElement) => (
13
- <div style={{ display: 'flex', flexDirection: 'column', gap }}>
14
- <Story />
15
- </div>
16
- );
17
5
 
18
6
  const meta = {
19
7
  title: 'Prompts/InfoPrompt/Tests',
@@ -256,44 +244,3 @@ export const TouchInteraction: Story = {
256
244
  );
257
245
  },
258
246
  };
259
-
260
- export const AllThemesAndSentiments: Story = {
261
- render: () => (
262
- <>
263
- {(['success', 'warning', 'negative', 'neutral', 'proposition'] as const).map((sentiment) => (
264
- <InfoPrompt
265
- key={sentiment}
266
- sentiment={sentiment}
267
- title={`${sentiment.charAt(0).toUpperCase() + sentiment.slice(1)} prompt`}
268
- description="This demonstrates the prompt appearance in different themes."
269
- media={sentiment === 'success' ? { asset: <Star title="Star" /> } : undefined}
270
- action={{ label: 'Action', onClick: action('action') }}
271
- onDismiss={action('dismiss')}
272
- />
273
- ))}
274
- </>
275
- ),
276
- decorators: [withComponentGrid({ gap: '1.5rem' })],
277
- parameters: {
278
- padding: '16px',
279
- variants: ['default', 'dark', 'bright-green', 'forest-green'],
280
- chromatic: {
281
- dark: allModes.dark,
282
- brightGreen: allModes.brightGreen,
283
- forestGreen: allModes.forestGreen,
284
- },
285
- },
286
- };
287
-
288
- export const TinyScreen: Story = {
289
- render: () => (
290
- <InfoPrompt
291
- sentiment="success"
292
- title="Payment received"
293
- description="Your transfer of 500 GBP has been successfully received and is now available in your account."
294
- action={{ label: 'View details', onClick: action('view-details') }}
295
- onDismiss={action('dismiss')}
296
- />
297
- ),
298
- ...withVariantConfig(['400%']),
299
- };
@@ -18,7 +18,8 @@ export type InfoPromptAction = Pick<LinkProps, 'href' | 'target' | 'onClick'> &
18
18
  export type InfoPromptMedia = {
19
19
  /**
20
20
  * The icon/image asset to display.
21
- * The asset should include its own accessibility attributes (e.g. title, aria-label) if it conveys meaning.
21
+ * The asset should include its own accessibility attributes (e.g. title, aria-label)
22
+ * if it conveys meaning, or aria-hidden="true" if decorative.
22
23
  */
23
24
  asset: ReactNode;
24
25
  };
@@ -52,7 +53,6 @@ export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> &
52
53
  * Description text for the prompt (required)
53
54
  */
54
55
  description: string;
55
- className?: string;
56
56
  };
57
57
 
58
58
  /**
@@ -61,8 +61,6 @@ export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> &
61
61
  * or positive feedback with optional actions and dismissal capabilities.
62
62
  *
63
63
  * Use this component to replace the deprecated Alert component.
64
- *
65
- * Guidance can be found in the [design system](https://wise.design/components/info-prompt).
66
64
  */
67
65
  export const InfoPrompt = ({
68
66
  sentiment = 'neutral',
@@ -144,6 +144,7 @@ export const Sentiments: StoryObj<PreviewStoryArgs> = {
144
144
  </>
145
145
  );
146
146
  },
147
+ decorators: [withComponentGrid()],
147
148
  };
148
149
 
149
150
  /**
@@ -163,30 +164,6 @@ export const Loading: StoryObj<PreviewStoryArgs> = {
163
164
  },
164
165
  };
165
166
 
166
- /**
167
- * Inline prompt is usually associated with a different component, such as `Input` or `ListItem`.
168
- * When those components are disabled, the prompt is often used to communicate to the user why they
169
- * cannot interact with them. For that reason, the prompt cannot inherit the usual disabled visual
170
- * style, and it must retain its interactivity. To bring these 2 components visually closer to each
171
- * other, `muted` prop introduces slightly reduced opacity and luminosity as well as a special
172
- * backslash circle icon.
173
- */
174
- export const Muted: StoryObj<PreviewStoryArgs> = {
175
- argTypes: previewArgTypes,
176
- args: {
177
- muted: true,
178
- previewMedia: false,
179
- },
180
- render: (args: PreviewStoryArgs) => {
181
- const [props, previewProps] = getPropsForPreview(args);
182
- return (
183
- <InlinePrompt {...props} {...previewProps}>
184
- Please <Link href="#">confirm your residential address</Link> to activate this feature.
185
- </InlinePrompt>
186
- );
187
- },
188
- };
189
-
190
167
  /**
191
168
  * `InlinePrompt` can include a single instance of the `Link` component, which can be rendered as
192
169
  * either HTML anchor or button. That element will spread across the whole surface of the Prompt
@@ -217,6 +194,31 @@ export const Interactivity: StoryObj<PreviewStoryArgs> = {
217
194
  </InlinePrompt>
218
195
  </>
219
196
  ),
197
+ decorators: [withComponentGrid()],
198
+ };
199
+
200
+ /**
201
+ * Inline prompt is usually associated with a different component, such as `Input` or `ListItem`.
202
+ * When those components are disabled, the prompt is often used to communicate to the user why they
203
+ * cannot interact with them. For that reason, the prompt cannot inherit the usual disabled visual
204
+ * style, and it must retain its interactivity. To bring these 2 components visually closer to each
205
+ * other, `muted` prop introduces slightly reduced opacity and luminosity as well as a special
206
+ * backslash circle icon.
207
+ */
208
+ export const Muted: StoryObj<PreviewStoryArgs> = {
209
+ argTypes: previewArgTypes,
210
+ args: {
211
+ muted: true,
212
+ previewMedia: false,
213
+ },
214
+ render: (args: PreviewStoryArgs) => {
215
+ const [props, previewProps] = getPropsForPreview(args);
216
+ return (
217
+ <InlinePrompt {...props} {...previewProps}>
218
+ Please <Link href="#">confirm your residential address</Link> to activate this feature.
219
+ </InlinePrompt>
220
+ );
221
+ },
220
222
  };
221
223
 
222
224
  /**
@@ -248,6 +250,7 @@ export const IconOverrides: StoryObj<PreviewStoryArgs> = {
248
250
  </>
249
251
  );
250
252
  },
253
+ decorators: [withComponentGrid()],
251
254
  };
252
255
 
253
256
  /**
@@ -278,6 +281,7 @@ export const SizingStrategies: StoryObj<PreviewStoryArgs> = {
278
281
  </>
279
282
  );
280
283
  },
284
+ decorators: [withComponentGrid()],
281
285
  };
282
286
 
283
287
  /**
@@ -305,4 +309,5 @@ export const SentimentAwareness: StoryObj<PreviewStoryArgs> = {
305
309
  </>
306
310
  );
307
311
  },
312
+ decorators: [withComponentGrid()],
308
313
  };
@@ -1,7 +1,7 @@
1
1
  import { Meta, Source, Canvas } from '@storybook/addon-docs/blocks';
2
2
  import * as stories from './SentimentSurface.story';
3
3
 
4
- <Meta title="Content/SentimentSurface/Developer Guide" />
4
+ <Meta title="Foundations/SentimentSurface/Developer Guide" />
5
5
 
6
6
  # Developer Guide
7
7
 
@@ -31,7 +31,7 @@ const withComponentGrid = (Story: () => JSX.Element) => (
31
31
  */
32
32
  const meta: Meta<typeof SentimentSurface> = {
33
33
  component: SentimentSurface,
34
- title: 'Content/SentimentSurface',
34
+ title: 'Foundations/SentimentSurface',
35
35
  argTypes: {
36
36
  sentiment: {
37
37
  control: 'select',
@@ -21,7 +21,7 @@ const screenModes: Theming['screenMode'][] = ['light', 'dark'];
21
21
 
22
22
  export default {
23
23
  component: SentimentSurface,
24
- title: 'Content/SentimentSurface/Tests',
24
+ title: 'Foundations/SentimentSurface/Tests',
25
25
  tags: ['!autodocs', '!manifest'],
26
26
  } satisfies Meta<typeof SentimentSurface>;
27
27