@transferwise/components 0.0.0-experimental-4b93e2b → 0.0.0-experimental-f144cf1

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 (218) hide show
  1. package/build/Calendar/DateHeader/DateHeader.js +6 -2
  2. package/build/Calendar/DateHeader/DateHeader.js.map +1 -1
  3. package/build/Calendar/DateHeader/DateHeader.mjs +7 -3
  4. package/build/Calendar/DateHeader/DateHeader.mjs.map +1 -1
  5. package/build/Carousel/Carousel.js +2 -2
  6. package/build/Carousel/Carousel.js.map +1 -1
  7. package/build/Carousel/Carousel.mjs +3 -3
  8. package/build/Carousel/Carousel.mjs.map +1 -1
  9. package/build/Inputs/SelectInput/OptionContent/SelectInputOptionContent.js +3 -4
  10. package/build/Inputs/SelectInput/OptionContent/SelectInputOptionContent.js.map +1 -1
  11. package/build/Inputs/SelectInput/OptionContent/SelectInputOptionContent.mjs +3 -4
  12. package/build/Inputs/SelectInput/OptionContent/SelectInputOptionContent.mjs.map +1 -1
  13. package/build/ListItem/Navigation/ListItemNavigation.js +1 -1
  14. package/build/ListItem/Navigation/ListItemNavigation.js.map +1 -1
  15. package/build/ListItem/Navigation/ListItemNavigation.mjs +2 -2
  16. package/build/ListItem/Navigation/ListItemNavigation.mjs.map +1 -1
  17. package/build/PhoneNumberInput/PhoneNumberInput.js +1 -2
  18. package/build/PhoneNumberInput/PhoneNumberInput.js.map +1 -1
  19. package/build/PhoneNumberInput/PhoneNumberInput.mjs +1 -2
  20. package/build/PhoneNumberInput/PhoneNumberInput.mjs.map +1 -1
  21. package/build/PromoCard/Indicator/PromoCardIndicator.js +1 -1
  22. package/build/PromoCard/Indicator/PromoCardIndicator.js.map +1 -1
  23. package/build/PromoCard/Indicator/PromoCardIndicator.mjs +2 -2
  24. package/build/PromoCard/Indicator/PromoCardIndicator.mjs.map +1 -1
  25. package/build/Snackbar/Snackbar.js +212 -108
  26. package/build/Snackbar/Snackbar.js.map +1 -1
  27. package/build/Snackbar/Snackbar.messages.js +14 -0
  28. package/build/Snackbar/Snackbar.messages.js.map +1 -0
  29. package/build/Snackbar/Snackbar.messages.mjs +10 -0
  30. package/build/Snackbar/Snackbar.messages.mjs.map +1 -0
  31. package/build/Snackbar/Snackbar.mjs +213 -107
  32. package/build/Snackbar/Snackbar.mjs.map +1 -1
  33. package/build/Snackbar/SnackbarContext.js.map +1 -1
  34. package/build/Snackbar/SnackbarContext.mjs.map +1 -1
  35. package/build/Snackbar/SnackbarPortal.js +92 -0
  36. package/build/Snackbar/SnackbarPortal.js.map +1 -0
  37. package/build/Snackbar/SnackbarPortal.mjs +89 -0
  38. package/build/Snackbar/SnackbarPortal.mjs.map +1 -0
  39. package/build/Snackbar/SnackbarProvider.js +21 -31
  40. package/build/Snackbar/SnackbarProvider.js.map +1 -1
  41. package/build/Snackbar/SnackbarProvider.mjs +22 -32
  42. package/build/Snackbar/SnackbarProvider.mjs.map +1 -1
  43. package/build/Snackbar/constants.js +14 -0
  44. package/build/Snackbar/constants.js.map +1 -0
  45. package/build/Snackbar/constants.mjs +8 -0
  46. package/build/Snackbar/constants.mjs.map +1 -0
  47. package/build/Snackbar/useSnackbarAutoDismiss.js +135 -0
  48. package/build/Snackbar/useSnackbarAutoDismiss.js.map +1 -0
  49. package/build/Snackbar/useSnackbarAutoDismiss.mjs +131 -0
  50. package/build/Snackbar/useSnackbarAutoDismiss.mjs.map +1 -0
  51. package/build/Snackbar/useSnackbarManager.js +164 -0
  52. package/build/Snackbar/useSnackbarManager.js.map +1 -0
  53. package/build/Snackbar/useSnackbarManager.mjs +160 -0
  54. package/build/Snackbar/useSnackbarManager.mjs.map +1 -0
  55. package/build/Snackbar/useSnackbarSwipeDismiss.js +114 -0
  56. package/build/Snackbar/useSnackbarSwipeDismiss.js.map +1 -0
  57. package/build/Snackbar/useSnackbarSwipeDismiss.mjs +110 -0
  58. package/build/Snackbar/useSnackbarSwipeDismiss.mjs.map +1 -0
  59. package/build/common/PropsValues/typography.js +0 -6
  60. package/build/common/PropsValues/typography.js.map +1 -1
  61. package/build/common/PropsValues/typography.mjs +0 -6
  62. package/build/common/PropsValues/typography.mjs.map +1 -1
  63. package/build/i18n/en.json +1 -0
  64. package/build/i18n/en.json.js +1 -0
  65. package/build/i18n/en.json.js.map +1 -1
  66. package/build/i18n/en.json.mjs +1 -0
  67. package/build/i18n/en.json.mjs.map +1 -1
  68. package/build/index.js +3 -1
  69. package/build/index.js.map +1 -1
  70. package/build/index.mjs +2 -1
  71. package/build/index.mjs.map +1 -1
  72. package/build/main.css +864 -1509
  73. package/build/styles/Button/Button.css +3 -0
  74. package/build/styles/DateInput/DateInput.css +0 -13
  75. package/build/styles/Inputs/Input/Input.css +24 -0
  76. package/build/styles/Inputs/TextArea/TextArea.css +24 -0
  77. package/build/styles/ListItem/ListItem.css +1 -1
  78. package/build/styles/PhoneNumberInput/PhoneNumberInput.css +0 -3
  79. package/build/styles/Popover/Popover.css +6 -0
  80. package/build/styles/PromoCard/PromoCard.css +3 -0
  81. package/build/styles/Snackbar/Snackbar.css +182 -61
  82. package/build/styles/_deprecated/Select/Select.css +6 -0
  83. package/build/styles/css/accordion.css +12 -0
  84. package/build/styles/css/button-groups.css +6 -0
  85. package/build/styles/css/buttons.css +2 -0
  86. package/build/styles/css/decision.css +12 -0
  87. package/build/styles/css/dropdowns.css +18 -2
  88. package/build/styles/css/droppable.css +6 -0
  89. package/build/styles/css/footer.css +12 -0
  90. package/build/styles/css/input-groups.css +42 -2
  91. package/build/styles/css/list-group.css +6 -0
  92. package/build/styles/css/navbar.css +18 -2
  93. package/build/styles/css/neptune-core.css +512 -4
  94. package/build/styles/css/neptune.css +640 -1426
  95. package/build/styles/css/popovers.css +18 -0
  96. package/build/styles/css/tooltip.css +6 -0
  97. package/build/styles/less/neptune-tokens.less +0 -8
  98. package/build/styles/main.css +864 -1509
  99. package/build/styles/props/neptune-tokens.css +0 -8
  100. package/build/styles/styles/less/accordion.css +12 -0
  101. package/build/styles/styles/less/button-groups.css +6 -0
  102. package/build/styles/styles/less/buttons.css +2 -0
  103. package/build/styles/styles/less/decision.css +12 -0
  104. package/build/styles/styles/less/dropdowns.css +18 -2
  105. package/build/styles/styles/less/droppable.css +6 -0
  106. package/build/styles/styles/less/footer.css +12 -0
  107. package/build/styles/styles/less/forms/bootstrap-forms.css +24 -2
  108. package/build/styles/styles/less/input-groups.css +42 -2
  109. package/build/styles/styles/less/list-group.css +6 -0
  110. package/build/styles/styles/less/navbar.css +18 -2
  111. package/build/styles/styles/less/neptune-core.css +512 -4
  112. package/build/styles/styles/less/neptune.css +640 -1426
  113. package/build/styles/styles/less/popovers.css +18 -0
  114. package/build/styles/styles/less/tooltip.css +6 -0
  115. package/build/types/Inputs/SelectInput/OptionContent/SelectInputOptionContent.d.ts +1 -2
  116. package/build/types/Inputs/SelectInput/OptionContent/SelectInputOptionContent.d.ts.map +1 -1
  117. package/build/types/PhoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
  118. package/build/types/Snackbar/Snackbar.d.ts +41 -24
  119. package/build/types/Snackbar/Snackbar.d.ts.map +1 -1
  120. package/build/types/Snackbar/Snackbar.messages.d.ts +9 -0
  121. package/build/types/Snackbar/Snackbar.messages.d.ts.map +1 -0
  122. package/build/types/Snackbar/Snackbar.types.d.ts +21 -0
  123. package/build/types/Snackbar/Snackbar.types.d.ts.map +1 -0
  124. package/build/types/Snackbar/SnackbarContext.d.ts +2 -2
  125. package/build/types/Snackbar/SnackbarContext.d.ts.map +1 -1
  126. package/build/types/Snackbar/SnackbarPortal.d.ts +5 -0
  127. package/build/types/Snackbar/SnackbarPortal.d.ts.map +1 -0
  128. package/build/types/Snackbar/SnackbarProvider.d.ts +6 -0
  129. package/build/types/Snackbar/SnackbarProvider.d.ts.map +1 -1
  130. package/build/types/Snackbar/constants.d.ts +6 -0
  131. package/build/types/Snackbar/constants.d.ts.map +1 -0
  132. package/build/types/Snackbar/index.d.ts +3 -1
  133. package/build/types/Snackbar/index.d.ts.map +1 -1
  134. package/build/types/Snackbar/useSnackbar.d.ts +1 -1
  135. package/build/types/Snackbar/useSnackbar.d.ts.map +1 -1
  136. package/build/types/Snackbar/useSnackbarAutoDismiss.d.ts +18 -0
  137. package/build/types/Snackbar/useSnackbarAutoDismiss.d.ts.map +1 -0
  138. package/build/types/Snackbar/useSnackbarManager.d.ts +17 -0
  139. package/build/types/Snackbar/useSnackbarManager.d.ts.map +1 -0
  140. package/build/types/Snackbar/useSnackbarSwipeDismiss.d.ts +11 -0
  141. package/build/types/Snackbar/useSnackbarSwipeDismiss.d.ts.map +1 -0
  142. package/build/types/common/PropsValues/typography.d.ts +1 -13
  143. package/build/types/common/PropsValues/typography.d.ts.map +1 -1
  144. package/build/types/index.d.ts +2 -2
  145. package/build/types/index.d.ts.map +1 -1
  146. package/build/types/test-utils/index.d.ts +2 -0
  147. package/build/types/test-utils/index.d.ts.map +1 -1
  148. package/package.json +2 -2
  149. package/src/Button/Button.css +3 -0
  150. package/src/Button/Button.less +16 -0
  151. package/src/Button/_stories/Button.test.story.tsx +12 -12
  152. package/src/Calendar/DateHeader/DateHeader.tsx +3 -3
  153. package/src/Carousel/Carousel.tsx +3 -3
  154. package/src/DateInput/DateInput.css +0 -13
  155. package/src/DateInput/DateInput.less +2 -10
  156. package/src/DateInput/_stories/DateInput.test.story.tsx +13 -0
  157. package/src/Display/_stories/Display.story.tsx +6 -20
  158. package/src/ExpressiveMoneyInput/_stories/ExpressiveMoneyInput.rtl.test.story.tsx +1 -1
  159. package/src/InputWithDisplayFormat/_stories/InputWithDisplayFormat.test.story.tsx +0 -52
  160. package/src/Inputs/Input/Input.css +24 -0
  161. package/src/Inputs/SelectInput/OptionContent/SelectInputOptionContent.tsx +1 -4
  162. package/src/Inputs/SelectInput/SelectInput.test.tsx +0 -74
  163. package/src/Inputs/TextArea/TextArea.css +24 -0
  164. package/src/ListItem/ListItem.css +1 -1
  165. package/src/ListItem/ListItem.grid.less +4 -4
  166. package/src/ListItem/ListItem.less +1 -1
  167. package/src/ListItem/Navigation/ListItemNavigation.tsx +2 -2
  168. package/src/PhoneNumberInput/PhoneNumberInput.css +0 -3
  169. package/src/PhoneNumberInput/PhoneNumberInput.less +2 -10
  170. package/src/PhoneNumberInput/PhoneNumberInput.tsx +0 -1
  171. package/src/PhoneNumberInput/_stories/PhoneNumberInput.rtl.test.story.tsx +1 -1
  172. package/src/Popover/Popover.css +6 -0
  173. package/src/PromoCard/Indicator/PromoCardIndicator.tsx +2 -2
  174. package/src/PromoCard/PromoCard.css +3 -0
  175. package/src/PromoCard/PromoCard.less +5 -0
  176. package/src/Prompt/CriticalBanner/_stories/CriticalBanner.test.story.tsx +1 -1
  177. package/src/Snackbar/Snackbar.css +182 -61
  178. package/src/Snackbar/Snackbar.less +185 -56
  179. package/src/Snackbar/Snackbar.messages.ts +9 -0
  180. package/src/Snackbar/Snackbar.test.tsx +697 -0
  181. package/src/Snackbar/Snackbar.tsx +227 -96
  182. package/src/Snackbar/Snackbar.types.ts +26 -0
  183. package/src/Snackbar/SnackbarContext.ts +2 -2
  184. package/src/Snackbar/SnackbarPortal.tsx +110 -0
  185. package/src/Snackbar/SnackbarProvider.test.tsx +202 -0
  186. package/src/Snackbar/SnackbarProvider.tsx +25 -25
  187. package/src/Snackbar/_stories/Snackbar.accessibility.docs.mdx +73 -0
  188. package/src/Snackbar/_stories/Snackbar.rtl.test.story.tsx +1 -12
  189. package/src/Snackbar/_stories/Snackbar.story.tsx +447 -62
  190. package/src/Snackbar/_stories/Snackbar.test.story.tsx +91 -16
  191. package/src/Snackbar/constants.ts +5 -0
  192. package/src/Snackbar/index.ts +3 -1
  193. package/src/Snackbar/useSnackbarAutoDismiss.ts +155 -0
  194. package/src/Snackbar/useSnackbarManager.ts +181 -0
  195. package/src/Snackbar/useSnackbarSwipeDismiss.ts +129 -0
  196. package/src/_deprecated/Select/Select.css +6 -0
  197. package/src/common/PropsValues/typography.ts +1 -20
  198. package/src/i18n/en.json +1 -1
  199. package/src/index.ts +7 -2
  200. package/src/main.css +864 -1509
  201. package/src/styles/less/accordion.css +12 -0
  202. package/src/styles/less/button-groups.css +6 -0
  203. package/src/styles/less/buttons.css +2 -0
  204. package/src/styles/less/core/_typography.less +194 -26
  205. package/src/styles/less/decision.css +12 -0
  206. package/src/styles/less/dropdowns.css +18 -2
  207. package/src/styles/less/droppable.css +6 -0
  208. package/src/styles/less/footer.css +12 -0
  209. package/src/styles/less/forms/bootstrap-forms.css +24 -2
  210. package/src/styles/less/input-groups.css +42 -2
  211. package/src/styles/less/list-group.css +6 -0
  212. package/src/styles/less/navbar.css +18 -2
  213. package/src/styles/less/neptune-core.css +512 -4
  214. package/src/styles/less/neptune.bundle.less +0 -2
  215. package/src/styles/less/neptune.css +640 -1426
  216. package/src/styles/less/popovers.css +18 -0
  217. package/src/styles/less/tooltip.css +6 -0
  218. package/src/DateInput/_stories/DateInput.rtl.test.story.tsx +0 -36
