@transferwise/components 46.160.0 → 46.160.2

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 (183) hide show
  1. package/build/Calendar/Calendar.js +9 -3
  2. package/build/Calendar/Calendar.js.map +1 -1
  3. package/build/Calendar/Calendar.mjs +9 -3
  4. package/build/Calendar/Calendar.mjs.map +1 -1
  5. package/build/Carousel/Carousel.js +17 -10
  6. package/build/Carousel/Carousel.js.map +1 -1
  7. package/build/Carousel/Carousel.mjs +17 -10
  8. package/build/Carousel/Carousel.mjs.map +1 -1
  9. package/build/ExpressiveMoneyInput/Chevron/Chevron.js +10 -2
  10. package/build/ExpressiveMoneyInput/Chevron/Chevron.js.map +1 -1
  11. package/build/ExpressiveMoneyInput/Chevron/Chevron.mjs +11 -3
  12. package/build/ExpressiveMoneyInput/Chevron/Chevron.mjs.map +1 -1
  13. package/build/ProcessIndicator/ProcessIndicator.js +1 -1
  14. package/build/ProcessIndicator/ProcessIndicator.js.map +1 -1
  15. package/build/ProcessIndicator/ProcessIndicator.mjs +1 -1
  16. package/build/ProcessIndicator/ProcessIndicator.mjs.map +1 -1
  17. package/build/Prompt/CriticalBanner/CriticalBanner.js +10 -1
  18. package/build/Prompt/CriticalBanner/CriticalBanner.js.map +1 -1
  19. package/build/Prompt/CriticalBanner/CriticalBanner.mjs +10 -1
  20. package/build/Prompt/CriticalBanner/CriticalBanner.mjs.map +1 -1
  21. package/build/SegmentedControl/SegmentedControl.js +9 -2
  22. package/build/SegmentedControl/SegmentedControl.js.map +1 -1
  23. package/build/SegmentedControl/SegmentedControl.mjs +9 -2
  24. package/build/SegmentedControl/SegmentedControl.mjs.map +1 -1
  25. package/build/Tooltip/Tooltip.js +6 -1
  26. package/build/Tooltip/Tooltip.js.map +1 -1
  27. package/build/Tooltip/Tooltip.mjs +6 -1
  28. package/build/Tooltip/Tooltip.mjs.map +1 -1
  29. package/build/common/Panel/Panel.js +13 -25
  30. package/build/common/Panel/Panel.js.map +1 -1
  31. package/build/common/Panel/Panel.mjs +10 -22
  32. package/build/common/Panel/Panel.mjs.map +1 -1
  33. package/build/common/Panel/Panel.positionUtils.js +68 -0
  34. package/build/common/Panel/Panel.positionUtils.js.map +1 -0
  35. package/build/common/Panel/Panel.positionUtils.mjs +63 -0
  36. package/build/common/Panel/Panel.positionUtils.mjs.map +1 -0
  37. package/build/common/PropsValues/position.js +0 -25
  38. package/build/common/PropsValues/position.js.map +1 -1
  39. package/build/common/PropsValues/position.mjs +1 -24
  40. package/build/common/PropsValues/position.mjs.map +1 -1
  41. package/build/i18n/tr.json +5 -0
  42. package/build/i18n/tr.json.js +5 -0
  43. package/build/i18n/tr.json.js.map +1 -1
  44. package/build/i18n/tr.json.mjs +5 -0
  45. package/build/i18n/tr.json.mjs.map +1 -1
  46. package/build/i18n/zh-CN.json +5 -0
  47. package/build/i18n/zh-CN.json.js +5 -0
  48. package/build/i18n/zh-CN.json.js.map +1 -1
  49. package/build/i18n/zh-CN.json.mjs +5 -0
  50. package/build/i18n/zh-CN.json.mjs.map +1 -1
  51. package/build/main.css +134 -88
  52. package/build/styles/AvatarLayout/AvatarLayout.css +26 -9
  53. package/build/styles/AvatarView/AvatarView.css +8 -0
  54. package/build/styles/AvatarView/Dot/Dot.css +8 -0
  55. package/build/styles/CheckboxButton/CheckboxButton.css +3 -0
  56. package/build/styles/Nudge/Nudge.css +4 -38
  57. package/build/styles/Prompt/CriticalBanner/CriticalBanner.css +34 -0
  58. package/build/styles/SegmentedControl/SegmentedControl.css +2 -1
  59. package/build/styles/SlidingPanel/SlidingPanel.css +8 -0
  60. package/build/styles/Tooltip/Tooltip.css +0 -6
  61. package/build/styles/UploadInput/UploadItem/UploadItem.css +4 -5
  62. package/build/styles/_deprecated/Badge/Badge.css +4 -2
  63. package/build/styles/common/Panel/Panel.css +16 -8
  64. package/build/styles/css/accordion.css +3 -0
  65. package/build/styles/css/buttons.css +1 -7
  66. package/build/styles/css/chevron.css +3 -0
  67. package/build/styles/css/neptune.css +25 -19
  68. package/build/styles/css/process.css +15 -9
  69. package/build/styles/css/tick.css +6 -3
  70. package/build/styles/main.css +134 -88
  71. package/build/styles/styles/less/accordion.css +3 -0
  72. package/build/styles/styles/less/buttons.css +1 -7
  73. package/build/styles/styles/less/chevron.css +3 -0
  74. package/build/styles/styles/less/neptune.css +25 -19
  75. package/build/styles/styles/less/process.css +15 -9
  76. package/build/styles/styles/less/tick.css +6 -3
  77. package/build/types/Calendar/Calendar.d.ts.map +1 -1
  78. package/build/types/Carousel/Carousel.d.ts.map +1 -1
  79. package/build/types/Carousel/_stories/constants.d.ts +3 -0
  80. package/build/types/Carousel/_stories/constants.d.ts.map +1 -0
  81. package/build/types/ExpressiveMoneyInput/Chevron/Chevron.d.ts.map +1 -1
  82. package/build/types/Prompt/CriticalBanner/CriticalBanner.d.ts +6 -1
  83. package/build/types/Prompt/CriticalBanner/CriticalBanner.d.ts.map +1 -1
  84. package/build/types/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  85. package/build/types/Tooltip/Tooltip.d.ts.map +1 -1
  86. package/build/types/common/Panel/Panel.d.ts +1 -1
  87. package/build/types/common/Panel/Panel.d.ts.map +1 -1
  88. package/build/types/common/Panel/Panel.positionUtils.d.ts +7 -0
  89. package/build/types/common/Panel/Panel.positionUtils.d.ts.map +1 -0
  90. package/build/types/common/PropsValues/position.d.ts +0 -13
  91. package/build/types/common/PropsValues/position.d.ts.map +1 -1
  92. package/build/types/common/ResponsivePanel/ResponsivePanel.d.ts +1 -1
  93. package/package.json +8 -8
  94. package/src/AvatarLayout/AvatarLayout.css +26 -9
  95. package/src/AvatarLayout/AvatarLayout.less +46 -22
  96. package/src/AvatarLayout/_stories/AvatarLayout.test.story.tsx +17 -42
  97. package/src/AvatarView/AvatarView.css +8 -0
  98. package/src/AvatarView/Dot/Dot.css +8 -0
  99. package/src/AvatarView/Dot/Dot.less +7 -1
  100. package/src/Button/_stories/Button.test.story.tsx +1 -1
  101. package/src/Calendar/Calendar.test.tsx +82 -0
  102. package/src/Calendar/Calendar.tsx +6 -3
  103. package/src/Carousel/Carousel.test.tsx +50 -2
  104. package/src/Carousel/Carousel.tsx +13 -10
  105. package/src/Carousel/_stories/Carousel.story.tsx +12 -52
  106. package/src/Carousel/_stories/Carousel.test.story.tsx +7 -43
  107. package/src/Carousel/_stories/constants.tsx +46 -0
  108. package/src/Checkbox/_stories/Checkbox.story.tsx +1 -1
  109. package/src/Checkbox/_stories/Checkbox.test.story.tsx +3 -2
  110. package/src/CheckboxButton/CheckboxButton.css +3 -0
  111. package/src/CheckboxButton/CheckboxButton.less +4 -0
  112. package/src/Chevron/_stories/Chevron.test.story.tsx +2 -2
  113. package/src/DateLookup/_stories/DateLookup.rtl.test.story.tsx +59 -48
  114. package/src/ExpressiveMoneyInput/Chevron/Chevron.tsx +8 -3
  115. package/src/Field/_stories/Field.test.story.tsx +48 -37
  116. package/src/FlowNavigation/_stories/FlowNavigation.rtl.test.story.tsx +78 -0
  117. package/src/FlowNavigation/_stories/FlowNavigation.test.story.tsx +0 -44
  118. package/src/Header/_stories/Header.test.story.tsx +1 -1
  119. package/src/Info/_stories/Info.rtl.test.story.tsx +35 -10
  120. package/src/Info/_stories/Info.story.tsx +1 -1
  121. package/src/Inputs/SelectInput/_stories/SelectInput.test.story.tsx +1 -1
  122. package/src/Label/_stories/Label.test.story.tsx +35 -22
  123. package/src/ListItem/_stories/subcomponents.tsx +1 -1
  124. package/src/Loader/_stories/Loader.test.story.tsx +23 -0
  125. package/src/MoneyInput/_stories/MoneyInput.rtl.test.story.tsx +1 -1
  126. package/src/Nudge/Nudge.css +4 -38
  127. package/src/Nudge/Nudge.less +4 -4
  128. package/src/Nudge/_stories/Nudge.test.story.tsx +29 -12
  129. package/src/PhoneNumberInput/_stories/{PhoneNumberInput.test.story.tsx → PhoneNumberInput.rtl.test.story.tsx} +18 -10
  130. package/src/Popover/_stories/Popover.rtl.test.story.tsx +40 -50
  131. package/src/ProcessIndicator/ProcessIndicator.tsx +1 -1
  132. package/src/ProcessIndicator/_stories/ProcessIndicator.test.story.tsx +32 -0
  133. package/src/ProgressBar/_stories/ProgressBar.test.story.tsx +33 -19
  134. package/src/Prompt/CriticalBanner/CriticalBanner.css +34 -0
  135. package/src/Prompt/CriticalBanner/CriticalBanner.less +45 -0
  136. package/src/Prompt/CriticalBanner/CriticalBanner.test.tsx +26 -3
  137. package/src/Prompt/CriticalBanner/CriticalBanner.tsx +15 -1
  138. package/src/Prompt/CriticalBanner/_stories/CriticalBanner.accessibility.docs.mdx +4 -1
  139. package/src/Prompt/CriticalBanner/_stories/CriticalBanner.story.tsx +52 -0
  140. package/src/Prompt/CriticalBanner/_stories/CriticalBanner.test.story.tsx +22 -1
  141. package/src/SegmentedControl/SegmentedControl.css +2 -1
  142. package/src/SegmentedControl/SegmentedControl.less +4 -2
  143. package/src/SegmentedControl/SegmentedControl.tsx +8 -2
  144. package/src/SegmentedControl/_stories/SegmentedControl.test.story.tsx +16 -8
  145. package/src/SlidingPanel/SlidingPanel.css +8 -0
  146. package/src/SlidingPanel/SlidingPanel.less +8 -0
  147. package/src/SlidingPanel/_stories/SlidingPanel.rtl.test.story.tsx +4 -3
  148. package/src/Snackbar/_stories/Snackbar.rtl.test.story.tsx +14 -16
  149. package/src/Snackbar/_stories/Snackbar.story.tsx +1 -1
  150. package/src/Snackbar/_stories/Snackbar.test.story.tsx +1 -1
  151. package/src/StatusIcon/_stories/StatusIcon.test.story.tsx +12 -4
  152. package/src/Tooltip/Tooltip.css +0 -6
  153. package/src/Tooltip/Tooltip.less +0 -12
  154. package/src/Tooltip/Tooltip.test.tsx +51 -0
  155. package/src/Tooltip/Tooltip.tsx +11 -1
  156. package/src/Tooltip/_stories/Tooltip.rtl.test.story.tsx +16 -34
  157. package/src/UploadInput/UploadItem/UploadItem.css +4 -5
  158. package/src/UploadInput/UploadItem/UploadItem.less +7 -6
  159. package/src/UploadInput/_stories/UploadInput.test.story.tsx +1 -1
  160. package/src/_deprecated/Badge/Badge.css +4 -2
  161. package/src/_deprecated/Badge/Badge.less +7 -14
  162. package/src/_deprecated/CriticalBanner/_stories/CriticalCommsBanner.test.story.tsx +0 -16
  163. package/src/_deprecated/OverlayHeader/_stories/OverlayHeader.test.story.tsx +15 -0
  164. package/src/common/Panel/Panel.css +16 -8
  165. package/src/common/Panel/Panel.less +31 -13
  166. package/src/common/Panel/Panel.positionUtils.ts +79 -0
  167. package/src/common/Panel/Panel.test.tsx +40 -0
  168. package/src/common/Panel/Panel.tsx +13 -24
  169. package/src/common/Panel/_stories/Panel.rtl.test.story.tsx +110 -0
  170. package/src/common/PropsValues/position.ts +0 -50
  171. package/src/i18n/tr.json +5 -0
  172. package/src/i18n/zh-CN.json +5 -0
  173. package/src/main.css +134 -88
  174. package/src/styles/less/accordion.css +3 -0
  175. package/src/styles/less/buttons.css +1 -7
  176. package/src/styles/less/buttons.less +3 -11
  177. package/src/styles/less/chevron.css +3 -0
  178. package/src/styles/less/chevron.less +4 -0
  179. package/src/styles/less/neptune.css +25 -19
  180. package/src/styles/less/process.css +15 -9
  181. package/src/styles/less/process.less +13 -6
  182. package/src/styles/less/tick.css +6 -3
  183. package/src/styles/less/tick.less +7 -3
