@transferwise/components 0.0.0-experimental-63c3fc6 → 0.0.0-experimental-5f03867

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 (147) hide show
  1. package/build/Snackbar/Snackbar.js +178 -102
  2. package/build/Snackbar/Snackbar.js.map +1 -1
  3. package/build/Snackbar/Snackbar.messages.js +14 -0
  4. package/build/Snackbar/Snackbar.messages.js.map +1 -0
  5. package/build/Snackbar/Snackbar.messages.mjs +10 -0
  6. package/build/Snackbar/Snackbar.messages.mjs.map +1 -0
  7. package/build/Snackbar/Snackbar.mjs +180 -101
  8. package/build/Snackbar/Snackbar.mjs.map +1 -1
  9. package/build/Snackbar/SnackbarContext.js.map +1 -1
  10. package/build/Snackbar/SnackbarContext.mjs.map +1 -1
  11. package/build/Snackbar/SnackbarPortal.js +12 -0
  12. package/build/Snackbar/SnackbarPortal.js.map +1 -0
  13. package/build/Snackbar/SnackbarPortal.mjs +8 -0
  14. package/build/Snackbar/SnackbarPortal.mjs.map +1 -0
  15. package/build/Snackbar/SnackbarProvider.js +24 -28
  16. package/build/Snackbar/SnackbarProvider.js.map +1 -1
  17. package/build/Snackbar/SnackbarProvider.mjs +25 -29
  18. package/build/Snackbar/SnackbarProvider.mjs.map +1 -1
  19. package/build/Snackbar/constants.js +17 -0
  20. package/build/Snackbar/constants.js.map +1 -0
  21. package/build/Snackbar/constants.mjs +11 -0
  22. package/build/Snackbar/constants.mjs.map +1 -0
  23. package/build/Snackbar/useSnackbarAutoDismiss.js +66 -0
  24. package/build/Snackbar/useSnackbarAutoDismiss.js.map +1 -0
  25. package/build/Snackbar/useSnackbarAutoDismiss.mjs +62 -0
  26. package/build/Snackbar/useSnackbarAutoDismiss.mjs.map +1 -0
  27. package/build/Snackbar/useSnackbarManager.js +153 -0
  28. package/build/Snackbar/useSnackbarManager.js.map +1 -0
  29. package/build/Snackbar/useSnackbarManager.mjs +149 -0
  30. package/build/Snackbar/useSnackbarManager.mjs.map +1 -0
  31. package/build/Snackbar/useSnackbarSwipeDismiss.js +135 -0
  32. package/build/Snackbar/useSnackbarSwipeDismiss.js.map +1 -0
  33. package/build/Snackbar/useSnackbarSwipeDismiss.mjs +131 -0
  34. package/build/Snackbar/useSnackbarSwipeDismiss.mjs.map +1 -0
  35. package/build/i18n/en.json +1 -0
  36. package/build/i18n/en.json.js +1 -0
  37. package/build/i18n/en.json.js.map +1 -1
  38. package/build/i18n/en.json.mjs +1 -0
  39. package/build/i18n/en.json.mjs.map +1 -1
  40. package/build/index.js +0 -4
  41. package/build/index.js.map +1 -1
  42. package/build/index.mjs +0 -2
  43. package/build/index.mjs.map +1 -1
  44. package/build/main.css +116 -191
  45. package/build/styles/Snackbar/Snackbar.css +117 -54
  46. package/build/styles/main.css +116 -191
  47. package/build/types/Snackbar/Snackbar.d.ts +55 -9
  48. package/build/types/Snackbar/Snackbar.d.ts.map +1 -1
  49. package/build/types/Snackbar/Snackbar.messages.d.ts +9 -0
  50. package/build/types/Snackbar/Snackbar.messages.d.ts.map +1 -0
  51. package/build/types/Snackbar/Snackbar.types.d.ts +12 -0
  52. package/build/types/Snackbar/Snackbar.types.d.ts.map +1 -0
  53. package/build/types/Snackbar/SnackbarContext.d.ts +2 -2
  54. package/build/types/Snackbar/SnackbarContext.d.ts.map +1 -1
  55. package/build/types/Snackbar/SnackbarPortal.d.ts +4 -0
  56. package/build/types/Snackbar/SnackbarPortal.d.ts.map +1 -0
  57. package/build/types/Snackbar/SnackbarProvider.d.ts +7 -0
  58. package/build/types/Snackbar/SnackbarProvider.d.ts.map +1 -1
  59. package/build/types/Snackbar/constants.d.ts +9 -0
  60. package/build/types/Snackbar/constants.d.ts.map +1 -0
  61. package/build/types/Snackbar/index.d.ts +1 -1
  62. package/build/types/Snackbar/index.d.ts.map +1 -1
  63. package/build/types/Snackbar/useSnackbar.d.ts +1 -1
  64. package/build/types/Snackbar/useSnackbar.d.ts.map +1 -1
  65. package/build/types/Snackbar/useSnackbarAutoDismiss.d.ts +13 -0
  66. package/build/types/Snackbar/useSnackbarAutoDismiss.d.ts.map +1 -0
  67. package/build/types/Snackbar/useSnackbarManager.d.ts +20 -0
  68. package/build/types/Snackbar/useSnackbarManager.d.ts.map +1 -0
  69. package/build/types/Snackbar/useSnackbarSwipeDismiss.d.ts +22 -0
  70. package/build/types/Snackbar/useSnackbarSwipeDismiss.d.ts.map +1 -0
  71. package/build/types/index.d.ts +1 -5
  72. package/build/types/index.d.ts.map +1 -1
  73. package/build/types/test-utils/index.d.ts +2 -0
  74. package/build/types/test-utils/index.d.ts.map +1 -1
  75. package/package.json +3 -3
  76. package/src/Snackbar/Snackbar.css +117 -54
  77. package/src/Snackbar/Snackbar.less +139 -60
  78. package/src/Snackbar/Snackbar.messages.ts +9 -0
  79. package/src/Snackbar/Snackbar.test.tsx +989 -119
  80. package/src/Snackbar/Snackbar.tsx +208 -93
  81. package/src/Snackbar/Snackbar.types.ts +17 -0
  82. package/src/Snackbar/SnackbarContext.ts +2 -2
  83. package/src/Snackbar/SnackbarPortal.tsx +8 -0
  84. package/src/Snackbar/SnackbarProvider.test.tsx +783 -0
  85. package/src/Snackbar/SnackbarProvider.tsx +27 -19
  86. package/src/Snackbar/_stories/Snackbar.accessibility.docs.mdx +66 -0
  87. package/src/Snackbar/_stories/Snackbar.migration.docs.mdx +64 -0
  88. package/src/Snackbar/_stories/Snackbar.rtl.test.story.tsx +16 -8
  89. package/src/Snackbar/_stories/Snackbar.story.tsx +389 -61
  90. package/src/Snackbar/_stories/Snackbar.test.story.tsx +171 -47
  91. package/src/Snackbar/constants.ts +10 -0
  92. package/src/Snackbar/index.ts +1 -1
  93. package/src/Snackbar/useSnackbarAutoDismiss.ts +74 -0
  94. package/src/Snackbar/useSnackbarManager.ts +169 -0
  95. package/src/Snackbar/useSnackbarSwipeDismiss.ts +169 -0
  96. package/src/i18n/en.json +1 -0
  97. package/src/index.ts +1 -14
  98. package/src/main.css +116 -191
  99. package/src/main.less +0 -2
  100. package/build/CardContainer/CardContainer.js +0 -33
  101. package/build/CardContainer/CardContainer.js.map +0 -1
  102. package/build/CardContainer/CardContainer.mjs +0 -29
  103. package/build/CardContainer/CardContainer.mjs.map +0 -1
  104. package/build/CardWidget/CardWidget.js +0 -94
  105. package/build/CardWidget/CardWidget.js.map +0 -1
  106. package/build/CardWidget/CardWidget.mjs +0 -90
  107. package/build/CardWidget/CardWidget.mjs.map +0 -1
  108. package/build/CardWidget/CardWidgetAction.js +0 -70
  109. package/build/CardWidget/CardWidgetAction.js.map +0 -1
  110. package/build/CardWidget/CardWidgetAction.mjs +0 -65
  111. package/build/CardWidget/CardWidgetAction.mjs.map +0 -1
  112. package/build/styles/CardContainer/CardContainer.css +0 -74
  113. package/build/styles/CardWidget/CardWidget.css +0 -64
  114. package/build/types/CardContainer/CardContainer.d.ts +0 -8
  115. package/build/types/CardContainer/CardContainer.d.ts.map +0 -1
  116. package/build/types/CardContainer/CardContainer.types.d.ts +0 -44
  117. package/build/types/CardContainer/CardContainer.types.d.ts.map +0 -1
  118. package/build/types/CardContainer/_stories/helpers.d.ts +0 -3
  119. package/build/types/CardContainer/_stories/helpers.d.ts.map +0 -1
  120. package/build/types/CardContainer/index.d.ts +0 -3
  121. package/build/types/CardContainer/index.d.ts.map +0 -1
  122. package/build/types/CardWidget/CardWidget.d.ts +0 -8
  123. package/build/types/CardWidget/CardWidget.d.ts.map +0 -1
  124. package/build/types/CardWidget/CardWidget.types.d.ts +0 -52
  125. package/build/types/CardWidget/CardWidget.types.d.ts.map +0 -1
  126. package/build/types/CardWidget/CardWidgetAction.d.ts +0 -54
  127. package/build/types/CardWidget/CardWidgetAction.d.ts.map +0 -1
  128. package/build/types/CardWidget/index.d.ts +0 -4
  129. package/build/types/CardWidget/index.d.ts.map +0 -1
  130. package/src/CardContainer/CardContainer.css +0 -74
  131. package/src/CardContainer/CardContainer.less +0 -112
  132. package/src/CardContainer/CardContainer.test.tsx +0 -118
  133. package/src/CardContainer/CardContainer.tsx +0 -39
  134. package/src/CardContainer/CardContainer.types.ts +0 -50
  135. package/src/CardContainer/_stories/CardContainer.story.tsx +0 -136
  136. package/src/CardContainer/_stories/CardContainer.test.story.tsx +0 -103
  137. package/src/CardContainer/_stories/helpers.tsx +0 -50
  138. package/src/CardContainer/index.ts +0 -6
  139. package/src/CardWidget/CardWidget.css +0 -64
  140. package/src/CardWidget/CardWidget.less +0 -82
  141. package/src/CardWidget/CardWidget.test.tsx +0 -189
  142. package/src/CardWidget/CardWidget.tsx +0 -74
  143. package/src/CardWidget/CardWidget.types.ts +0 -60
  144. package/src/CardWidget/CardWidgetAction.tsx +0 -126
  145. package/src/CardWidget/_stories/CardWidget.story.tsx +0 -209
  146. package/src/CardWidget/_stories/CardWidget.test.story.tsx +0 -350
  147. package/src/CardWidget/index.ts +0 -3
