@transferwise/components 0.0.0-experimental-f727c49 → 0.0.0-experimental-854d710

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 (125) hide show
  1. package/build/common/hooks/useContainerSize.js +30 -0
  2. package/build/common/hooks/useContainerSize.js.map +1 -0
  3. package/build/common/hooks/useContainerSize.mjs +28 -0
  4. package/build/common/hooks/useContainerSize.mjs.map +1 -0
  5. package/build/common/hooks/useResizeObserver.js +3 -3
  6. package/build/common/hooks/useResizeObserver.js.map +1 -1
  7. package/build/common/hooks/useResizeObserver.mjs +3 -3
  8. package/build/common/hooks/useResizeObserver.mjs.map +1 -1
  9. package/build/criticalBanner/CriticalCommsBanner.js +3 -0
  10. package/build/criticalBanner/CriticalCommsBanner.js.map +1 -1
  11. package/build/criticalBanner/CriticalCommsBanner.mjs +3 -0
  12. package/build/criticalBanner/CriticalCommsBanner.mjs.map +1 -1
  13. package/build/field/Field.js +3 -2
  14. package/build/field/Field.js.map +1 -1
  15. package/build/field/Field.mjs +3 -2
  16. package/build/field/Field.mjs.map +1 -1
  17. package/build/i18n/en.json +2 -0
  18. package/build/i18n/en.json.js +2 -0
  19. package/build/i18n/en.json.js.map +1 -1
  20. package/build/i18n/en.json.mjs +2 -0
  21. package/build/i18n/en.json.mjs.map +1 -1
  22. package/build/index.js +2 -0
  23. package/build/index.js.map +1 -1
  24. package/build/index.mjs +1 -0
  25. package/build/index.mjs.map +1 -1
  26. package/build/listItem/Prompt/ListItemPrompt.js +3 -2
  27. package/build/listItem/Prompt/ListItemPrompt.js.map +1 -1
  28. package/build/listItem/Prompt/ListItemPrompt.mjs +3 -2
  29. package/build/listItem/Prompt/ListItemPrompt.mjs.map +1 -1
  30. package/build/main.css +194 -29
  31. package/build/prompt/ActionPrompt/ActionPrompt.js +8 -40
  32. package/build/prompt/ActionPrompt/ActionPrompt.js.map +1 -1
  33. package/build/prompt/ActionPrompt/ActionPrompt.mjs +8 -40
  34. package/build/prompt/ActionPrompt/ActionPrompt.mjs.map +1 -1
  35. package/build/prompt/CriticalBanner/CriticalBanner.js +143 -0
  36. package/build/prompt/CriticalBanner/CriticalBanner.js.map +1 -0
  37. package/build/prompt/CriticalBanner/CriticalBanner.mjs +141 -0
  38. package/build/prompt/CriticalBanner/CriticalBanner.mjs.map +1 -0
  39. package/build/prompt/CriticalBanner/helpers.js +29 -0
  40. package/build/prompt/CriticalBanner/helpers.js.map +1 -0
  41. package/build/prompt/CriticalBanner/helpers.mjs +26 -0
  42. package/build/prompt/CriticalBanner/helpers.mjs.map +1 -0
  43. package/build/prompt/PrimitivePrompt/PrimitivePrompt.js +11 -4
  44. package/build/prompt/PrimitivePrompt/PrimitivePrompt.js.map +1 -1
  45. package/build/prompt/PrimitivePrompt/PrimitivePrompt.mjs +11 -4
  46. package/build/prompt/PrimitivePrompt/PrimitivePrompt.mjs.map +1 -1
  47. package/build/prompt/common/Expander/Expander.js +35 -0
  48. package/build/prompt/common/Expander/Expander.js.map +1 -0
  49. package/build/prompt/common/Expander/Expander.messages.js +17 -0
  50. package/build/prompt/common/Expander/Expander.messages.js.map +1 -0
  51. package/build/prompt/common/Expander/Expander.messages.mjs +13 -0
  52. package/build/prompt/common/Expander/Expander.messages.mjs.map +1 -0
  53. package/build/prompt/common/Expander/Expander.mjs +33 -0
  54. package/build/prompt/common/Expander/Expander.mjs.map +1 -0
  55. package/build/prompt/helpers/promptMedia.js +52 -0
  56. package/build/prompt/helpers/promptMedia.js.map +1 -0
  57. package/build/prompt/helpers/promptMedia.mjs +50 -0
  58. package/build/prompt/helpers/promptMedia.mjs.map +1 -0
  59. package/build/styles/main.css +194 -29
  60. package/build/styles/prompt/CriticalBanner/CriticalBanner.css +134 -0
  61. package/build/styles/prompt/CriticalBanner/CriticalBanner.vars.css +0 -0
  62. package/build/styles/prompt/common/Expander/Expander.css +8 -0
  63. package/build/typeahead/Typeahead.js +3 -2
  64. package/build/typeahead/Typeahead.js.map +1 -1
  65. package/build/typeahead/Typeahead.mjs +3 -2
  66. package/build/typeahead/Typeahead.mjs.map +1 -1
  67. package/build/types/common/hooks/useContainerSize.d.ts +14 -0
  68. package/build/types/common/hooks/useContainerSize.d.ts.map +1 -0
  69. package/build/types/common/hooks/useResizeObserver.d.ts +1 -1
  70. package/build/types/common/hooks/useResizeObserver.d.ts.map +1 -1
  71. package/build/types/criticalBanner/CriticalCommsBanner.d.ts +3 -0
  72. package/build/types/criticalBanner/CriticalCommsBanner.d.ts.map +1 -1
  73. package/build/types/index.d.ts +2 -2
  74. package/build/types/index.d.ts.map +1 -1
  75. package/build/types/prompt/ActionPrompt/ActionPrompt.d.ts +2 -11
  76. package/build/types/prompt/ActionPrompt/ActionPrompt.d.ts.map +1 -1
  77. package/build/types/prompt/CriticalBanner/CriticalBanner.d.ts +39 -0
  78. package/build/types/prompt/CriticalBanner/CriticalBanner.d.ts.map +1 -0
  79. package/build/types/prompt/CriticalBanner/helpers.d.ts +18 -0
  80. package/build/types/prompt/CriticalBanner/helpers.d.ts.map +1 -0
  81. package/build/types/prompt/CriticalBanner/index.d.ts +3 -0
  82. package/build/types/prompt/CriticalBanner/index.d.ts.map +1 -0
  83. package/build/types/prompt/PrimitivePrompt/PrimitivePrompt.d.ts +35 -3
  84. package/build/types/prompt/PrimitivePrompt/PrimitivePrompt.d.ts.map +1 -1
  85. package/build/types/prompt/common/Expander/Expander.d.ts +20 -0
  86. package/build/types/prompt/common/Expander/Expander.d.ts.map +1 -0
  87. package/build/types/prompt/common/Expander/Expander.messages.d.ts +14 -0
  88. package/build/types/prompt/common/Expander/Expander.messages.d.ts.map +1 -0
  89. package/build/types/prompt/helpers/promptMedia.d.ts +22 -0
  90. package/build/types/prompt/helpers/promptMedia.d.ts.map +1 -0
  91. package/build/types/prompt/index.d.ts +2 -0
  92. package/build/types/prompt/index.d.ts.map +1 -1
  93. package/build/types/test-utils/index.d.ts +4 -0
  94. package/build/types/test-utils/index.d.ts.map +1 -1
  95. package/package.json +3 -3
  96. package/src/common/hooks/useContainerSize.test.tsx +125 -0
  97. package/src/common/hooks/useContainerSize.ts +32 -0
  98. package/src/common/hooks/useResizeObserver.ts +3 -2
  99. package/src/criticalBanner/CriticalCommsBanner.story.tsx +4 -0
  100. package/src/criticalBanner/CriticalCommsBanner.test.story.tsx +1 -1
  101. package/src/criticalBanner/CriticalCommsBanner.tsx +3 -0
  102. package/src/i18n/en.json +2 -0
  103. package/src/index.ts +2 -2
  104. package/src/main.css +194 -29
  105. package/src/main.less +3 -1
  106. package/src/prompt/ActionPrompt/ActionPrompt.tsx +9 -62
  107. package/src/prompt/CriticalBanner/CriticalBanner.accessibility.docs.mdx +113 -0
  108. package/src/prompt/CriticalBanner/CriticalBanner.css +134 -0
  109. package/src/prompt/CriticalBanner/CriticalBanner.less +155 -0
  110. package/src/prompt/CriticalBanner/CriticalBanner.story.tsx +635 -0
  111. package/src/prompt/CriticalBanner/CriticalBanner.test.story.tsx +422 -0
  112. package/src/prompt/CriticalBanner/CriticalBanner.tsx +179 -0
  113. package/src/prompt/CriticalBanner/CriticalBanner.vars.css +0 -0
  114. package/src/prompt/CriticalBanner/CriticalBanner.vars.less +6 -0
  115. package/src/prompt/CriticalBanner/helpers.ts +39 -0
  116. package/src/prompt/CriticalBanner/index.ts +2 -0
  117. package/src/prompt/PrimitivePrompt/PrimitivePrompt.tsx +56 -40
  118. package/src/prompt/common/Expander/Expander.css +8 -0
  119. package/src/prompt/common/Expander/Expander.less +9 -0
  120. package/src/prompt/common/Expander/Expander.messages.ts +14 -0
  121. package/src/prompt/common/Expander/Expander.test.tsx +167 -0
  122. package/src/prompt/common/Expander/Expander.tsx +83 -0
  123. package/src/prompt/helpers/promptMedia.tsx +79 -0
  124. package/src/prompt/index.ts +4 -0
  125. package/src/sentimentSurface/SentimentSurface.story.tsx +43 -17