@@ -41,7 +41,10 @@
41
41
  inline-size: 161px;
42
42
  }
43
43
  [dir="rtl"] .wds-nudge-media-globe {
44
- transform: scaleX(-1);
44
+ margin-inline-start: -63px;
45
+ margin-block-start: -15px;
46
+ position: absolute;
47
+ inline-size: 161px;
45
48
  }
46
49
  .wds-nudge-media-lock {
47
50
  margin-inline-start: -11px;
@@ -50,36 +53,24 @@
50
53
  inline-size: 104px;
51
54
  transform: rotate(-14.86deg);
52
55
  }
53
- [dir="rtl"] .wds-nudge-media-lock {
54
- transform: scaleX(-1);
55
- }
56
56
  .wds-nudge-media-wallet {
57
57
  margin-inline-start: -15px;
58
58
  margin-block-start: 18px;
59
59
  position: absolute;
60
60
  inline-size: 107px;
61
61
  }
62
- [dir="rtl"] .wds-nudge-media-wallet {
63
- transform: scaleX(-1);
64
- }
65
62
  .wds-nudge-media-gear {
66
63
  margin-inline-start: -18px;
67
64
  margin-block-start: 19px;
68
65
  position: absolute;
69
66
  inline-size: 111px;
70
67
  }
71
- [dir="rtl"] .wds-nudge-media-gear {
72
- transform: scaleX(-1);
73
- }
74
68
  .wds-nudge-media-invite-letter {
75
69
  margin-inline-start: -17px;
76
70
  margin-block-start: -2px;
77
71
  position: absolute;
78
72
  inline-size: 105px;
79
73
  }