@@ -1,89 +1,474 @@
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 type { SnackbarProps } from '../Snackbar.types';
11
12
  import { SnackbarConsumer } from '../SnackbarContext';
12
13
  import SnackbarProvider from '../SnackbarProvider';
13
14
 
14
- const meta: Meta = {
15
- component: Snackbar,
15
+ const SENTIMENTS = ['neutral', 'negative', 'warning', 'success', 'proposition'] as const;
16
+ const DURATIONS = ['default', 'extended', 'indefinite'] as const;
17
+
18
+ const meta = {
16
19
  title: 'Prompts/Snackbar',
17
- };
20
+ component: Snackbar,
21
+ parameters: {
22
+ docs: {
23
+ toc: true,
24
+ description: {
25
+ component:
26
+ 'Snackbar is a brief, temporary notification that confirms the outcome of an action or process that has just happened. Keep its message short and plain: it supports one optional action, but no inline links or rich-text formatting. `Snackbar` can be rendered directly; use `SnackbarProvider` with `useSnackbar()` for app-level transient feedback.',
27
+ },
28
+ },
29
+ },
30
+ args: {
31
+ duration: 'default',
32
+ loading: false,
33
+ sentiment: 'neutral',
34
+ text: 'Changes saved',
35
+ },
36
+ argTypes: {
37
+ text: {
38
+ control: 'text',
39
+ description: 'Short, plain-text Snackbar message.',
40
+ table: {
41
+ type: { summary: 'ReactNode' },
42
+ },
43
+ },
44
+ sentiment: {
45
+ control: 'radio',
46
+ options: SENTIMENTS,
47
+ description: 'Controls the colour scheme and sentiment-derived default media.',
48
+ table: {
49
+ defaultValue: { summary: 'neutral' },
50
+ type: { summary: 'Sentiment' },
51
+ },
52
+ },
53
+ media: {
54
+ control: false,
55
+ description:
56
+ 'Overrides the sentiment-derived media. Custom media is displayed in the 24×24px media slot and should provide its own accessibility attributes when meaningful.',
57
+ table: {
58
+ type: { summary: 'ReactNode' },
59
+ },
60
+ },
61
+ loading: {
62
+ control: 'boolean',
63
+ description:
64
+ 'Replaces the media with a ProcessIndicator, uses neutral styling, and prevents automatic dismissal.',
65
+ table: {
66
+ defaultValue: { summary: 'false' },
67
+ type: { summary: 'boolean' },
68
+ },
69
+ },
70
+ action: {
71
+ control: false,
72
+ description:
73
+ 'One optional small Button. Supports a callback action or navigation through href and target.',
74
+ table: {
75
+ type: {
76
+ summary: 'SnackbarAction',
77
+ detail:
78
+ "Pick<ButtonProps, 'onClick' | 'href' | 'target'> & { label: ButtonProps['children'] }",
79
+ },
80
+ },
81
+ },
82
+ duration: {
83
+ control: 'inline-radio',
84
+ options: DURATIONS,
85
+ description: 'Controls automatic dismissal using the design-defined duration presets.',
86
+ table: {
87
+ defaultValue: { summary: 'default' },
88
+ type: { summary: "'default' | 'extended' | 'indefinite'" },
89
+ },
90
+ },
91
+ onDismiss: {
92
+ control: false,
93
+ description: 'Called after the Snackbar completes its exit transition.',
94
+ table: {
95
+ type: { summary: '() => void' },
96
+ },
97
+ },
98
+ timeout: {
99
+ table: { disable: true },
100
+ },
101
+ theme: {
102
+ table: { disable: true },
103
+ },
104
+ },
105
+ } satisfies Meta<SnackbarProps>;
106
+
18
107
  export default meta;