@@ -1,89 +1,417 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
- import { fn, userEvent, within } from 'storybook/test';
2
+ import { fn } from 'storybook/test';
3
+ import { Card } from '@transferwise/icons';
3
4
 
4
- import { wait } from '../../test-utils/wait';
5
5
  import {
6
6
  createSandboxStory,
7
7
  globalScope,
8
8
  } from '../../../.storybook/components/sandbox/SandboxEditor';
9
9
  import Button from '../../Button';
10
10
  import { Snackbar } from '../Snackbar';
11
- import { SnackbarConsumer } from '../SnackbarContext';
11
+ import type { SnackbarCreationProps } from '../Snackbar.types';
12
12
  import SnackbarProvider from '../SnackbarProvider';
13
+ import useSnackbar from '../useSnackbar';
13
14
 
14
- const meta: Meta = {
15
+ const meta = {
15
16
  component: Snackbar,
16
17
  title: 'Prompts/Snackbar',
17
- };
18
+ tags: ['new'],
19
+ parameters: {
20
+ docs: {
21
+ description: {
22
+ component: `Snackbar provides short feedback after an action, with one optional action of its own.
23
+ Use \`useSnackbar()\` inside \`SnackbarProvider\`. A newer message replaces the current one; Snackbars do not stack.
24
+
25
+ Change the Playground controls, then click **Show Snackbar** to try them. Keep messages short and plain.
26
+
27
+ See [Accessibility](?path=/docs/prompts-snackbar-accessibility--docs) for keyboard and announcement behaviour,
28
+ or [Migration](?path=/docs/prompts-snackbar-migration--docs) when updating existing usage.`,
29
+ },
30
+ },
31
+ },
32
+ argTypes: {
33
+ text: { control: 'text', table: { type: { summary: 'ReactNode' } } },
34
+ sentiment: {
35
+ control: 'select',
36
+ options: ['neutral', 'success', 'warning', 'negative', 'proposition'],
37
+ },
38
+ duration: { control: 'select', options: ['default', 'extended', 'indefinite'] },
39
+ loading: { control: 'boolean' },
40
+ media: { control: false, table: { type: { summary: 'ReactNode' } } },
41
+ action: {
42
+ control: false,
43
+ table: {
44
+ type: {
45
+ summary: 'SnackbarAction',
46
+ detail:
47
+ '{ label: ReactNode; href?: string; target?: string; onClick?: MouseEventHandler }',
48
+ },
49
+ },
50
+ },
51
+ onDismiss: { control: false },
52
+ announcementKey: { table: { disable: true } },
53
+ onDismissRequest: { table: { disable: true } },
54
+ onExited: { table: { disable: true } },
55
+ visible: { table: { disable: true } },
56
+ timeout: { table: { disable: true } },
57
+ timestamp: { table: { disable: true } },
58
+ theme: { table: { disable: true } },
59
+ },
60
+ } satisfies Meta<typeof Snackbar>;
61
+
18
62
  export default meta;