80
- [dir="rtl"] .wds-nudge-media-invite-letter {
81
- transform: scaleX(-1);
82
- }
83
74
  .wds-nudge-media-personal-card,
84
75
  .wds-nudge-media-business-card {
85
76
  margin-inline-start: -15px;
@@ -87,73 +78,48 @@
87
78
  position: absolute;
88
79
  inline-size: 104px;
89
80
  }
90
- [dir="rtl"] .wds-nudge-media-personal-card,
91
- [dir="rtl"] .wds-nudge-media-business-card {
92
- transform: scaleX(-1);
93
- }
94
81
  .wds-nudge-media-heart {
95
82
  margin-inline-start: -7px;
96
83
  margin-block-start: 11px;
97
84
  position: absolute;
98
85
  inline-size: 95px;
99
86
  }
100
- [dir="rtl"] .wds-nudge-media-heart {
101
- transform: scaleX(-1);
102
- }
103
87
  .wds-nudge-media-multi-currency {
104
88
  margin-inline-start: -30px;
105
89
  margin-block-start: 1px;
106
90
  position: absolute;
107
91
  inline-size: 131px;
108
92
  }
109
- [dir="rtl"] .wds-nudge-media-multi-currency {
110
- transform: scaleX(-1);
111
- }
112
93
  .wds-nudge-media-shopping-bag {
113
94
  margin-inline-start: -9px;
114
95
  margin-block-start: 14px;
115
96
  position: absolute;
116
97
  inline-size: 116px;
117
98
  }