108
+ type Story = StoryObj<typeof meta>;
109
+
110
+ type PreviewStoryArgs = SnackbarProps & {
111
+ previewAction: boolean;
112
+ previewMedia: 'default' | 'custom';
113
+ previewOnDismiss: boolean;
114
+ };
115
+
116
+ type SnackbarExample = {
117
+ label: string;
118
+ props: SnackbarProps;
119
+ };
120
+
121
+ const previewArgGroup = {
122
+ category: 'Storybook Preview options',
123
+ type: { summary: undefined },
124
+ };
125
+
126
+ const previewArgTypes = {
127
+ previewAction: {
128
+ name: 'Preview with `action`',
129
+ control: 'boolean',
130
+ table: previewArgGroup,
131
+ },
132
+ previewMedia: {
133
+ name: 'Preview with `media`',
134
+ control: 'radio',
135
+ options: ['default', 'custom'],
136
+ table: previewArgGroup,
137
+ },
138
+ previewOnDismiss: {
139
+ name: 'Preview with `onDismiss`',
140
+ control: 'boolean',
141
+ table: previewArgGroup,
142
+ },
143
+ } as const;
144
+
145
+ const SnackbarExamples = ({ examples }: { examples: SnackbarExample[] }) => (
146
+ <SnackbarProvider>
147
+ <SnackbarConsumer>
148
+ {({ createSnackbar }) => (
149
+ <div
150
+ style={{
151
+ display: 'flex',
152
+ flexWrap: 'wrap',
153
+ gap: 'var(--size-8)',
154
+ }}
155
+ >
156
+ {examples.map(({ label, props }) => (
157
+ <Button key={label} v2 size="md" onClick={() => createSnackbar(props)}>
158
+ {label}
159
+ </Button>
160
+ ))}
161
+ </div>
162
+ )}
163
+ </SnackbarConsumer>
164
+ </SnackbarProvider>
165
+ );
166
+
167
+ /**
168
+ * Use the controls to preview the public Snackbar properties. Rich values such as `action`,
169
+ * `media`, and `onDismiss` use dedicated preview controls because Storybook cannot edit React
170
+ * elements or callback functions directly.
171
+ */
172
+ export const Playground: StoryObj<PreviewStoryArgs> = {
173
+ args: {
174
+ duration: 'default',
175
+ previewAction: true,
176
+ previewMedia: 'default',
177
+ previewOnDismiss: true,
178
+ loading: false,
179
+ },
180
+ argTypes: previewArgTypes,
181
+ render: ({ previewAction, previewMedia, previewOnDismiss, ...props }) => (
182
+ <SnackbarExamples
183
+ examples={[
184
+ {
185
+ label: 'Show Snackbar',
186
+ props: {
187
+ ...props,
188
+ action: previewAction ? { label: 'View', onClick: fn() } : undefined,
189
+ media: previewMedia === 'custom' ? <Card title="Card" /> : undefined,
190
+ onDismiss: previewOnDismiss ? fn() : undefined,
191
+ },
192
+ },
193
+ ]}
194
+ />
195
+ ),
196
+ };
19
197
 
