@transferwise/components 46.115.1 → 46.116.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 (77) hide show
  1. package/build/alert/Alert.js +2 -1
  2. package/build/alert/Alert.js.map +1 -1
  3. package/build/alert/Alert.mjs +2 -1
  4. package/build/alert/Alert.mjs.map +1 -1
  5. package/build/criticalBanner/CriticalCommsBanner.js +1 -0
  6. package/build/criticalBanner/CriticalCommsBanner.js.map +1 -1
  7. package/build/criticalBanner/CriticalCommsBanner.mjs +1 -0
  8. package/build/criticalBanner/CriticalCommsBanner.mjs.map +1 -1
  9. package/build/main.css +428 -44
  10. package/build/mocks.js +7 -0
  11. package/build/mocks.js.map +1 -1
  12. package/build/mocks.mjs +7 -1
  13. package/build/mocks.mjs.map +1 -1
  14. package/build/sentimentSurface/SentimentSurface.js +43 -0
  15. package/build/sentimentSurface/SentimentSurface.js.map +1 -0
  16. package/build/sentimentSurface/SentimentSurface.mjs +39 -0
  17. package/build/sentimentSurface/SentimentSurface.mjs.map +1 -0
  18. package/build/sentimentSurface/classMap.js +17 -0
  19. package/build/sentimentSurface/classMap.js.map +1 -0
  20. package/build/sentimentSurface/classMap.mjs +14 -0
  21. package/build/sentimentSurface/classMap.mjs.map +1 -0
  22. package/build/statusIcon/StatusIcon.js +10 -1
  23. package/build/statusIcon/StatusIcon.js.map +1 -1
  24. package/build/statusIcon/StatusIcon.mjs +10 -1
  25. package/build/statusIcon/StatusIcon.mjs.map +1 -1
  26. package/build/styles/inputs/Input.css +2 -4
  27. package/build/styles/inputs/TextArea.css +2 -4
  28. package/build/styles/main.css +428 -44
  29. package/build/styles/popover/Popover.css +2 -4
  30. package/build/styles/sentimentSurface/SentimentSurface.css +420 -0
  31. package/build/styles/statusIcon/StatusIcon.css +4 -36
  32. package/build/types/alert/Alert.d.ts.map +1 -1
  33. package/build/types/criticalBanner/CriticalCommsBanner.d.ts +2 -1
  34. package/build/types/criticalBanner/CriticalCommsBanner.d.ts.map +1 -1
  35. package/build/types/mocks.d.ts +1 -0
  36. package/build/types/mocks.d.ts.map +1 -1
  37. package/build/types/sentimentSurface/SentimentSurface.d.ts +30 -0
  38. package/build/types/sentimentSurface/SentimentSurface.d.ts.map +1 -0
  39. package/build/types/sentimentSurface/SentimentSurface.types.d.ts +80 -0
  40. package/build/types/sentimentSurface/SentimentSurface.types.d.ts.map +1 -0
  41. package/build/types/sentimentSurface/classMap.d.ts +4 -0
  42. package/build/types/sentimentSurface/classMap.d.ts.map +1 -0
  43. package/build/types/sentimentSurface/index.d.ts +3 -0
  44. package/build/types/sentimentSurface/index.d.ts.map +1 -0
  45. package/build/types/statusIcon/StatusIcon.d.ts.map +1 -1
  46. package/build/types/test-utils/window-mock.d.ts +1 -0
  47. package/build/types/test-utils/window-mock.d.ts.map +1 -1
  48. package/package.json +2 -2
  49. package/src/alert/Alert.tsx +3 -1
  50. package/src/criticalBanner/CriticalCommsBanner.tsx +3 -2
  51. package/src/expressiveMoneyInput/ExpressiveMoneyInput.spec.tsx +229 -0
  52. package/src/expressiveMoneyInput/amountInput/AmountInput.spec.tsx +282 -0
  53. package/src/expressiveMoneyInput/currencySelector/CurrencySelector.spec.tsx +160 -0
  54. package/src/inputs/Input.css +2 -4
  55. package/src/inputs/SelectInput.spec.tsx +7 -1
  56. package/src/inputs/TextArea.css +2 -4
  57. package/src/main.css +428 -44
  58. package/src/main.less +2 -0
  59. package/src/mocks.ts +7 -0
  60. package/src/moneyInput/MoneyInput.spec.tsx +9 -1
  61. package/src/popover/Popover.css +2 -4
  62. package/src/provider/theme/ThemeProvider.story.tsx +78 -11
  63. package/src/sentimentSurface/SentimentSurface.css +420 -0
  64. package/src/sentimentSurface/SentimentSurface.docs.mdx +549 -0
  65. package/src/sentimentSurface/SentimentSurface.less +293 -0
  66. package/src/sentimentSurface/SentimentSurface.spec.tsx +140 -0
  67. package/src/sentimentSurface/SentimentSurface.story.tsx +303 -0
  68. package/src/sentimentSurface/SentimentSurface.tests.story.tsx +72 -0
  69. package/src/sentimentSurface/SentimentSurface.tsx +72 -0
  70. package/src/sentimentSurface/SentimentSurface.types.ts +104 -0
  71. package/src/sentimentSurface/classMap.ts +15 -0
  72. package/src/sentimentSurface/index.ts +8 -0
  73. package/src/statusIcon/StatusIcon.css +4 -36
  74. package/src/statusIcon/StatusIcon.less +3 -41
  75. package/src/statusIcon/StatusIcon.tsx +14 -1
  76. package/src/test-utils/jest.setup.ts +0 -5
  77. package/src/test-utils/window-mock.ts +5 -0