19
63
 
20
- export const Sandbox = createSandboxStory({
21
- code: `<SnackbarProvider>
22
- <SnackbarConsumer>
23
- {({ createSnackbar }) => (
24
- <Button v2 size="md"
25
- onClick={() =>
26
- createSnackbar({
27
- text: <span>Transfer completed successfully</span>,
28
- action: {
29
- label: 'View',
30
- onClick: () => console.log('View clicked'),
31
- },
32
- })
64
+ type PreviewStoryArgs = Pick<
65
+ SnackbarCreationProps,
66
+ 'sentiment' | 'duration' | 'loading' | 'onDismiss'
67
+ > & {
68
+ text: string;
69
+ previewMedia: 'default' | 'card';
70
+ previewActionLabel: string;
71
+ previewActionHref: string;
72
+ };
73
+
74
+ function SnackbarTrigger({
75
+ label = 'Show Snackbar',
76
+ ...props
77
+ }: SnackbarCreationProps & { label?: string }) {
78
+ const createSnackbar = useSnackbar();
79
+
80
+ return (
81
+ <Button v2 size="md" onClick={() => createSnackbar(props)}>
82
+ {label}
83
+ </Button>
84
+ );
85
+ }
86
+
87
+ const onActionClick = fn();
88
+
89
+ type SnackbarExample = SnackbarCreationProps & { label: string };
90
+
91
+ function SnackbarExamples({ examples }: { examples: SnackbarExample[] }) {
92
+ return (
93
+ <SnackbarProvider>
94
+ <div className="d-flex flex-wrap" style={{ gap: 'var(--size-8)' }}>
95
+ {examples.map(({ label, ...props }) => (
96
+ <SnackbarTrigger key={label} label={label} {...props} />
97
+ ))}
98
+ </div>
99
+ </SnackbarProvider>
100
+ );
101
+ }
102
+
103
+ const getExamplesCode = (examples: string, withMedia = false) =>
104
+ `import { Button, SnackbarProvider, useSnackbar } from '@transferwise/components';
105
+ ${withMedia ? "import { Card } from '@transferwise/icons';\n" : ''}
106
+ function App() {
107
+ return (
108
+ <SnackbarProvider>
109
+ <ShowExamples />
110
+ </SnackbarProvider>
111
+ );
112
+ }
113
+
114
+ function ShowExamples() {
115
+ const createSnackbar = useSnackbar();
116
+ const examples = ${examples};
117
+
118
+ return (
119
+ <div className="d-flex flex-wrap" style={{ gap: 'var(--size-8)' }}>
120
+ {examples.map(({ label, ...props }) => (
121
+ <Button key={label} v2 size="md" onClick={() => createSnackbar(props)}>
122
+ {label}
123
+ </Button>
124
+ ))}
125
+ </div>
126
+ );
127
+ }
128
+
129
+ export default App;`;
130
+
131
+ const getExampleCode = ({
132
+ text,
133
+ sentiment,
134
+ duration,
135
+ loading,
136
+ previewMedia,
137
+ previewActionLabel,
138
+ previewActionHref,
139
+ }: PreviewStoryArgs) => `function App() {
140
+ return (
141
+ <SnackbarProvider>
142
+ <ShowSnackbar />
143
+ </SnackbarProvider>
144
+ );
145
+ }
146
+
147
+ function ShowSnackbar() {
148
+ const createSnackbar = useSnackbar();
149
+
150
+ return (
151
+ <Button v2 size="md" onClick={() => createSnackbar({
152
+ text: ${JSON.stringify(text)},
153
+ sentiment: ${JSON.stringify(sentiment)},
154
+ duration: ${JSON.stringify(duration)},${loading ? '\n loading: true,' : ''}${previewMedia === 'card' ? '\n media: <Card aria-hidden />,' : ''}${
155
+ previewActionLabel
156
+ ? `
157
+ action: {
158
+ label: ${JSON.stringify(previewActionLabel)},${previewActionHref ? `\n href: ${JSON.stringify(previewActionHref)},` : ''}
159
+ onClick: () => console.log('Action clicked'),
160
+ },`
161
+ : ''
162
+ }
163
+ onDismiss: () => console.log('Dismissed'),
164
+ })}>
165
+ Show Snackbar
166
+ </Button>
167
+ );
168
+ }`;
169
+
170
+ /** Change the controls, then click Show Snackbar to create a message with those values. */
171
+ export const Playground: StoryObj<PreviewStoryArgs> = {
172
+ args: {
173
+ text: 'Changes saved',
174
+ sentiment: 'success',
175
+ duration: 'default',
176
+ loading: false,
177
+ previewMedia: 'default',
178
+ previewActionLabel: '',
179
+ previewActionHref: '',
180
+ onDismiss: fn(),
181
+ },
182
+ argTypes: {
183
+ previewMedia: {
184
+ name: 'Preview with custom media',
185
+ control: 'select',
186
+ options: ['default', 'card'],
187
+ table: { category: 'Storybook Preview options' },
188
+ },
189
+ previewActionLabel: {
190
+ name: 'Action label (empty for no action)',
191
+ control: 'text',
192
+ table: { category: 'Storybook Preview options' },
193
+ },
194
+ previewActionHref: {
195
+ name: 'Action href (empty for callback only)',
196
+ control: 'text',
197
+ table: { category: 'Storybook Preview options' },
198
+ },
199
+ },
200
+ render: ({ previewMedia, previewActionLabel, previewActionHref, ...props }) => (
201
+ <SnackbarProvider>
202
+ <SnackbarTrigger
203
+ {...props}
204
+ media={previewMedia === 'card' ? <Card aria-hidden /> : undefined}
205
+ action={
206
+ previewActionLabel
207
+ ? {
208
+ label: previewActionLabel,
209
+ href: previewActionHref || undefined,
210
+ onClick: onActionClick,
211
+ }
212
+ : undefined
33
213
  }
34
- >
35
- Show Snackbar
36
- </Button>
37
- )}
38
- </SnackbarConsumer>
39
- </SnackbarProvider>`,
214
+ />
215
+ </SnackbarProvider>
216
+ ),
217
+ parameters: {
218
+ chromatic: { disableSnapshot: true },
219
+ docs: {
220
+ source: {
221
+ type: 'dynamic',
222
+ transform: (source: string, { args }: { args: PreviewStoryArgs }) =>
223
+ `import { Button, SnackbarProvider, useSnackbar } from '@transferwise/components';\n${args.previewMedia === 'card' ? "import { Card } from '@transferwise/icons';\n" : ''}\n${getExampleCode(args)}\n\nexport default App;`,
224
+ },
225
+ },
226
+ },
227
+ };
228
+
229
+ export const Sandbox = createSandboxStory({
230
+ code: getExampleCode({
231
+ text: 'Card added',
232
+ sentiment: 'neutral',
233
+ duration: 'extended',
234
+ previewMedia: 'card',
235
+ previewActionLabel: 'View',
236
+ previewActionHref: '',
237
+ }),
40
238
  scope: globalScope,
41
239
  });