20
198
  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
- })
33
- }
34
- >
35
- Show Snackbar
36
- </Button>
37
- )}
38
- </SnackbarConsumer>
39
- </SnackbarProvider>`,
199
+ code: `const SnackbarTrigger = () => {
200
+ const showSnackbar = useSnackbar();
201
+
202
+ return (
203
+ <Button
204
+ v2
205
+ size="md"
206
+ onClick={() =>
207
+ showSnackbar({
208
+ text: 'Transfer completed successfully',
209
+ sentiment: 'success',
210
+ action: {
211
+ label: 'View',
212
+ onClick: () => console.log('View clicked'),
213
+ },
214
+ duration: 'extended',
215
+ })
216
+ }
217
+ >
218
+ Show Snackbar
219
+ </Button>
220
+ );
221
+ };
222
+
223
+ const App = () => (
224
+ <SnackbarProvider>
225
+ <SnackbarTrigger />
226
+ </SnackbarProvider>
227
+ );`,
40
228
  scope: globalScope,
41
229
  });
42
230
 
43
- export const Basic: StoryObj = {
231
+ /**
232
+ * `Snackbar` remains available for direct rendering. For app-level feedback, wrap the relevant
233
+ * app boundary in `SnackbarProvider`, then call the function returned by `useSnackbar()` after an
234
+ * action or process completes. The Provider owns mounting, timers, replacement, and
235
+ * exit-before-enter behaviour.
236
+ *
237
+ * Keep the message short and plain. Snackbar copy does not support inline links or bold text.
238
+ */
239
+ export const Basic: Story = {
240
+ args: {
241
+ duration: 'indefinite',
242
+ sentiment: 'success',
243
+ text: 'Changes saved',
244
+ },
245
+ parameters: {
246
+ docs: {
247
+ source: {
248
+ code: `// Direct rendering
249
+ <Snackbar text="Changes saved" sentiment="success" />;
250
+
251
+ // Programmatic app-level feedback
252
+ const showSnackbar = useSnackbar();
253
+
254
+ showSnackbar({
255
+ text: 'Changes saved',
256
+ sentiment: 'success',
257
+ });`,
258
+ },
259
+ },
260
+ },
261
+ render: (args) => <SnackbarExamples examples={[{ label: 'Show Snackbar', props: args }]} />,
262
+ };
263
+
264
+ /**
265
+ * Use `sentiment` to match the meaning of the feedback. Sentiment controls appearance and default
266
+ * media; it does not change the polite screen-reader announcement.
267
+ *
268
+ * - `neutral`: general confirmation or information
269
+ * - `negative`: failed or unsuccessful outcome
270
+ * - `warning`: outcome that needs attention
271
+ * - `success`: successful completion
272
+ * - `proposition`: a relevant product proposition or benefit
273
+ */
274
+ export const Sentiments: Story = {
275
+ parameters: {
276
+ docs: {
277
+ source: {
278
+ code: `showSnackbar({ text: 'Details updated', sentiment: 'neutral' });
279
+ showSnackbar({ text: 'Transfer failed', sentiment: 'negative' });
280
+ showSnackbar({ text: 'Check the recipient details', sentiment: 'warning' });
281
+ showSnackbar({ text: 'Transfer completed', sentiment: 'success' });
282
+ showSnackbar({ text: 'You can earn a referral reward', sentiment: 'proposition' });`,
283
+ },
284
+ },
285
+ },
286
+ render: () => (
287
+ <SnackbarExamples
288
+ examples={[
289
+ { label: 'Neutral', props: { text: 'Details updated', sentiment: 'neutral' } },
290
+ { label: 'Negative', props: { text: 'Transfer failed', sentiment: 'negative' } },
291
+ {
292
+ label: 'Warning',
293
+ props: { text: 'Check the recipient details', sentiment: 'warning' },
294
+ },
295
+ { label: 'Success', props: { text: 'Transfer completed', sentiment: 'success' } },
296
+ {
297
+ label: 'Proposition',
298
+ props: { text: 'You can earn a referral reward', sentiment: 'proposition' },
299
+ },
300
+ ]}
301
+ />
302
+ ),
303
+ };
304
+
305
+ /**
306
+ * Snackbar supports one optional small action. Use `onClick` for an in-place action or `href` for
307
+ * navigation. An action dismisses the Snackbar, so use `duration="extended"` when people need
308
+ * enough time to read and act.
309
+ */
310
+ export const Actions: Story = {
311
+ parameters: {
312
+ docs: {
313
+ source: {
314
+ code: `showSnackbar({
315
+ text: 'Transfer deleted',
316
+ action: {
317
+ label: 'Undo',
318
+ onClick: handleUndo,
319
+ },
320
+ duration: 'extended',
321
+ });
322
+
323
+ showSnackbar({
324
+ text: 'Your transfer is ready',
325
+ action: {
326
+ label: 'View transfer',
327
+ href: '/transfers/123',
328
+ },
329
+ duration: 'extended',
330
+ });`,
331
+ },
332
+ },
333
+ },
334
+ render: () => (
335
+ <SnackbarExamples
336
+ examples={[
337
+ {
338
+ label: 'Callback action',
339
+ props: {
340
+ text: 'Transfer deleted',
341
+ action: { label: 'Undo', onClick: fn() },
342
+ duration: 'extended',
343
+ },
344
+ },
345
+ {
346
+ label: 'Navigation action',
347
+ props: {
348
+ text: 'Your transfer is ready',
349
+ action: { label: 'View transfer', href: '/transfers/123' },
350
+ duration: 'extended',
351
+ },
352
+ },
353
+ ]}
354
+ />
355
+ ),
356
+ };
357
+
358
+ /**
359
+ * Default media is derived from `sentiment`. Use `media` only when a custom icon communicates the
360
+ * message more clearly. `loading` has the highest priority: it replaces default or custom media
361
+ * with a ProcessIndicator, switches to neutral styling, and remains visible indefinitely.
362
+ */
363
+ export const MediaAndLoading: Story = {
44
364
  parameters: {
45
365
  docs: {
46
366
  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;
367
+ code: `// Sentiment-derived default media
368
+ showSnackbar({ text: 'Transfer completed', sentiment: 'success' });
369
+
370
+ // Custom media
371
+ showSnackbar({
372
+ text: 'Added to your wallet',
373
+ sentiment: 'success',
374
+ media: <Card title="Card" />,
375
+ });
376
+
377
+ // Loading takes precedence over media and duration
378
+ showSnackbar({
379
+ text: 'Uploading document',
380
+ loading: true,
381
+ });`,
382
+ },
383
+ },
384
+ },
385
+ render: () => (
386
+ <SnackbarExamples
387
+ examples={[
388
+ {
389
+ label: 'Default media',
390
+ props: { text: 'Transfer completed', sentiment: 'success', duration: 'indefinite' },
391
+ },
392
+ {
393
+ label: 'Custom media',
394
+ props: {
395
+ text: 'Added to your wallet',
396
+ sentiment: 'success',
397
+ media: <Card title="Card" />,
398
+ duration: 'indefinite',
399
+ },
51
400
  },
401
+ {
402
+ label: 'Loading',
403
+ props: { text: 'Uploading document', loading: true },
404
+ },
405
+ ]}
406
+ />
407
+ ),
408
+ };
409
+
410
+ /**
411
+ * Duration is semantic rather than accepting arbitrary milliseconds:
412
+ *
413
+ * - `default`: 4 seconds for short confirmation without an action
414
+ * - `extended`: 6 seconds for additional reading or an action
415
+ * - `indefinite`: no automatic dismissal
416
+ *
417
+ * Timed Snackbars pause while hovered or while keyboard focus is inside, then resume when the
418
+ * interaction ends. Loading Snackbars are always indefinite.
419
+ *
420
+ * The deprecated millisecond `timeout` remains supported during migration but is not available in
421
+ * Storybook controls. Migrate values from 0–5000ms to `default`, values above 5000ms to `extended`,
422
+ * and intentionally persistent usage to `indefinite`. Semantic `duration` takes precedence when
423
+ * both are provided.
424
+ */
425
+ export const Durations: Story = {
426
+ parameters: {
427
+ docs: {
428
+ source: {
429
+ code: `showSnackbar({ text: 'Changes saved', duration: 'default' });
430
+ showSnackbar({ text: 'Transfer deleted', duration: 'extended' });
431
+ showSnackbar({ text: 'Uploading document', duration: 'indefinite' });`,
52
432
  },
53
433
  },
54
- source: {
55
- type: 'code',
56
- },
57
- },
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
- );
83
434
  },
84
- play: async ({ canvasElement }) => {
85
- await wait(0);
86
- const canvas = within(canvasElement);
87
- await userEvent.click(canvas.getByRole('button'));
435
+ render: () => (
436
+ <SnackbarExamples
437
+ examples={[
438
+ { label: 'Default · 4s', props: { text: 'Changes saved', duration: 'default' } },
439
+ { label: 'Extended · 6s', props: { text: 'Transfer deleted', duration: 'extended' } },
440
+ {
441
+ label: 'Indefinite',
442
+ props: { text: 'Uploading document', duration: 'indefinite' },
443
+ },
444
+ ]}
445
+ />
446
+ ),
447
+ };
448
+
449
+ /**
450
+ * Snackbars dismiss after their duration, after their action is used, through the desktop close
451
+ * button, or with a downward swipe on mobile. A mobile swipe must reach 50% of the rendered
452
+ * Snackbar height; shorter or cancelled swipes return it to its resting position.
453
+ *
454
+ * `onDismiss` runs after the exit transition completes, regardless of how dismissal started.
455
+ */
456
+ export const Dismissal: Story = {
457
+ args: {
458
+ duration: 'indefinite',
459
+ onDismiss: fn(),
460
+ text: 'Swipe down or use the close button',
461
+ },
462
+ parameters: {
463
+ docs: {
464
+ source: {
465
+ code: `showSnackbar({
466
+ text: 'Changes saved',
467
+ duration: 'indefinite',
468
+ onDismiss: handleDismiss,
469
+ });`,
470
+ },
471
+ },
88
472
  },
473
+ render: (args) => <SnackbarExamples examples={[{ label: 'Show Snackbar', props: args }]} />,
89
474
  };