@@ -0,0 +1,549 @@
1
+ import { Meta, Source, ArgTypes } from '@storybook/addon-docs/blocks';
2
+ import * as SentimentSurfaceStories from './SentimentSurface.story';
3
+
4
+ <Meta title="Content/SentimentSurface/Developer Guide" />
5
+
6
+ # SentimentSurface Developer Guide
7
+
8
+ `SentimentSurface` is a polymorphic container component that establishes a semantic colour context by setting CSS custom properties (tokens) based on sentiment types. It does not apply any default background or text styling - instead, it provides access to sentiment-aware tokens that child components can use to style themselves appropriately.
9
+
10
+ ## Basic Usage
11
+
12
+ Import and use the component with a required `sentiment` prop:
13
+
14
+ <Source dark code={`
15
+ import { SentimentSurface } from '@transferwise/components';
16
+
17
+ <SentimentSurface sentiment="negative">Your payment has failed</SentimentSurface>
18
+ `} />
19
+
20
+ ### Props
21
+
22
+ <ArgTypes of={SentimentSurfaceStories} />
23
+
24
+ ## Sentiment Types
25
+
26
+ The component supports five sentiment types (`negative`, `warning`, `neutral`, `proposition` and `success`), each communicating different types of information:
27
+
28
+ ```tsx
29
+ <SentimentSurface sentiment="negative">
30
+ Your payment has failed. Please try again.
31
+ </SentimentSurface>
32
+
33
+ <SentimentSurface sentiment="warning">
34
+ Action required: Please verify your email address.
35
+ </SentimentSurface>
36
+
37
+ <SentimentSurface sentiment="neutral">
38
+ Your account is up to date.
39
+ </SentimentSurface>
40
+
41
+ <SentimentSurface sentiment="proposition">
42
+ Try our new feature and save time on transfers.
43
+ </SentimentSurface>
44
+
45
+ <SentimentSurface sentiment="success">
46
+ Your payment was successful!
47
+ </SentimentSurface>
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Emphasis Levels
53
+
54
+ Each sentiment type supports two emphasis levels that provide different sets of token values (`base`, `elevated`):
55
+
56
+ ```tsx
57
+ <SentimentSurface sentiment="success" emphasis="base">
58
+ Base emphasis - provides tokens suited for subtle contexts
59
+ </SentimentSurface>
60
+
61
+ <SentimentSurface sentiment="success" emphasis="elevated">
62
+ Elevated emphasis - provides tokens suited for prominent contexts
63
+ </SentimentSurface>
64
+ ```
65
+
66
+ **When to use:**
67
+
68
+ - **Base**: For inline notifications, subtle callouts, or when sentiment needs to be communicated without drawing too much attention
69
+ - **Elevated**: For prominent banners, critical alerts, or when the sentiment should be immediately noticeable
70
+
71
+ Note: The component does not apply background or text colours by default. Use the provided tokens in your styles to achieve the desired visual effect.
72
+
73
+ ---
74
+
75
+ ## Polymorphic Rendering
76
+
77
+ The component is fully polymorphic, meaning it can render as any HTML element using the `as` prop while maintaining full type safety.
78
+
79
+ ### Basic Examples
80
+
81
+ ```tsx
82
+ // Render as div (default)
83
+ <SentimentSurface sentiment="neutral">
84
+ Default div element
85
+ </SentimentSurface>
86
+
87
+ // Render as section
88
+ <SentimentSurface sentiment="negative" as="section">
89
+ Semantic section element
90
+ </SentimentSurface>
91
+
92
+ // Render as article
93
+ <SentimentSurface sentiment="success" as="article">
94
+ Article element for standalone content
95
+ </SentimentSurface>
96
+ ```
97
+
98
+ ### With HTML Attributes
99
+
100
+ The component accepts all valid HTML attributes for the rendered element:
101
+
102
+ ```tsx
103
+ // Section with ARIA attributes
104
+ <SentimentSurface
105
+ sentiment="negative"
106
+ as="section"
107
+ role="alert"
108
+ aria-live="polite"
109
+ aria-atomic="true"
110
+ >
111
+ Critical error message
112
+ </SentimentSurface>
113
+
114
+ // Article with data attributes
115
+ <SentimentSurface
116
+ sentiment="proposition"
117
+ as="article"
118
+ data-tracking-id="promo-banner"
119
+ data-position="top"
120
+ >
121
+ Promotional content
122
+ </SentimentSurface>
123
+ ```
124
+
125
+ ### Type Safety
126
+
127
+ TypeScript provides full type checking for HTML attributes based on the `as` prop:
128
+
129
+ ```tsx
130
+ // ✅ Valid - section supports these attributes
131
+ <SentimentSurface as="section" role="region" aria-labelledby="heading">
132
+ Content
133
+ </SentimentSurface>
134
+
135
+ // ❌ TypeScript error - 'href' is not valid for 'div'
136
+ <SentimentSurface as="div" href="/link">
137
+ Content
138
+ </SentimentSurface>
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Nesting Sentiment Surfaces
144
+
145
+ Sentiment surfaces can be nested to create layered contexts. Each nested surface establishes its own colour context through CSS custom properties.
146
+
147
+ ### Basic Nesting
148
+
149
+ ```tsx
150
+ <SentimentSurface sentiment="negative" emphasis="base">
151
+ <Title size="large">Payment Failed</Title>
152
+ <Body>There was an issue processing your payment.</Body>
153
+
154
+ <SentimentSurface sentiment="neutral" emphasis="elevated">
155
+ <Title size="small">What happened?</Title>
156
+ <Body>Your card was declined by your bank.</Body>
157
+ </SentimentSurface>
158
+ </SentimentSurface>
159
+ ```
160
+
161
+ ### Multi-Level Nesting
162
+
163
+ ```tsx
164
+ <SentimentSurface sentiment="success" emphasis="base">
165
+ Outer: Payment successful
166
+ <SentimentSurface sentiment="neutral" emphasis="elevated">
167
+ Inner: Transaction details
168
+ <SentimentSurface sentiment="warning" emphasis="base">
169
+ Deepest: Additional fees may apply
170
+ </SentimentSurface>
171
+ </SentimentSurface>
172
+ </SentimentSurface>
173
+ ```
174
+
175
+ ### Nesting with Different Elements
176
+
177
+ ```tsx
178
+ <SentimentSurface sentiment="negative" as="section">
179
+ <Title size="large">Error Section</Title>
180
+
181
+ <SentimentSurface sentiment="neutral" as="article">
182
+ <Title size="small">Details</Title>
183
+ <Body>More information about the error.</Body>
184
+ </SentimentSurface>
185
+
186
+ <SentimentSurface sentiment="proposition" as="aside">
187
+ <Body>Need help? Contact support.</Body>
188
+ </SentimentSurface>
189
+ </SentimentSurface>
190
+ ```
191
+
192
+ ---
193
+
194
+ ## CSS Custom Properties (Tokens)
195
+
196
+ The component sets CSS custom properties (CSS variables) that child components can use to style themselves according to the sentiment context. The component itself does not apply any visual styling - it simply makes these tokens available for use in your own styles or by sentiment-aware child components.
197
+
198
+ ### Available Tokens
199
+
200
+ Each `SentimentSurface` sets tokens for content, interactive elements, and background surfaces. See the [Tokens story](?path=/story/content-sentimentsurface--tokens) for a visual demonstration of all tokens across sentiments and emphasis levels:
201
+
202
+ #### Content Tokens
203
+
204
+ ```css
205
+ --color-sentiment-content-primary
206
+ --color-sentiment-content-primary-hover
207
+ --color-sentiment-content-primary-active
208
+ ```
209
+
210
+ #### Interactive Primary Tokens
211
+
212
+ ```css
213
+ --color-sentiment-interactive-primary
214
+ --color-sentiment-interactive-primary-hover
215
+ --color-sentiment-interactive-primary-active
216
+ ```
217
+
218
+ #### Interactive Secondary Tokens
219
+
220
+ ```css
221
+ --color-sentiment-interactive-secondary
222
+ --color-sentiment-interactive-secondary-hover
223
+ --color-sentiment-interactive-secondary-active
224
+ ```
225
+
226
+ #### Interactive Secondary Neutral Tokens
227
+
228
+ ```css
229
+ --color-sentiment-interactive-secondary-neutral
230
+ --color-sentiment-interactive-secondary-neutral-hover
231
+ --color-sentiment-interactive-secondary-neutral-active
232
+ ```
233
+
234
+ #### Interactive Control Tokens
235
+
236
+ ```css
237
+ --color-sentiment-interactive-control
238
+ --color-sentiment-interactive-control-hover
239
+ --color-sentiment-interactive-control-active
240
+ ```
241
+
242
+ #### Background Surface Tokens
243
+
244
+ ```css
245
+ --color-sentiment-background-surface
246
+ --color-sentiment-background-surface-hover
247
+ --color-sentiment-background-surface-active
248
+ ```
249
+
250
+ ### Using Tokens in Component Styles
251
+
252
+ Components can reference these tokens with fallbacks to maintain functionality outside of a `SentimentSurface`:
253
+
254
+ ```css
255
+ .wds-Button {
256
+ /* Primary button uses interactive primary tokens */
257
+ --Button-background: var(--color-sentiment-interactive-primary, var(--color-interactive-accent));
258
+ --Button-background-hover: var(
259
+ --color-sentiment-interactive-primary-hover,
260
+ var(--color-interactive-accent-hover)
261
+ );
262
+ --Button-background-active: var(
263
+ --color-sentiment-interactive-primary-active,
264
+ var(--color-interactive-accent-active)
265
+ );
266
+
267
+ /* Button text color uses control tokens */
268
+ --Button-color: var(--color-sentiment-interactive-control, var(--color-interactive-control));
269
+ --Button-color-hover: var(
270
+ --color-sentiment-interactive-control-hover,
271
+ var(--color-interactive-control-hover)
272
+ );
273
+ --Button-color-active: var(
274
+ --color-sentiment-interactive-control-active,
275
+ var(--color-interactive-control-active)
276
+ );
277
+ }
278
+
279
+ .wds-Button--secondary {
280
+ /* Secondary button uses secondary tokens */
281
+ --Button-background: var(
282
+ --color-sentiment-interactive-secondary,
283
+ var(--color-interactive-neutral)
284
+ );
285
+ --Button-background-hover: var(
286
+ --color-sentiment-interactive-secondary-hover,
287
+ var(--color-interactive-neutral-hover)
288
+ );
289
+ --Button-color: var(--color-sentiment-interactive-primary, var(--color-interactive-primary));
290
+ }
291
+ ```
292
+
293
+ ### Example: Button in Sentiment Context
294
+
295
+ ```tsx
296
+ import { Button, Body } from '@transferwise/components';
297
+
298
+ // Button automatically adapts to the sentiment context
299
+ <SentimentSurface sentiment="success">
300
+ <Body>Your payment was successful!</Body>
301
+ <Button v2 priority="primary">View Receipt</Button>
302
+ <Button v2 priority="secondary">Send Another</Button>
303
+ </SentimentSurface>
304
+
305
+ // Same buttons in different sentiment context
306
+ <SentimentSurface sentiment="negative">
307
+ <Body>Payment failed</Body>
308
+ <Button v2 priority="primary">Try Again</Button>
309
+ <Button v2 priority="secondary">Cancel</Button>
310
+ </SentimentSurface>
311
+ ```
312
+
313
+ The buttons will automatically use appropriate colours based on the sentiment context.
314
+
315
+ ### Applying Sentiment Tokens
316
+
317
+ To visually style content within a SentimentSurface, use the provided tokens in your styles:
318
+
319
+ ```tsx
320
+ // Apply tokens via inline styles
321
+ <SentimentSurface sentiment="warning">
322
+ <div
323
+ style={{
324
+ padding: '24px',
325
+ borderRadius: '16px',
326
+ backgroundColor: 'var(--color-sentiment-background-surface)',
327
+ color: 'var(--color-sentiment-content-primary)',
328
+ }}
329
+ >
330
+ This content uses the sentiment tokens for styling
331
+ </div>
332
+ </SentimentSurface>
333
+ ```
334
+
335
+ ### Creating Custom Components with Sentiment Tokens
336
+
337
+ You can create your own components that respect sentiment context:
338
+
339
+ ```tsx
340
+ // CustomCard.css
341
+ .custom-card {
342
+ background-color: var(
343
+ --color-sentiment-background-surface,
344
+ var(--color-background-surface)
345
+ );
346
+ color: var(
347
+ --color-sentiment-content-primary,
348
+ var(--color-content-primary)
349
+ );
350
+ border: 1px solid var(
351
+ --color-sentiment-interactive-secondary,
352
+ var(--color-border-neutral)
353
+ );
354
+ }
355
+
356
+ .custom-card:hover {
357
+ background-color: var(
358
+ --color-sentiment-background-surface-hover,
359
+ var(--color-background-surface-hover)
360
+ );
361
+ }
362
+
363
+ .custom-card-link {
364
+ color: var(
365
+ --color-sentiment-interactive-primary,
366
+ var(--color-interactive-primary)
367
+ );
368
+ }
369
+
370
+ .custom-card-link:hover {
371
+ color: var(
372
+ --color-sentiment-interactive-primary-hover,
373
+ var(--color-interactive-primary-hover)
374
+ );
375
+ }
376
+ ```
377
+
378
+ ```tsx
379
+ // CustomCard.tsx
380
+ function CustomCard({ children }) {
381
+ return (
382
+ <div className="custom-card">
383
+ {children}
384
+ <a href="#" className="custom-card-link">
385
+ Learn more
386
+ </a>
387
+ </div>
388
+ );
389
+ }
390
+
391
+ // Usage
392
+ <SentimentSurface sentiment="warning">
393
+ <CustomCard>This card adapts to the warning sentiment</CustomCard>
394
+ </SentimentSurface>;
395
+ ```
396
+
397
+ ---
398
+
399
+ ## Accessibility Considerations
400
+
401
+ ### Semantic HTML
402
+
403
+ Use appropriate HTML elements via the `as` prop to ensure proper document structure:
404
+
405
+ ```tsx
406
+ // ✅ Good - semantic HTML for alerts
407
+ <SentimentSurface
408
+ sentiment="negative"
409
+ as="section"
410
+ role="alert"
411
+ aria-live="assertive"
412
+ >
413
+ Critical error: Payment failed
414
+ </SentimentSurface>
415
+
416
+ // ✅ Good - article for standalone content
417
+ <SentimentSurface sentiment="proposition" as="article">
418
+ <Title size="large">New Feature Available</Title>
419
+ <Body>Try our new instant transfer feature...</Body>
420
+ </SentimentSurface>
421
+ ```
422
+
423
+ ### ARIA Attributes
424
+
425
+ Add appropriate ARIA attributes for screen readers:
426
+
427
+ ```tsx
428
+ // For errors or critical warnings
429
+ <SentimentSurface
430
+ sentiment="negative"
431
+ role="alert"
432
+ aria-live="assertive"
433
+ aria-atomic="true"
434
+ >
435
+ Error: Your session has expired
436
+ </SentimentSurface>
437
+
438
+ // For important but not critical information
439
+ <SentimentSurface
440
+ sentiment="warning"
441
+ role="status"
442
+ aria-live="polite"
443
+ >
444
+ Please verify your email address
445
+ </SentimentSurface>
446
+
447
+ // For regions with headings
448
+ <SentimentSurface
449
+ sentiment="success"
450
+ as="section"
451
+ role="region"
452
+ aria-labelledby="success-heading"
453
+ >
454
+ <Title id="success-heading" size="large">Payment Successful</Title>
455
+ <Body>Your payment has been processed.</Body>
456
+ </SentimentSurface>
457
+ ```
458
+
459
+ ### Color Contrast
460
+
461
+ The tokens provided by the component are designed with WCAG AA contrast ratios in mind:
462
+
463
+ - **Base emphasis**: Tokens provide sufficient contrast for body text
464
+ - **Elevated emphasis**: Tokens provide higher contrast, often with inverted colours
465
+
466
+ Always test your content for contrast compliance, especially when:
467
+
468
+ - Applying tokens via `style` or `className`
469
+ - Using small text sizes
470
+ - Displaying important information
471
+
472
+ ### Don't Rely on Color Alone
473
+
474
+ Sentiment should not be conveyed through colour alone. Always include:
475
+
476
+ ```tsx
477
+ // ✅ Good - includes text and icons
478
+ <SentimentSurface sentiment="negative">
479
+ <ErrorIcon aria-hidden="true" />
480
+ <strong>Error:</strong> Payment failed
481
+ </SentimentSurface>
482
+
483
+ // ❌ Bad - relies only on colour
484
+ <SentimentSurface sentiment="negative">
485
+ Payment failed
486
+ </SentimentSurface>
487
+ ```
488
+
489
+ ### Focus Management
490
+
491
+ When using for alerts, consider focus management:
492
+
493
+ ```tsx
494
+ function ErrorBanner({ error }) {
495
+ const errorRef = useRef<HTMLElement>(null);
496
+
497
+ useEffect(() => {
498
+ if (error && errorRef.current) {
499
+ errorRef.current.focus();
500
+ }
501
+ }, [error]);
502
+
503
+ if (!error) return null;
504
+
505
+ return (
506
+ <SentimentSurface
507
+ ref={errorRef}
508
+ sentiment="negative"
509
+ as="section"
510
+ role="alert"
511
+ tabIndex={-1}
512
+ aria-live="assertive"
513
+ >
514
+ {error.message}
515
+ </SentimentSurface>
516
+ );
517
+ }
518
+ ```
519
+
520
+ ---
521
+
522
+ ## Best Practices
523
+
524
+ ### Do's
525
+
526
+ ✅ Use semantic HTML elements via the `as` prop
527
+ ✅ Include ARIA attributes for screen readers
528
+ ✅ Combine colour with icons and text for clarity
529
+ ✅ Use appropriate sentiment types for the content
530
+ ✅ Leverage CSS tokens for consistent theming
531
+ ✅ Test contrast ratios for accessibility
532
+ ✅ Provide focus management for critical alerts
533
+
534
+ ### Don'ts
535
+
536
+ ❌ Don't rely solely on colour to convey meaning
537
+ ❌ Don't nest too many levels (3+ levels can be confusing)
538
+ ❌ Don't override sentiment tokens without good reason
539
+ ❌ Don't use `sentiment="negative"` for informational content
540
+ ❌ Don't forget to test with screen readers
541
+
542
+ ---
543
+
544
+ ## Further Reading
545
+
546
+ - [Component Source Code](./SentimentSurface.tsx)
547
+ - [Storybook Examples](?path=/docs/content-sentimentsurface--docs)
548
+ - [WCAG Color Contrast Guidelines](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html)
549
+ - [ARIA Live Regions](https://www.w3.org/WAI/WCAG21/Understanding/status-messages.html)