@@ -0,0 +1,125 @@
1
+ import React, { act } from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+
4
+ import { mockResizeObserver } from '../../test-utils/window-mock';
5
+
6
+ import { useContainerSize } from './useContainerSize';
7
+
8
+ mockResizeObserver();
9
+
10
+ describe('useContainerSize', () => {
11
+ let resizeObserverCallback: ResizeObserverCallback | null = null;
12
+ const OriginalResizeObserver = global.ResizeObserver;
13
+
14
+ beforeEach(() => {
15
+ resizeObserverCallback = null;
16
+
17
+ // Capture the ResizeObserver callback for testing
18
+ global.ResizeObserver = jest.fn().mockImplementation((callback: ResizeObserverCallback) => {
19
+ resizeObserverCallback = callback;
20
+ const instance = new OriginalResizeObserver(callback);
21
+ return instance;
22
+ }) as unknown as typeof ResizeObserver;
23
+ });
24
+
25
+ afterEach(() => {
26
+ global.ResizeObserver = OriginalResizeObserver;
27
+ });
28
+
29
+ // Helper to create a test component
30
+ function TestComponent({
31
+ breakpoint,
32
+ onStateChange,
33
+ }: {
34
+ breakpoint: number;
35
+ onStateChange: (state: boolean) => void;
36
+ }) {
37
+ const [ref, isAboveBreakpoint] = useContainerSize(breakpoint);
38
+
39
+ // Call the callback whenever state changes
40
+ React.useEffect(() => {
41
+ onStateChange(isAboveBreakpoint);
42
+ }, [isAboveBreakpoint, onStateChange]);
43
+
44
+ return (
45
+ <div ref={ref} data-testid="container">
46
+ Test
47
+ </div>
48
+ );
49
+ }
50
+
51
+ // Helper to simulate container resize
52
+ function simulateResize(width: number, options?: { noBorderBoxSize?: boolean }) {
53
+ const container = screen.getByTestId('container');
54
+
55
+ act(() => {
56
+ if (resizeObserverCallback) {
57
+ const entry = {
58
+ target: container,
59
+ borderBoxSize: options?.noBorderBoxSize
60
+ ? undefined
61
+ : [{ inlineSize: width, blockSize: 100 }],
62
+ contentRect: {
63
+ width,
64
+ height: 100,
65
+ x: 0,
66
+ y: 0,
67
+ top: 0,
68
+ right: width,
69
+ bottom: 100,
70
+ left: 0,
71
+ toJSON: () => ({}),
72
+ },
73
+ contentBoxSize: [{ inlineSize: width, blockSize: 100 }],
74
+ devicePixelContentBoxSize: [{ inlineSize: width, blockSize: 100 }],
75
+ } as ResizeObserverEntry;
76
+
77
+ resizeObserverCallback([entry], {} as ResizeObserver);
78
+ }
79
+ });
80
+ }
81
+
82
+ it('returns initial state as false', () => {
83
+ const onStateChange = jest.fn();
84
+ render(<TestComponent breakpoint={480} onStateChange={onStateChange} />);
85
+
86
+ // Initial call with false
87
+ expect(onStateChange).toHaveBeenCalledWith(false);
88
+ });
89
+
90
+ it('updates to true when container width is above breakpoint', () => {
91
+ const onStateChange = jest.fn();
92
+ render(<TestComponent breakpoint={480} onStateChange={onStateChange} />);
93
+
94
+ simulateResize(600);
95
+ expect(onStateChange).toHaveBeenCalledWith(true);
96
+ });
97
+
98
+ it('remains false when container width is below breakpoint', () => {
99
+ const onStateChange = jest.fn();
100
+ render(<TestComponent breakpoint={480} onStateChange={onStateChange} />);
101
+
102
+ simulateResize(400);
103
+ expect(onStateChange).toHaveBeenCalledWith(false);
104
+ expect(onStateChange).not.toHaveBeenCalledWith(true);
105
+ });
106
+
107
+ it('returns true when container width equals breakpoint', () => {
108
+ const onStateChange = jest.fn();
109
+ render(<TestComponent breakpoint={480} onStateChange={onStateChange} />);
110
+
111
+ simulateResize(480);
112
+ expect(onStateChange).toHaveBeenCalledWith(true);
113
+ });
114
+
115
+ it('updates state when container crosses breakpoint from below to above', () => {
116
+ const onStateChange = jest.fn();
117
+ render(<TestComponent breakpoint={480} onStateChange={onStateChange} />);
118
+
119
+ simulateResize(400);
120
+ expect(onStateChange).toHaveBeenCalledWith(false);
121
+
122
+ simulateResize(600);
123
+ expect(onStateChange).toHaveBeenCalledWith(true);
124
+ });
125
+ });
@@ -0,0 +1,32 @@
1
+ import { useRef, useState } from 'react';
2
+
3
+ import { useResizeObserver } from './useResizeObserver';
4
+
5
+ /**
6
+ * Hook that returns a ref and a boolean indicating if the container
7
+ * has a width >= the specified breakpoint.
8
+ *
9
+ * Useful for synchronizing React state with CSS container queries.
10
+ *
11
+ * Uses content-box measurements to match CSS container query behavior
12
+ * (excludes padding and border).
13
+ *
14
+ * @param breakpoint - Minimum width in pixels
15
+ * @returns [ref, isAboveBreakpoint] tuple
16
+ */
17
+ export function useContainerSize(breakpoint: number): [React.RefObject<HTMLDivElement>, boolean] {
18
+ const ref = useRef<HTMLDivElement>(null);
19
+ const [isAboveBreakpoint, setIsAboveBreakpoint] = useState(false);
20
+
21
+ useResizeObserver(
22
+ ref,
23
+ (entry) => {
24
+ // Use contentRect.width to match CSS container query box model (content-box)
25
+ const inlineSize = entry.contentRect.width;
26
+ setIsAboveBreakpoint(inlineSize >= breakpoint);
27
+ },
28
+ 'content-box',
29
+ );
30
+
31
+ return [ref, isAboveBreakpoint];
32
+ }
@@ -5,6 +5,7 @@ import { useEffectEvent } from './useEffectEvent';
5
5
  export function useResizeObserver(
6
6
  ref: React.MutableRefObject<Element | null>,
7
7
  callback: (entry: ResizeObserverEntry) => void,
8
+ box: ResizeObserverBoxOptions = 'border-box',
8
9
  ) {
9
10
  const handleCallback = useEffectEvent(callback);
10
11
  useEffect(() => {
@@ -12,11 +13,11 @@ export function useResizeObserver(
12
13
  const resizeObserver = new ResizeObserver(([entry]) => {
13
14
  handleCallback(entry);
14
15
  });
15
- resizeObserver.observe(ref.current, { box: 'border-box' });
16
+ resizeObserver.observe(ref.current, { box });
16
17
  return () => {
17
18
  resizeObserver.disconnect();
18
19
  };
19
20
  }
20
21
  return () => {};
21
- }, [handleCallback, ref]);
22
+ }, [handleCallback, ref, box]);
22
23
  }