118
- [dir="rtl"] .wds-nudge-media-shopping-bag {
119
- transform: scaleX(-1);
120
- }
121
99
  .wds-nudge-media-flower {
122
100
  margin-inline-start: -24px;
123
101
  margin-block-start: 11px;
124
102
  position: absolute;
125
103
  inline-size: 156px;
126
104
  }
127
- [dir="rtl"] .wds-nudge-media-flower {
128
- transform: scaleX(-1);
129
- }
130
105
  .wds-nudge-media-backpack {
131
106
  margin-inline-start: -16px;
132
107
  margin-block-start: 6px;
133
108
  position: absolute;
134
109
  inline-size: 123px;
135
110
  }
136
- [dir="rtl"] .wds-nudge-media-backpack {
137
- transform: scaleX(-1);
138
- }
139
111
  .wds-nudge-media-gift-box {
140
112
  margin-inline-start: -22px;
141
113
  margin-block-start: 6px;
142
114
  position: absolute;
143
115
  inline-size: 129px;
144
116
  }
145
- [dir="rtl"] .wds-nudge-media-gift-box {
146
- transform: scaleX(-1);
147
- }
148
117
  .wds-nudge-media-documents {
149
118
  margin-inline-start: -8px;
150
119
  margin-block-start: 8px;
151
120
  position: absolute;
152
121
  inline-size: 120px;
153
122
  }