42
240
 
43
- export const Basic: StoryObj = {
241
+ /**
242
+ * Choose a sentiment to match the message. Each has default media; all use polite announcements.
243
+ * Show one at a time using the buttons below.
244
+ */
245
+ export const Sentiments: StoryObj = {
246
+ render: () => (
247
+ <SnackbarExamples
248
+ examples={(['neutral', 'success', 'warning', 'negative', 'proposition'] as const).map(
249
+ (sentiment) => ({ label: sentiment, sentiment, text: 'Your details have been updated' }),
250
+ )}
251
+ />
252
+ ),
44
253
  parameters: {
254
+ controls: { disable: true },
255
+ chromatic: { disableSnapshot: true },
45
256
  docs: {
46
257
  source: {
47
- type: 'code',
48
- transform: async (src = '') => {
49
- const match = /(function Render\(\).*?)(,\s+play:\s+async)/s.exec(src);
50
- return match?.[1] || src;
51
- },
258
+ code: getExamplesCode(`(['neutral', 'success', 'warning', 'negative', 'proposition'] as const).map(
259
+ (sentiment) => ({ label: sentiment, sentiment, text: 'Your details have been updated' }),
260
+ )`),
52
261
  },
53
262
  },
54
- source: {
55
- type: 'code',
263
+ },
264
+ };
265
+
266
+ /**
267
+ * Default media follows the sentiment. Override it with a custom icon, including for neutral and
268
+ * proposition. Wise icons fill the 24×24px slot without an explicit size.
269
+ */
270
+ export const MediaTypes: StoryObj = {
271
+ render: () => (
272
+ <SnackbarExamples
273
+ examples={[
274
+ { label: 'Default media', text: 'Changes saved', sentiment: 'success' },
275
+ { label: 'Custom neutral', text: 'Card added', media: <Card aria-hidden /> },
276
+ {
277
+ label: 'Custom proposition',
278
+ text: 'Your new card is ready',
279
+ sentiment: 'proposition',
280
+ media: <Card aria-hidden />,
281
+ },
282
+ ]}
283
+ />
284
+ ),
285
+ parameters: {
286
+ controls: { disable: true },
287
+ chromatic: { disableSnapshot: true },
288
+ docs: {
289
+ source: {
290
+ code: getExamplesCode(
291
+ `[
292
+ { label: 'Default media', text: 'Changes saved', sentiment: 'success' as const },
293
+ { label: 'Custom neutral', text: 'Card added', media: <Card aria-hidden /> },
294
+ {
295
+ label: 'Custom proposition',
296
+ text: 'Your new card is ready',
297
+ sentiment: 'proposition' as const,
298
+ media: <Card aria-hidden />,
299
+ },
300
+ ]`,
301
+ true,
302
+ ),
303
+ },
56
304
  },
57
305
  },
58
- render: function Render() {
59
- return (
60
- <SnackbarProvider>
61
- <SnackbarConsumer>
62
- {({ createSnackbar }) => {
63
- return (
64
- <Button
65
- block
66
- onClick={() => {
67
- createSnackbar({
68
- action: {
69
- label: 'View',
70
- onClick: fn(),
71
- },
72
- text: <span>Some text</span>,
73
- });
74
- }}
75
- >
76
- Show Snackbar
77
- </Button>
78
- );
79
- }}
80
- </SnackbarConsumer>
81
- </SnackbarProvider>
82
- );
306
+ };
307
+
308
+ /** Loading replaces media, uses neutral styling and prevents auto-dismiss, regardless of duration. */
309
+ export const Loading: StoryObj<PreviewStoryArgs> = {
310
+ ...Playground,
311
+ args: { ...Playground.args, text: 'Uploading document', loading: true },
312
+ };
313
+
314
+ /**
315
+ * Add one action: a callback with `onClick`, navigation with `href`, or both.
316
+ * Activating it requests dismissal after the callback runs.
317
+ */
318
+ export const WithAction: StoryObj = {
319
+ render: () => (
320
+ <SnackbarExamples
321
+ examples={[
322
+ {
323
+ label: 'Callback action',
324
+ text: 'Transfer deleted',
325
+ duration: 'extended',
326
+ action: { label: 'Undo', onClick: onActionClick },
327
+ },
328
+ {
329
+ label: 'Navigation action',
330
+ text: 'Your transfer is ready',
331
+ duration: 'extended',
332
+ action: { label: 'View transfer', href: '/transfers/123', target: '_blank' },
333
+ },
334
+ ]}
335
+ />
336
+ ),
337
+ parameters: {
338
+ controls: { disable: true },
339
+ chromatic: { disableSnapshot: true },
340
+ docs: {
341
+ source: {
342
+ code: getExamplesCode(`[
343
+ {
344
+ label: 'Callback action',
345
+ text: 'Transfer deleted',
346
+ duration: 'extended' as const,
347
+ action: { label: 'Undo', onClick: () => console.log('Undo clicked') },
348
+ },
349
+ {
350
+ label: 'Navigation action',
351
+ text: 'Your transfer is ready',
352
+ duration: 'extended' as const,
353
+ action: { label: 'View transfer', href: '/transfers/123', target: '_blank' },
354
+ },
355
+ ]`),
356
+ },
357
+ },
83
358
  },
84
- play: async ({ canvasElement }) => {
85
- await wait(0);
86
- const canvas = within(canvasElement);
87
- await userEvent.click(canvas.getByRole('button'));
359
+ };
360
+
361
+ /**
362
+ * `default` lasts 4 seconds, `extended` lasts 6, and `indefinite` has no timer.
363
+ * Timers begin after entry and pause during hover, focus or a swipe, then resume with the remaining time.
364
+ */
365
+ export const Durations: StoryObj = {
366
+ render: () => (
367
+ <SnackbarExamples
368
+ examples={[
369
+ { label: 'Default (4s)', text: 'Changes saved', duration: 'default' },
370
+ {
371
+ label: 'Extended (6s)',
372
+ text: 'Transfer deleted',
373
+ duration: 'extended',
374
+ action: { label: 'Undo', onClick: onActionClick },
375
+ },
376
+ {
377
+ label: 'Indefinite',
378
+ text: 'Uploading document',
379
+ duration: 'indefinite',
380
+ loading: true,
381
+ },
382
+ ]}
383
+ />
384
+ ),
385
+ parameters: {
386
+ controls: { disable: true },
387
+ chromatic: { disableSnapshot: true },
388
+ docs: {
389
+ source: {
390
+ code: getExamplesCode(`[
391
+ { label: 'Default (4s)', text: 'Changes saved', duration: 'default' as const },
392
+ {
393
+ label: 'Extended (6s)',
394
+ text: 'Transfer deleted',
395
+ duration: 'extended' as const,
396
+ action: { label: 'Undo', onClick: () => console.log('Undo clicked') },
397
+ },
398
+ {
399
+ label: 'Indefinite',
400
+ text: 'Uploading document',
401
+ duration: 'indefinite' as const,
402
+ loading: true,
403
+ },
404
+ ]`),
405
+ },
406
+ },
88
407
  },
89
408
  };
409
+
410
+ /**
411
+ * Dismiss with Escape, the desktop close button, an action, or a downward swipe/drag of at least half
412
+ * the surface height. `onDismiss` fires after exit; check the Actions panel to see it.
413
+ */
414
+ export const Dismissal: StoryObj<PreviewStoryArgs> = {
415
+ ...Playground,
416
+ args: { ...Playground.args, duration: 'indefinite' },
417
+ };