@@ -1,9 +1,13 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import CriticalCommsBanner from '.';
3
3
 
4
+ /**
5
+ * @deprecated Use **`CriticalBanner`** component instead.
6
+ */
4
7
  export default {
5
8
  component: CriticalCommsBanner,
6
9
  title: 'Prompts/CriticalCommsBanner',
10
+ tags: ['deprecated'],
7
11
  } satisfies Meta<typeof CriticalCommsBanner>;
8
12
 
9
13
  type Story = StoryObj<typeof CriticalCommsBanner>;
@@ -6,7 +6,7 @@ import { withVariantConfig } from '../../.storybook/helpers';
6
6
  export default {
7
7
  component: CriticalCommsBanner,
8
8
  title: 'Prompts/CriticalCommsBanner/Tests',
9
- tags: ['!autodocs'],
9
+ tags: ['!autodocs', 'deprecated'],
10
10
  } satisfies Meta<typeof CriticalCommsBanner>;
11
11
 
12
12
  type Story = StoryObj<typeof CriticalCommsBanner>;
@@ -51,6 +51,9 @@ const iconBySentiment: Record<CriticalCommsBannerSentiment, React.ReactNode> = {
51
51
  ),
52
52
  };
53
53
 
54
+ /**
55
+ * @deprecated `CriticalCommsBanner` component is deprecated and now replaced by the `CriticalBanner` component.
56
+ */
54
57
  function CriticalCommsBanner({
55
58
  title,
56
59
  subtitle,
package/src/i18n/en.json CHANGED
@@ -16,6 +16,8 @@
16
16
  "neptune.DateLookup.selected": "selected",
17
17
  "neptune.DateLookup.twentyYears": "20 years",
18
18
  "neptune.DateLookup.year": "year",
19
+ "neptune.Expander.collapseAriaLabel": "Collapse",
20
+ "neptune.Expander.expandAriaLabel": "Expand",
19
21
  "neptune.ExpressiveMoneyInput.currency.search.placeholder": "Type a currency / country",
20
22
  "neptune.ExpressiveMoneyInput.currency.select.currency": "Select currency",
21
23
  "neptune.FlowNavigation.back": "back to previous step",
package/src/index.ts CHANGED
@@ -36,7 +36,7 @@ export type { HeaderProps } from './header';
36
36
  export type { EmphasisProps } from './emphasis';
37
37
  export type { FieldProps } from './field/Field';
38
38
  export type { InfoProps } from './info';
39
- export type { InlinePromptProps, ActionPromptProps } from './prompt';
39
+ export type { InlinePromptProps, ActionPromptProps, CriticalBannerProps } from './prompt';
40
40
  export type { InfoPromptProps, InfoPromptAction, InfoPromptMedia } from './prompt';
41
41
  export type { InputWithDisplayFormatProps } from './inputWithDisplayFormat';
42
42
  export type { InputProps } from './inputs/Input';
@@ -175,7 +175,7 @@ export { default as Header } from './header';
175
175
  export { default as Image } from './image';
176
176
  export { default as Info } from './info';
177
177
  export { default as InlineAlert } from './inlineAlert';
178
- export { InfoPrompt, InlinePrompt, ActionPrompt } from './prompt';
178
+ export { InfoPrompt, InlinePrompt, ActionPrompt, CriticalBanner } from './prompt';
179
179
  export { default as InputWithDisplayFormat } from './inputWithDisplayFormat';
180
180
  export { Input } from './inputs/Input';
181
181
  export { InputGroup } from './inputs/InputGroup';
package/src/main.css CHANGED
@@ -32235,6 +32235,200 @@ html:not([dir="rtl"]) .np-navigation-option {
32235
32235
  }
32236
32236
  }
32237
32237
 
32238
+ .wds-action-prompt {
32239
+ --Prompt-padding: var(--size-16);
32240
+ --Prompt-actions-gap: var(--size-8);
32241
+ --Prompt-gap: var(--size-10) var(--size-16);
32242
+ --Prompt-border-radius: var(--radius-large);
32243
+ }
32244
+
32245
+ .wds-action-prompt--media-image {
32246
+ width: 48px;
32247
+ width: var(--size-48);
32248
+ height: 48px;
32249
+ height: var(--size-48);
32250
+ -o-object-fit: contain;
32251
+ object-fit: contain;
32252
+ }
32253
+
32254
+ @media (max-width: 767px) {
32255
+ .wds-action-prompt--with-two-actions .wds-prompt__actions-wrapper .wds-Button {
32256
+ flex: 1 1 calc(50% - (var(--Prompt-actions-gap) / 2));
32257
+ min-width: -moz-fit-content;
32258
+ min-width: fit-content;
32259
+ }
32260
+ }
32261
+
32262
+ .wds-action-prompt__content {
32263
+ max-width: calc(48px * 10);
32264
+ max-width: calc(var(--size-48) * 10);
32265
+ }
32266
+
32267
+ .wds-critical-banner {
32268
+ --Prompt-padding: var(--size-16);
32269
+ --Prompt-actions-gap: var(--size-8);
32270
+ --Prompt-gap: var(--size-10) var(--size-16);
32271
+ --Prompt-border-radius: 0;
32272
+ container-type: inline-size;
32273
+ --critical-banner-easing: cubic-bezier(0.9, 0, 0.7, 1);
32274
+ --critical-banner-duration: 150ms;
32275
+ /* Override PrimitivePrompt's --screen-sm-max actions behaviour:
32276
+ actions only go full-width below mobile-max token (container query) */
32277
+ }
32278
+
32279
+ .wds-critical-banner--media-image {
32280
+ width: 48px;
32281
+ width: var(--size-48);
32282
+ height: 48px;
32283
+ height: var(--size-48);
32284
+ -o-object-fit: contain;
32285
+ object-fit: contain;
32286
+ }
32287
+
32288
+ .wds-critical-banner__title {
32289
+ display: -webkit-box;
32290
+ -webkit-line-clamp: 8;
32291
+ line-clamp: 8;
32292
+ -webkit-box-orient: vertical;
32293
+ max-height: 192px;
32294
+ overflow: hidden;
32295
+ transition: max-height var(--critical-banner-duration) var(--critical-banner-easing);
32296
+ }
32297
+
32298
+ .wds-critical-banner--collapsed .wds-critical-banner__title {
32299
+ -webkit-line-clamp: 2;
32300
+ line-clamp: 2;
32301
+ max-height: 48px;
32302
+ }
32303
+
32304
+ .wds-critical-banner__description {
32305
+ max-height: 30vh;
32306
+ opacity: 1;
32307
+ overflow: hidden;
32308
+ transition: max-height var(--critical-banner-duration) var(--critical-banner-easing), opacity var(--critical-banner-duration) var(--critical-banner-easing);
32309
+ }
32310
+
32311
+ .wds-critical-banner--collapsed .wds-critical-banner__description--with-title {
32312
+ max-height: 0;
32313
+ opacity: 0;
32314
+ }
32315
+
32316
+ .wds-critical-banner--collapsed .wds-critical-banner__description:not(.wds-critical-banner__description--with-title) {
32317
+ display: -webkit-box;
32318
+ -webkit-line-clamp: 2;
32319
+ line-clamp: 2;
32320
+ -webkit-box-orient: vertical;
32321
+ overflow: hidden;
32322
+ }
32323
+
32324
+ .wds-critical-banner .wds-prompt__content-wrapper {
32325
+ grid-template-columns: auto 1fr auto;
32326
+ align-items: flex-start;
32327
+ transition: row-gap var(--critical-banner-duration) var(--critical-banner-easing);
32328
+ }
32329
+
32330
+ .wds-critical-banner--collapsed .wds-prompt__content-wrapper {
32331
+ row-gap: 0;
32332
+ }
32333
+
32334
+ .wds-critical-banner__text-wrapper {
32335
+ position: relative;
32336
+ display: flex;
32337
+ flex-direction: column;
32338
+ justify-content: center;
32339
+ align-items: flex-start;
32340
+ text-align: start;
32341
+ min-width: 0;
32342
+ max-width: 480px;
32343
+ height: 100%;
32344
+ padding-top: 3px;
32345
+ }
32346
+
32347
+ @media (max-width: 320px) {
32348
+ .wds-critical-banner__text-wrapper {
32349
+ padding-top: 0;
32350
+ }
32351
+ }
32352
+
32353
+ @container (max-width: 320px) {
32354
+ .wds-critical-banner__text-wrapper {
32355
+ padding-top: 0;
32356
+ }
32357
+ }
32358
+
32359
+ .wds-critical-banner--collapsed .wds-critical-banner__title,
32360
+ .wds-critical-banner--collapsed .wds-critical-banner__description {
32361
+ transform: translateY(0);
32362
+ }
32363
+
32364
+ .wds-critical-banner__toggle {
32365
+ flex-shrink: 0;
32366
+ transition: transform var(--critical-banner-duration) var(--critical-banner-easing);
32367
+ }
32368
+
32369
+ .wds-critical-banner__toggle--collapsed {
32370
+ transform: rotate(180deg);
32371
+ }
32372
+
32373
+ @container (min-width: 768px) {
32374
+ .wds-critical-banner__toggle {
32375
+ display: none;
32376
+ }
32377
+ }
32378
+
32379
+ .wds-critical-banner .wds-prompt__actions-wrapper {
32380
+ grid-column-start: 2;
32381
+ grid-column-end: 3;
32382
+ max-height: 200px;
32383
+ opacity: 1;
32384
+ overflow: visible;
32385
+ }
32386
+
32387
+ @container (max-width: 600px) {
32388
+ .wds-critical-banner .wds-prompt__actions-wrapper {
32389
+ grid-column: span 3;
32390
+ width: 100%;
32391
+ }
32392
+ .wds-critical-banner .wds-prompt__actions-wrapper .wds-Button {
32393
+ flex: 1 1 100%;
32394
+ width: 100%;
32395
+ }
32396
+ }
32397
+
32398
+ .wds-critical-banner--collapsed .wds-prompt__actions-wrapper {
32399
+ max-height: 0;
32400
+ opacity: 0;
32401
+ overflow: hidden;
32402
+ pointer-events: none;
32403
+ }
32404
+
32405
+ @container (max-width: 600px) {
32406
+ .wds-critical-banner--with-two-actions .wds-prompt__actions-wrapper .wds-Button {
32407
+ flex: 1 1 calc(50% - (var(--Prompt-actions-gap) / 2));
32408
+ min-width: -moz-fit-content;
32409
+ min-width: fit-content;
32410
+ }
32411
+ }
32412
+
32413
+ @media (prefers-reduced-motion: reduce) {
32414
+ .wds-critical-banner__description,
32415
+ .wds-critical-banner__title,
32416
+ .wds-critical-banner .wds-prompt__content-wrapper,
32417
+ .wds-critical-banner__toggle {
32418
+ transition: none !important;
32419
+ }
32420
+ }
32421
+
32422
+ .wds-expander-toggle {
32423
+ align-self: flex-start;
32424
+ flex-shrink: 0;
32425
+ transition: transform 0.2s ease-in-out;
32426
+ }
32427
+
32428
+ .wds-expander-toggle--collapsed {
32429
+ transform: rotate(180deg);
32430
+ }
32431
+
32238
32432
  .wds-radio-group .np-radio:last-child label {
32239
32433
  margin-bottom: 0;
32240
32434
  }
@@ -34660,32 +34854,3 @@ html:not([dir="rtl"]) .np-navigation-option {
34660
34854
  .np-list-item__action {
34661
34855
  flex-shrink: 0;
34662
34856
  }
34663
-
34664
- .wds-action-prompt {
34665
- --Prompt-padding: var(--size-16);
34666
- --Prompt-actions-gap: var(--size-8);
34667
- --Prompt-gap: var(--size-10) var(--size-16);
34668
- --Prompt-border-radius: var(--radius-large);
34669
- }
34670
-
34671
- .wds-action-prompt--media-image {
34672
- width: 48px;
34673
- width: var(--size-48);
34674
- height: 48px;
34675
- height: var(--size-48);
34676
- -o-object-fit: contain;
34677
- object-fit: contain;
34678
- }
34679
-
34680
- @media (max-width: 767px) {
34681
- .wds-action-prompt--with-two-actions .wds-prompt__actions-wrapper .wds-Button {
34682
- flex: 1 1 calc(50% - (var(--Prompt-actions-gap) / 2));
34683
- min-width: -moz-fit-content;
34684
- min-width: fit-content;
34685
- }
34686
- }
34687
-
34688
- .wds-action-prompt__content {
34689
- max-width: calc(48px * 10);
34690
- max-width: calc(var(--size-48) * 10);
34691
- }
package/src/main.less CHANGED
@@ -67,6 +67,9 @@
67
67
  @import "./prompt/PrimitivePrompt/PrimitivePrompt.less";
68
68
  @import "./prompt/InlinePrompt/InlinePrompt.less";
69
69
  @import "./prompt/InfoPrompt/InfoPrompt.less";
70
+ @import "./prompt/ActionPrompt/ActionPrompt.less";
71
+ @import "./prompt/CriticalBanner/CriticalBanner.less";
72
+ @import "./prompt/common/Expander/Expander.less";
70
73
  @import "./radioGroup/RadioGroup.less";
71
74
  @import "./section/Section.less";
72
75
  @import "./slidingPanel/SlidingPanel.less";
@@ -91,7 +94,6 @@
91
94
  @import "./progress/Progress.less";
92
95
  @import "./progressBar/ProgressBar.less";
93
96
  @import "./legacylistItem/LegacyListItem.less";
94
- @import "./prompt/ActionPrompt/ActionPrompt.less";
95
97
 
96
98
  // List all less files in src in alphabetical order: find -s src -type f -name '*.less' ! -name 'main.less'
97
99
  // Make sure you are not referencing main.less itself in this file!
@@ -1,31 +1,19 @@
1
1
  import { AriaAttributes, ReactNode, useId } from 'react';
2
2
  import { clsx } from 'clsx';
3
3
 
4
- import StatusIcon from '../../statusIcon';
5
4
  import Body from '../../body';
6
5
  import Button from '../../button';
7
6
  import { Breakpoint, Typography } from '../../common';
8
- import AvatarView, { AvatarViewProps } from '../../avatarView';
9
- import Image from '../../image';
10
7
  import { ButtonProps } from '../../button/Button.types';
11
8
  import { PrimitivePrompt, PrimitivePromptProps } from '../PrimitivePrompt';
12
- import { BadgeAssetsProps } from '../../badge';
13
- import { GiftBox } from '@transferwise/icons';
14
9
  import { useScreenSize } from '../../common/hooks/useScreenSize';
10
+ import { renderPromptMedia, PromptMedia } from '../helpers/promptMedia';
15
11
 
16
12
  export type ActionPromptProps = {
17
13
  title: ReactNode;
18
14
  description?: ReactNode;
19
15
  /** @default {} */
20
- media?: {
21
- imgSrc?: string;
22
- avatar?: Pick<AvatarViewProps, 'imgSrc' | 'profileName' | 'profileType'> & {
23
- asset?: AvatarViewProps['children'];
24
- badge?: Pick<BadgeAssetsProps, 'flagCode'>;
25
- };
26
- 'aria-label'?: string;
27
- 'aria-hidden'?: boolean;
28
- };
16
+ media?: PromptMedia;
29
17
  action: Pick<ButtonProps, 'onClick' | 'href' | 'target'> & {
30
18
  label: ButtonProps['children'];
31
19
  };
@@ -74,54 +62,13 @@ export const ActionPrompt = ({
74
62
  .filter(Boolean)
75
63
  .join(' ');
76
64
 
77
- const renderMedia = () => {
78
- if (media?.imgSrc) {
79
- return (
80
- <Image
81
- id={mediaId}
82
- src={media.imgSrc}
83
- className="wds-action-prompt--media-image"
84
- alt={media['aria-label'] ?? ''}
85
- />
86
- );
87
- }
88
- if (media?.avatar) {
89
- const badge = media.avatar.badge
90
- ? media.avatar.badge
91
- : sentiment === 'proposition'
92
- ? {}
93
- : { status: sentiment };
94
- return (
95
- <AvatarView
96
- {...media.avatar}
97
- badge={badge}
98
- aria-label={media['aria-label']}
99
- aria-hidden={media['aria-hidden']}
100
- id={mediaId}
101
- size={48}
102
- >
103
- {media.avatar.asset}
104
- </AvatarView>
105
- );
106
- }
107
- return sentiment === 'proposition' ? (
108
- <AvatarView
109
- id={mediaId}
110
- size={48}
111
- aria-label={media['aria-label']}
112
- aria-hidden={media['aria-hidden']}
113
- >
114
- <GiftBox />
115
- </AvatarView>
116
- ) : (
117
- <StatusIcon
118
- id={mediaId}
119
- size={48}
120
- sentiment={sentiment}
121
- iconLabel={media['aria-hidden'] ? null : media['aria-label']}
122
- />
123
- );
124
- };
65
+ const renderMedia = () =>
66
+ renderPromptMedia({
67
+ media,
68
+ sentiment,
69
+ mediaId,
70
+ imgClassName: 'wds-action-prompt--media-image',
71
+ });
125
72
 
126
73
  return (
127
74
  <PrimitivePrompt