154
- [dir="rtl"] .wds-nudge-media-documents {
155
- transform: scaleX(-1);
156
- }
157
123
  .wds-nudge-container {
158
124
  align-items: stretch;
159
125
  display: flex;
@@ -32,6 +32,10 @@
32
32
 
33
33
  &-globe {
34
34
  .media-position(161px, -76px, -5px);
35
+
36
+ [dir="rtl"] & {
37
+ .media-position(161px, -63px, -15px);
38
+ }
35
39
  }
36
40
 
37
41
  &-lock {
@@ -129,8 +133,4 @@
129
133
  margin-top: @margin-top;
130
134
  position: absolute;
131
135
  width: @size;
132
-
133
- [dir="rtl"] & {
134
- transform: scaleX(-1);
135
- }
136
136
  }
@@ -10,28 +10,45 @@ export default {
10
10
 
11
11
  type Story = StoryObj<typeof Nudge>;
12
12
 
13
+ const MEDIA_NAMES = [
14
+ 'globe',
15
+ 'lock',
16
+ 'wallet',
17
+ 'gear',
18
+ 'invite-letter',
19
+ 'personal-card',
20
+ 'business-card',
21
+ 'heart',
22
+ 'multi-currency',
23
+ 'shopping-bag',
24
+ 'flower',
25
+ 'gift-box',
26
+ 'backpack',
27
+ 'documents',
28
+ ] as const;
29
+
13
30
  export const RTLDiff: Story = {
14
31
  render: () => (
15
32
  <>
33
+ {MEDIA_NAMES.map((mediaName) => (
34
+ <Nudge
35
+ key={mediaName}
36
+ mediaName={mediaName}
37
+ title={mediaName}
38
+ link="Link"
39
+ href="#"
40
+ onDismiss={() => {}}
41
+ />
42
+ ))}
16
43
  <Nudge
17
44
  mediaName="globe"
18
- title="With media and dismiss"
19
- link="Link"
20
- href="#"
21
- onDismiss={() => {}}
22
- />
23
- <div style={{ marginTop: 16 }} />
24
- <Nudge
25
- mediaName="globe"
26
- title="With media, action, and dismiss"
45
+ title="With action"
27
46
  action={{ text: 'Learn more', href: '#' }}
28
47
  onDismiss={() => {}}
29
48
  />
30
- <div style={{ marginTop: 16 }} />
31
49
  <Nudge title="Without media, with dismiss" link="Link" href="#" onDismiss={() => {}} />
32
- <div style={{ marginTop: 16 }} />
33
50
  <Nudge title="Without media, no dismiss" link="Link" href="#" />
34
51
  </>
35
52
  ),
36
- ...withRTLDiff(),
53
+ ...withRTLDiff({ width: 'stretch' }),
37
54
  };
@@ -1,4 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import { userEvent, within } from 'storybook/test';
2
3
 
3
4
  import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
4
5
  import Field from '../../Field/Field';
@@ -6,21 +7,13 @@ import PhoneNumberInput from '..';
6
7
 
7
8
  export default {
8
9
  component: PhoneNumberInput,
9
- title: 'Forms/PhoneNumberInput/Tests',
10
+ title: 'Forms/PhoneNumberInput/Tests/RTL Diff',
10
11
  tags: ['!autodocs', '!manifest'],
11
12
  } satisfies Meta<typeof PhoneNumberInput>;
12
13
 
13
14
  type Story = StoryObj<typeof PhoneNumberInput>;
14
15
 
15
- /**
16
- * The country code selector sits on the inline-start side of the phone number input.
17
- * In RTL the two elements should swap sides — the selector moves to the right and
18
- * the text field moves to the left. This diff verifies the flex row reverses correctly.
19
- *
20
- * Size variants are included because the size prop changes the input-group class and
21
- * may affect spacing between the two sub-inputs.
22
- */
23
- export const RTLDiff: Story = {
16
+ export const Sizes: Story = {
24
17
  render: () => (
25
18
  <>
26
19
  <Field label="Default (md)">
@@ -42,3 +35,18 @@ export const RTLDiff: Story = {
42
35
  ),
43
36
  ...withRTLDiff(),
44
37
  };
38
+
39
+ export const DropdownOpen: Story = {
40
+ render: () => (
41
+ <Field label="Phone number">
42
+ <PhoneNumberInput onChange={() => {}} />
43
+ </Field>
44
+ ),
45
+ play: async ({ canvasElement }) => {
46
+ const canvas = within(canvasElement);
47
+ const trigger = canvas.queryByRole('combobox');
48
+ if (!trigger) return;
49
+ await userEvent.click(trigger);
50
+ },
51
+ ...withRTLDiff({ renderAs: 'iframe', hAlign: 'center' }),
52
+ };
@@ -14,16 +14,6 @@ export default {
14
14
 
15
15
  type Story = StoryObj<typeof Popover>;
16
16
 
17
- const iframeDiff = withRTLDiff({ renderAs: 'iframe' });
18
-
19
- const containerStyle: React.CSSProperties = {
20
- display: 'flex',
21
- justifyContent: 'center',
22
- alignItems: 'center',
23
- minHeight: '24rem',
24
- width: '100%',
25
- };
26
-
27
17
  const clickTrigger = async ({ canvasElement }: { canvasElement: HTMLElement }) => {
28
18
  const canvas = within(canvasElement);
29
19
  const trigger = canvas.queryByRole('button');
@@ -33,64 +23,64 @@ const clickTrigger = async ({ canvasElement }: { canvasElement: HTMLElement }) =
33
23
 
34
24
  export const PlacementRight: Story = {
35
25
  render: () => (
36
- <div style={containerStyle}>
37
- <Popover
38
- preferredPlacement={Position.RIGHT}
39
- title="Guaranteed rate"
40
- content="You'll get this rate as long as we receive your 10 EUR within the next 51 hours."
41
- >
42
- <Button v2>Placement right</Button>
43
- </Popover>
44
- </div>
26
+ <Popover
27
+ preferredPlacement={Position.RIGHT}
28
+ title="Guaranteed rate"
29
+ content="You'll get this rate as long as we receive your 10 EUR within the next 51 hours."
30
+ >
31
+ <Button size="sm" v2>
32
+ right
33
+ </Button>
34
+ </Popover>
45
35
  ),
46
36
  play: clickTrigger,
47
- ...iframeDiff,
37
+ ...withRTLDiff({ renderAs: 'iframe', hAlign: 'left', vAlign: 'center' }),
48
38
  };
49
39
 
50
40
  export const PlacementLeft: Story = {
51
41
  render: () => (
52
- <div style={containerStyle}>
53
- <Popover
54
- preferredPlacement={Position.LEFT}
55
- title="Guaranteed rate"
56
- content="You'll get this rate as long as we receive your 10 EUR within the next 51 hours."
57
- >
58
- <Button v2>Placement left</Button>
59
- </Popover>
60
- </div>
42
+ <Popover
43
+ preferredPlacement={Position.LEFT}
44
+ title="Guaranteed rate"
45
+ content="You'll get this rate as long as we receive your 10 EUR within the next 51 hours."
46
+ >
47
+ <Button size="sm" v2>
48
+ left
49
+ </Button>
50
+ </Popover>
61
51
  ),
62
52
  play: clickTrigger,
63
- ...iframeDiff,
53
+ ...withRTLDiff({ renderAs: 'iframe', hAlign: 'right', vAlign: 'center' }),
64
54
  };
65
55
 
66
56
  export const PlacementTop: Story = {
67
57
  render: () => (
68
- <div style={containerStyle}>
69
- <Popover
70
- preferredPlacement={Position.TOP}
71
- title="Guaranteed rate"
72
- content="You'll get this rate as long as we receive your 10 EUR within the next 51 hours."
73
- >
74
- <Button v2>Placement top</Button>
75
- </Popover>
76
- </div>
58
+ <Popover
59
+ preferredPlacement={Position.TOP}
60
+ title="Guaranteed rate"
61
+ content="You'll get this rate as long as we receive your 10 EUR within the next 51 hours."
62
+ >
63
+ <Button size="sm" v2>
64
+ top
65
+ </Button>
66
+ </Popover>
77
67
  ),
78
68
  play: clickTrigger,
79
- ...iframeDiff,
69
+ ...withRTLDiff({ renderAs: 'iframe', vAlign: 'bottom', hAlign: 'center' }),
80
70
  };
81
71
 
82
72
  export const PlacementBottom: Story = {
83
73
  render: () => (
84
- <div style={containerStyle}>
85
- <Popover
86
- preferredPlacement={Position.BOTTOM}
87
- title="Guaranteed rate"
88
- content="You'll get this rate as long as we receive your 10 EUR within the next 51 hours."
89
- >
90
- <Button v2>Placement bottom</Button>
91
- </Popover>
92
- </div>
74
+ <Popover
75
+ preferredPlacement={Position.BOTTOM}
76
+ title="Guaranteed rate"
77
+ content="You'll get this rate as long as we receive your 10 EUR within the next 51 hours."
78
+ >
79
+ <Button size="sm" v2>
80
+ bottom
81
+ </Button>
82
+ </Popover>
93
83
  ),
94
84
  play: clickTrigger,
95
- ...iframeDiff,
85
+ ...withRTLDiff({ renderAs: 'iframe', hAlign: 'center' }),
96
86
  };
@@ -3,7 +3,7 @@ import { Component } from 'react';
3
3
 
4
4
  import { Status, Size } from '../common';
5
5
 
6
- const radius = { xxs: 6, xs: 11, sm: 22, xl: 61 };
6
+ const radius = { xxs: 5, xs: 11, sm: 22, xl: 61 };
7
7
 
8
8
  const ANIMATION_DURATION_IN_MS = 1500;
9
9
 
@@ -0,0 +1,32 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+
3
+ import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
4
+ import ProcessIndicator from '..';
5
+
6
+ export default {
7
+ component: ProcessIndicator,
8
+ title: 'Loading/ProcessIndicator/Tests',
9
+ tags: ['!autodocs', '!manifest'],
10
+ } satisfies Meta<typeof ProcessIndicator>;
11
+
12
+ type Story = StoryObj<typeof ProcessIndicator>;
13
+
14
+ const sizes = ['xxs', 'xs', 'sm', 'xl'] as const;
15
+ const statuses = ['succeeded', 'failed'] as const;
16
+
17
+ const rowStyle = { display: 'flex', alignItems: 'center', gap: '16px' } as const;
18
+
19
+ export const RTLDiff: Story = {
20
+ render: () => (
21
+ <>
22
+ {statuses.map((status) => (
23
+ <div key={status} style={rowStyle}>
24
+ {sizes.map((size) => (
25
+ <ProcessIndicator key={size} size={size} status={status} />
26
+ ))}
27
+ </div>
28
+ ))}
29
+ </>
30
+ ),
31
+ ...withRTLDiff(),
32
+ };
@@ -1,6 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
 
3
- import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
3
+ import { useRtlDiffI18n, withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
4
4
  import ProgressBar from '..';
5
5
 
6
6
  export default {
@@ -12,22 +12,36 @@ export default {
12
12
  type Story = StoryObj<typeof ProgressBar>;
13
13
 
14
14
  export const RTLDiff: Story = {
15
- render: () => (
16
- <>
17
- <ProgressBar
18
- id="progress-bar-with-description"
19
- title="Send 5,550 GBP"
20
- description="You have 3 days to reach your goal."
21
- progress={{ value: 50, max: 100 }}
22
- textEnd="200 GBP to go"
23
- />
24
- <ProgressBar
25
- id="progress-bar-without-description"
26
- title="Total limit"
27
- progress={{ value: 800, max: 1000 }}
28
- textEnd="200 GBP remaining"
29
- />
30
- </>
31
- ),
32
- ...withRTLDiff(),
15
+ render: () => {
16
+ const i18n = useRtlDiffI18n();
17
+ return (
18
+ <>
19
+ <ProgressBar
20
+ id="progress-bar-with-description"
21
+ title={i18n.title1}
22
+ description={i18n.description}
23
+ progress={{ value: 50, max: 100 }}
24
+ textEnd={i18n.textEnd1}
25
+ />
26
+ <ProgressBar
27
+ id="progress-bar-without-description"
28
+ title={i18n.title2}
29
+ progress={{ value: 800, max: 1000 }}
30
+ textEnd={i18n.textEnd2}
31
+ />
32
+ </>
33
+ );
34
+ },
35
+ ...withRTLDiff({
36
+ i18n: {
37
+ title1: { ltr: 'Send 5,550 GBP', rtl: 'أرسل ٥٬٥٥٠ جنيه إسترليني' },
38
+ description: {
39
+ ltr: 'You have 3 days to reach your goal.',
40
+ rtl: 'لديك ٣ أيام للوصول إلى هدفك.',
41
+ },
42
+ textEnd1: { ltr: '200 GBP to go', rtl: '٢٠٠ جنيه إسترليني متبقي' },
43
+ title2: { ltr: 'Total limit', rtl: 'الحد الإجمالي' },
44
+ textEnd2: { ltr: '200 GBP remaining', rtl: '٢٠٠ جنيه إسترليني متبقي' },
45
+ },
46
+ }),
33
47
  };
@@ -155,6 +155,35 @@
155
155
  mask: var(--critical-banner-overhang-mask);
156
156
  }
157
157
  }
158
+ .wds-critical-banner__entry-mask {
159
+ display: grid;
160
+ grid-template-rows: 1fr;
161
+ overflow: visible;
162
+ }
163
+ .wds-critical-banner__entry-mask--animated {
164
+ grid-template-rows: 0fr;
165
+ overflow: hidden;
166
+ animation: wds-critical-banner-reveal-height 400ms cubic-bezier(0.3, 0, 0.1, 1) 500ms both, wds-critical-banner-release-overflow 1ms linear 899ms both;
167
+ }
168
+ .wds-critical-banner__entry-track {
169
+ min-block-size: 0;
170
+ }
171
+ @keyframes wds-critical-banner-reveal-height {
172
+ from {
173
+ grid-template-rows: 0fr;
174
+ }
175
+ to {
176
+ grid-template-rows: 1fr;
177
+ }
178
+ }
179
+ @keyframes wds-critical-banner-release-overflow {
180
+ from {
181
+ overflow: hidden;
182
+ }
183
+ to {
184
+ overflow: visible;
185
+ }
186
+ }
158
187
  @media (prefers-reduced-motion: reduce) {
159
188
  .wds-critical-banner__description,
160
189
  .wds-critical-banner__title,
@@ -162,4 +191,9 @@
162
191
  .wds-critical-banner__toggle {
163
192
  transition: none !important;
164
193
  }
194
+ .wds-critical-banner__entry-mask--animated {
195
+ animation: none;
196
+ grid-template-rows: 1fr;
197
+ overflow: visible;
198
+ }
165
199
  }
@@ -193,6 +193,45 @@
193
193
  }
194
194
  }
195
195
 
196
+ .wds-critical-banner__entry-mask {
197
+ display: grid;
198
+ grid-template-rows: 1fr;
199
+ overflow: visible;
200
+
201
+ // Clip only during the reveal, then release overflow so actions remain visible.
202
+ &--animated {
203
+ grid-template-rows: 0fr;
204
+ overflow: hidden;
205
+ animation:
206
+ wds-critical-banner-reveal-height 400ms cubic-bezier(0.3, 0, 0.1, 1) 500ms both,
207
+ wds-critical-banner-release-overflow 1ms linear 899ms both;
208
+ }
209
+ }
210
+
211
+ .wds-critical-banner__entry-track {
212
+ min-height: 0;
213
+ }
214
+
215
+ @keyframes wds-critical-banner-reveal-height {
216
+ from {
217
+ grid-template-rows: 0fr;
218
+ }
219
+
220
+ to {
221
+ grid-template-rows: 1fr;
222
+ }
223
+ }
224
+
225
+ @keyframes wds-critical-banner-release-overflow {
226
+ from {
227
+ overflow: hidden;
228
+ }
229
+
230
+ to {
231
+ overflow: visible;
232
+ }
233
+ }
234
+
196
235
  // Reduced motion support
197
236
  @media (prefers-reduced-motion: reduce) {
198
237
  .wds-critical-banner__description,
@@ -201,4 +240,10 @@
201
240
  .wds-critical-banner__toggle {
202
241
  transition: none !important;
203
242
  }
243
+
244
+ .wds-critical-banner__entry-mask--animated {
245
+ animation: none;
246
+ grid-template-rows: 1fr;
247
+ overflow: visible;
248
+ }
204
249
  }
@@ -15,7 +15,7 @@ describe('CriticalBanner', () => {
15
15
  resetLiveRegionAnnouncementQueue();
16
16
  });
17
17
 
18
- it('renders mobile overhang markup without entry animation markup', () => {
18
+ it('applies mobile overhang classes by default', () => {
19
19
  const { container } = render(
20
20
  <CriticalBanner {...defaultProps} data-testid="critical-banner" />,
21
21
  );
@@ -23,7 +23,30 @@ describe('CriticalBanner', () => {
23
23
  expect(screen.getByTestId('critical-banner')).toBeInTheDocument();
24
24
  expect(container.querySelector('.wds-critical-banner-overhang')).toBeInTheDocument();
25
25
  expect(container.querySelector('.wds-critical-banner-overhang-query')).toBeInTheDocument();
26
- expect(container.querySelector('.wds-critical-banner__entry-mask')).not.toBeInTheDocument();
27
- expect(container.querySelector('.wds-critical-banner__entry-track')).not.toBeInTheDocument();
26
+ });
27
+
28
+ it('applies the entry animation by default', () => {
29
+ const { container } = render(
30
+ <CriticalBanner {...defaultProps} data-testid="critical-banner" />,
31
+ );
32
+
33
+ expect(screen.getByTestId('critical-banner')).toBeInTheDocument();
34
+ expect(
35
+ container.querySelector('.wds-critical-banner__entry-mask--animated'),
36
+ ).toBeInTheDocument();
37
+ expect(container.querySelector('.wds-critical-banner__entry-track')).toBeInTheDocument();
38
+ });
39
+
40
+ it('can render without the entry animation', () => {
41
+ const { container } = render(
42
+ <CriticalBanner {...defaultProps} animateEntry={false} data-testid="critical-banner" />,
43
+ );
44
+
45
+ expect(screen.getByTestId('critical-banner')).toBeInTheDocument();
46
+ expect(container.querySelector('.wds-critical-banner__entry-mask')).toBeInTheDocument();
47
+ expect(
48
+ container.querySelector('.wds-critical-banner__entry-mask--animated'),
49
+ ).not.toBeInTheDocument();
50
+ expect(container.querySelector('.wds-critical-banner__entry-track')).toBeInTheDocument();
28
51
  });
29
52
  });
@@ -24,6 +24,11 @@ export type CriticalBannerProps = {
24
24
  label: ButtonProps['children'];
25
25
  };
26
26
  sentiment?: Exclude<PrimitivePromptProps['sentiment'], 'proposition'>;
27
+ /**
28
+ * Controls whether the banner plays its entry animation 500ms after mounting.
29
+ * Expand and collapse animations are unaffected.
30
+ */
31
+ animateEntry?: boolean;
27
32
  /**
28
33
  * Controls whether the description and actions are visible, as a controlled component.
29
34
  * When collapsed with a title, only the title is shown.
@@ -53,6 +58,7 @@ export const CriticalBanner = ({
53
58
  media = {},
54
59
  action,
55
60
  actionSecondary,
61
+ animateEntry = true,
56
62
  expanded: expandedProp,
57
63
  onToggle,
58
64
  id,
@@ -179,7 +185,15 @@ export const CriticalBanner = ({
179
185
  actionSecondaryLabel: actionSecondary?.label,
180
186
  })}
181
187
  >
182
- <div className="wds-critical-banner-overhang-query">{bannerSurface}</div>
188
+ <div className="wds-critical-banner-overhang-query">
189
+ <div
190
+ className={clsx('wds-critical-banner__entry-mask', {
191
+ 'wds-critical-banner__entry-mask--animated': animateEntry,
192
+ })}
193
+ >
194
+ <div className="wds-critical-banner__entry-track">{bannerSurface}</div>
195
+ </div>
196
+ </div>
183
197
  </LiveRegion>
184
198
  );
185
199
  };
@@ -66,7 +66,10 @@ Screen readers announce the banner content whenever:
66
66
 
67
67
  ## Reduced Motion
68
68
 
69
- `CriticalBanner` respects the user's reduced motion preference. When reduced motion is on, the component does not show the collapse and expand animation used when the user toggles the banner content.
69
+ `CriticalBanner` respects the user's reduced motion preference. When reduced motion is on, the component does not show:
70
+
71
+ - the entry animation that reveals the banner from above when it first appears
72
+ - the collapse and expand animation used when the user toggles the banner content
70
73
 
71
74
  The same content is still rendered and announced. Reduced motion only removes the visual movement.
72
75