@transferwise/components 46.159.0 → 46.159.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 (179) hide show
  1. package/build/i18n/cs.json +5 -0
  2. package/build/i18n/cs.json.js +5 -0
  3. package/build/i18n/cs.json.js.map +1 -1
  4. package/build/i18n/cs.json.mjs +5 -0
  5. package/build/i18n/cs.json.mjs.map +1 -1
  6. package/build/i18n/de.json +5 -0
  7. package/build/i18n/de.json.js +5 -0
  8. package/build/i18n/de.json.js.map +1 -1
  9. package/build/i18n/de.json.mjs +5 -0
  10. package/build/i18n/de.json.mjs.map +1 -1
  11. package/build/i18n/es.json +5 -0
  12. package/build/i18n/es.json.js +5 -0
  13. package/build/i18n/es.json.js.map +1 -1
  14. package/build/i18n/es.json.mjs +5 -0
  15. package/build/i18n/es.json.mjs.map +1 -1
  16. package/build/i18n/fr.json +5 -0
  17. package/build/i18n/fr.json.js +5 -0
  18. package/build/i18n/fr.json.js.map +1 -1
  19. package/build/i18n/fr.json.mjs +5 -0
  20. package/build/i18n/fr.json.mjs.map +1 -1
  21. package/build/i18n/hu.json +5 -0
  22. package/build/i18n/hu.json.js +5 -0
  23. package/build/i18n/hu.json.js.map +1 -1
  24. package/build/i18n/hu.json.mjs +5 -0
  25. package/build/i18n/hu.json.mjs.map +1 -1
  26. package/build/i18n/id.json +5 -0
  27. package/build/i18n/id.json.js +5 -0
  28. package/build/i18n/id.json.js.map +1 -1
  29. package/build/i18n/id.json.mjs +5 -0
  30. package/build/i18n/id.json.mjs.map +1 -1
  31. package/build/i18n/it.json +5 -0
  32. package/build/i18n/it.json.js +5 -0
  33. package/build/i18n/it.json.js.map +1 -1
  34. package/build/i18n/it.json.mjs +5 -0
  35. package/build/i18n/it.json.mjs.map +1 -1
  36. package/build/i18n/ja.json +5 -0
  37. package/build/i18n/ja.json.js +5 -0
  38. package/build/i18n/ja.json.js.map +1 -1
  39. package/build/i18n/ja.json.mjs +5 -0
  40. package/build/i18n/ja.json.mjs.map +1 -1
  41. package/build/i18n/nl.json +5 -0
  42. package/build/i18n/nl.json.js +5 -0
  43. package/build/i18n/nl.json.js.map +1 -1
  44. package/build/i18n/nl.json.mjs +5 -0
  45. package/build/i18n/nl.json.mjs.map +1 -1
  46. package/build/i18n/pl.json +5 -0
  47. package/build/i18n/pl.json.js +5 -0
  48. package/build/i18n/pl.json.js.map +1 -1
  49. package/build/i18n/pl.json.mjs +5 -0
  50. package/build/i18n/pl.json.mjs.map +1 -1
  51. package/build/i18n/pt.json +5 -0
  52. package/build/i18n/pt.json.js +5 -0
  53. package/build/i18n/pt.json.js.map +1 -1
  54. package/build/i18n/pt.json.mjs +5 -0
  55. package/build/i18n/pt.json.mjs.map +1 -1
  56. package/build/i18n/ro.json +5 -0
  57. package/build/i18n/ro.json.js +5 -0
  58. package/build/i18n/ro.json.js.map +1 -1
  59. package/build/i18n/ro.json.mjs +5 -0
  60. package/build/i18n/ro.json.mjs.map +1 -1
  61. package/build/i18n/ru.json +5 -0
  62. package/build/i18n/ru.json.js +5 -0
  63. package/build/i18n/ru.json.js.map +1 -1
  64. package/build/i18n/ru.json.mjs +5 -0
  65. package/build/i18n/ru.json.mjs.map +1 -1
  66. package/build/i18n/th.json +5 -0
  67. package/build/i18n/th.json.js +5 -0
  68. package/build/i18n/th.json.js.map +1 -1
  69. package/build/i18n/th.json.mjs +5 -0
  70. package/build/i18n/th.json.mjs.map +1 -1
  71. package/build/i18n/uk.json +5 -0
  72. package/build/i18n/uk.json.js +5 -0
  73. package/build/i18n/uk.json.js.map +1 -1
  74. package/build/i18n/uk.json.mjs +5 -0
  75. package/build/i18n/uk.json.mjs.map +1 -1
  76. package/build/i18n/zh-HK.json +5 -0
  77. package/build/i18n/zh-HK.json.js +5 -0
  78. package/build/i18n/zh-HK.json.js.map +1 -1
  79. package/build/i18n/zh-HK.json.mjs +5 -0
  80. package/build/i18n/zh-HK.json.mjs.map +1 -1
  81. package/build/main.css +4 -4104
  82. package/build/styles/ListItem/ListItem.css +1 -0
  83. package/build/styles/PocketSurface/PocketSurface.css +2 -11
  84. package/build/styles/SentimentSurface/SentimentSurface.css +0 -23
  85. package/build/styles/css/neptune.css +0 -2821
  86. package/build/styles/main.css +4 -4104
  87. package/build/styles/styles/less/neptune.css +0 -2821
  88. package/build/types/ListItem/_stories/variants/helpers.d.ts.map +1 -1
  89. package/package.json +10 -10
  90. package/src/Accordion/_stories/Accordion.test.story.tsx +71 -0
  91. package/src/AvatarLayout/_stories/AvatarLayout.test.story.tsx +64 -0
  92. package/src/AvatarView/_stories/AvatarView.test.story.tsx +151 -0
  93. package/src/Button/LegacyButton/_stories/LegacyButton.test.story.tsx +22 -0
  94. package/src/Button/_stories/Button.test.story.tsx +65 -0
  95. package/src/Calendar/_stories/Calendar.test.story.tsx +73 -24
  96. package/src/Carousel/_stories/Carousel.test.story.tsx +59 -0
  97. package/src/Checkbox/_stories/Checkbox.test.story.tsx +20 -0
  98. package/src/CheckboxButton/_stories/CheckboxButton.test.story.tsx +17 -37
  99. package/src/Chevron/_stories/Chevron.test.story.tsx +26 -0
  100. package/src/Chips/_stories/Chips.test.story.tsx +39 -34
  101. package/src/CircularButton/_stories/CircularButton.test.story.tsx +0 -68
  102. package/src/DateInput/_stories/DateInput.test.story.tsx +13 -0
  103. package/src/DateLookup/_stories/DateLookup.rtl.test.story.tsx +88 -0
  104. package/src/Decision/_stories/Decision.test.story.tsx +79 -0
  105. package/src/DefinitionList/_stories/DefinitionList.test.story.tsx +54 -1
  106. package/src/Drawer/_stories/Drawer.rtl.test.story.tsx +58 -0
  107. package/src/ExpressiveMoneyInput/_stories/ExpressiveMoneyInput.rtl.test.story.tsx +118 -0
  108. package/src/Field/_stories/Field.test.story.tsx +46 -0
  109. package/src/FlowNavigation/_stories/FlowNavigation.test.story.tsx +44 -0
  110. package/src/Header/_stories/Header.test.story.tsx +27 -0
  111. package/src/Info/_stories/Info.rtl.test.story.tsx +64 -0
  112. package/src/Inputs/SelectInput/_stories/SelectInput.rtl.test.story.tsx +135 -0
  113. package/src/Inputs/_stories/InputGroup.test.story.tsx +69 -0
  114. package/src/Inputs/_stories/SearchInput.test.story.tsx +32 -0
  115. package/src/Inputs/_stories/TextArea.test.story.tsx +27 -0
  116. package/src/InstructionsList/_stories/InstructionsList.test.story.tsx +30 -0
  117. package/src/Label/_stories/Label.test.story.tsx +36 -0
  118. package/src/Link/_stories/Link.test.story.tsx +26 -0
  119. package/src/ListItem/ListItem.css +1 -0
  120. package/src/ListItem/_stories/{variants/ListItem.rtl.test.story.tsx → ListItem.rtl.test.story.tsx} +6 -10
  121. package/src/ListItem/_stories/variants/helpers.tsx +2 -6
  122. package/src/Logo/_stories/Logo.test.story.tsx +44 -0
  123. package/src/Modal/_stories/Modal.rtl.test.story.tsx +59 -0
  124. package/src/MoneyInput/_stories/MoneyInput.rtl.test.story.tsx +88 -0
  125. package/src/Nudge/_stories/Nudge.test.story.tsx +37 -0
  126. package/src/PhoneNumberInput/_stories/PhoneNumberInput.test.story.tsx +44 -0
  127. package/src/PocketSurface/PocketSurface.css +2 -11
  128. package/src/PocketSurface/PocketSurface.less +1 -1
  129. package/src/Popover/_stories/Popover.rtl.test.story.tsx +96 -0
  130. package/src/Progress/_stories/Progress.test.story.tsx +27 -0
  131. package/src/ProgressBar/_stories/ProgressBar.test.story.tsx +33 -0
  132. package/src/PromoCard/_stories/PromoCard.test.story.tsx +42 -0
  133. package/src/PromoCard/_stories/PromoCardGroup.test.story.tsx +51 -0
  134. package/src/Prompt/ActionPrompt/_stories/ActionPrompt.test.story.tsx +42 -1
  135. package/src/Prompt/CriticalBanner/_stories/CriticalBanner.test.story.tsx +34 -31
  136. package/src/Prompt/InfoPrompt/_stories/InfoPrompt.test.story.tsx +25 -0
  137. package/src/Prompt/InlinePrompt/_stories/InlinePrompt.test.story.tsx +27 -0
  138. package/src/Radio/_stories/Radio.test.story.tsx +61 -0
  139. package/src/RadioGroup/_stories/RadioGroup.test.story.tsx +56 -0
  140. package/src/Section/_stories/Section.test.story.tsx +29 -0
  141. package/src/SegmentedControl/_stories/SegmentedControl.test.story.tsx +45 -0
  142. package/src/SentimentSurface/SentimentSurface.css +0 -23
  143. package/src/SlidingPanel/_stories/SlidingPanel.rtl.test.story.tsx +70 -0
  144. package/src/Snackbar/_stories/Snackbar.rtl.test.story.tsx +45 -0
  145. package/src/Snackbar/_stories/Snackbar.test.story.tsx +0 -31
  146. package/src/Stepper/_stories/Stepper.test.story.tsx +17 -0
  147. package/src/Switch/_stories/Switch.test.story.tsx +20 -26
  148. package/src/Table/_stories/Table.test.story.tsx +104 -0
  149. package/src/Tabs/_stories/Tabs.test.story.tsx +72 -0
  150. package/src/TextareaWithDisplayFormat/_stories/TextareaWithDisplayFormat.test.story.tsx +48 -0
  151. package/src/Tooltip/_stories/Tooltip.rtl.test.story.tsx +79 -0
  152. package/src/Typeahead/_stories/Typeahead.rtl.test.story.tsx +90 -0
  153. package/src/Upload/_stories/Upload.rtl.test.story.tsx +53 -0
  154. package/src/UploadInput/_stories/UploadInput.test.story.tsx +47 -0
  155. package/src/_deprecated/Alert/_stories/Alert.test.story.tsx +28 -1
  156. package/src/_deprecated/Card/_stories/Card.test.story.tsx +38 -0
  157. package/src/_deprecated/CriticalBanner/_stories/CriticalCommsBanner.test.story.tsx +18 -0
  158. package/src/_deprecated/InlineAlert/_stories/InlineAlert.test.story.tsx +26 -0
  159. package/src/_deprecated/OverlayHeader/_stories/OverlayHeader.test.story.tsx +42 -0
  160. package/src/_deprecated/Select/_stories/Select.rtl.test.story.tsx +71 -0
  161. package/src/common/BottomSheet/_stories/BottomSheet.test.story.tsx +23 -0
  162. package/src/i18n/cs.json +5 -0
  163. package/src/i18n/de.json +5 -0
  164. package/src/i18n/es.json +5 -0
  165. package/src/i18n/fr.json +5 -0
  166. package/src/i18n/hu.json +5 -0
  167. package/src/i18n/id.json +5 -0
  168. package/src/i18n/it.json +5 -0
  169. package/src/i18n/ja.json +5 -0
  170. package/src/i18n/nl.json +5 -0
  171. package/src/i18n/pl.json +5 -0
  172. package/src/i18n/pt.json +5 -0
  173. package/src/i18n/ro.json +5 -0
  174. package/src/i18n/ru.json +5 -0
  175. package/src/i18n/th.json +5 -0
  176. package/src/i18n/uk.json +5 -0
  177. package/src/i18n/zh-HK.json +5 -0
  178. package/src/main.css +4 -4104
  179. package/src/styles/less/neptune.css +0 -2821
@@ -0,0 +1,70 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+
3
+ import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
4
+ import SlidingPanel from '../SlidingPanel';
5
+
6
+ export default {
7
+ component: SlidingPanel,
8
+ title: 'Dialogs/SlidingPanel/Tests/RTL Diff',
9
+ tags: ['!autodocs', '!manifest'],
10
+ } satisfies Meta<typeof SlidingPanel>;
11
+
12
+ type Story = StoryObj<typeof SlidingPanel>;
13
+
14
+ const iframeDiff = withRTLDiff({ renderAs: 'iframe' });
15
+
16
+ const containerStyle: React.CSSProperties = {
17
+ position: 'relative',
18
+ width: 300,
19
+ height: 120,
20
+ overflow: 'hidden',
21
+ border: '1px dashed #ccc',
22
+ };
23
+
24
+ const panelContentStyle: React.CSSProperties = {
25
+ padding: '16px',
26
+ };
27
+
28
+ export const PositionLeft: Story = {
29
+ render: () => (
30
+ <div style={containerStyle}>
31
+ <SlidingPanel open position="left">
32
+ <div style={panelContentStyle}>Left panel content</div>
33
+ </SlidingPanel>
34
+ </div>
35
+ ),
36
+ ...iframeDiff,
37
+ };
38
+
39
+ export const PositionRight: Story = {
40
+ render: () => (
41
+ <div style={containerStyle}>
42
+ <SlidingPanel open position="right">
43
+ <div style={panelContentStyle}>Right panel content</div>
44
+ </SlidingPanel>
45
+ </div>
46
+ ),
47
+ ...iframeDiff,
48
+ };
49
+
50
+ export const PositionLeftWithBorder: Story = {
51
+ render: () => (
52
+ <div style={containerStyle}>
53
+ <SlidingPanel open position="left" showSlidingPanelBorder>
54
+ <div style={panelContentStyle}>Left panel with border</div>
55
+ </SlidingPanel>
56
+ </div>
57
+ ),
58
+ ...iframeDiff,
59
+ };
60
+
61
+ export const PositionRightWithBorder: Story = {
62
+ render: () => (
63
+ <div style={containerStyle}>
64
+ <SlidingPanel open position="right" showSlidingPanelBorder>
65
+ <div style={panelContentStyle}>Right panel with border</div>
66
+ </SlidingPanel>
67
+ </div>
68
+ ),
69
+ ...iframeDiff,
70
+ };
@@ -0,0 +1,45 @@
1
+ import { useState } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
3
+
4
+ import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
5
+ import { Snackbar } from '../Snackbar';
6
+
7
+ export default {
8
+ component: Snackbar,
9
+ title: 'Dialogs/Snackbar/Tests/RTL Diff',
10
+ tags: ['!autodocs', '!manifest'],
11
+ } satisfies Meta<typeof Snackbar>;
12
+
13
+ type Story = StoryObj<typeof Snackbar>;
14
+
15
+ const TIMEOUT = 10_000_000;
16
+ const iframeDiff = withRTLDiff({ renderAs: 'iframe' });
17
+
18
+ function RenderWithAction() {
19
+ const [timestamp] = useState(() => Date.now());
20
+
21
+ return (
22
+ <Snackbar
23
+ text={<span>Transfer completed successfully</span>}
24
+ timestamp={timestamp}
25
+ timeout={TIMEOUT}
26
+ action={{ label: 'View' }}
27
+ />
28
+ );
29
+ }
30
+
31
+ function RenderWithoutAction() {
32
+ const [timestamp] = useState(() => Date.now());
33
+
34
+ return <Snackbar text={<span>Settings saved</span>} timestamp={timestamp} timeout={TIMEOUT} />;
35
+ }
36
+
37
+ export const WithAction: Story = {
38
+ render: () => <RenderWithAction />,
39
+ ...iframeDiff,
40
+ };
41
+
42
+ export const WithoutAction: Story = {
43
+ render: () => <RenderWithoutAction />,
44
+ ...iframeDiff,
45
+ };
@@ -103,37 +103,6 @@ export const DarkMode = {
103
103
  ...withVariantConfig(['dark'], Default),
104
104
  };
105
105
 
106
- const switchToRTL = async (context: { canvasElement: HTMLElement }) => {
107
- await Default.play(context);
108
- // the snackbar is outside the storybook dom so the RTL variant doesn't apply
109
- const body = document.querySelector('div.storybook-container');
110
- if (body) {
111
- body.setAttribute('dir', 'rtl');
112
- }
113
- const snackbar = document.querySelector('div.snackbar');
114
- if (snackbar) {
115
- snackbar.setAttribute('dir', 'rtl');
116
- }
117
- };
118
-
119
- export const RTL = {
120
- ...Default,
121
- args: {
122
- ...Default.args,
123
- },
124
- play: switchToRTL,
125
- ...withVariantConfig(['rtl'], Default),
126
- };
127
-
128
- export const RTLMobile = {
129
- ...Default,
130
- args: {
131
- ...RTL.args,
132
- },
133
- play: switchToRTL,
134
- ...withVariantConfig(['rtl', 'mobile'], Default),
135
- };
136
-
137
106
  export const Zoom400 = {
138
107
  ...Default,
139
108
  args: {
@@ -1,6 +1,7 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { fn, within, expect, screen } from 'storybook/test';
3
3
  import Stepper from '../Stepper';
4
+ import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
4
5
 
5
6
  const STEPS = [
6
7
  {
@@ -88,3 +89,19 @@ export const ProgressbarWidth: Story = {
88
89
  steps: STEPS,
89
90
  },
90
91
  };
92
+
93
+ const RTL_STEPS = [{ label: 'Amount' }, { label: 'You' }, { label: 'Review' }, { label: 'Pay' }];
94
+
95
+ export const RTLDiff: Story = {
96
+ args: {
97
+ steps: RTL_STEPS,
98
+ },
99
+ render: () => (
100
+ <div style={{ width: '80%', marginLeft: 'auto', marginRight: 'auto' }}>
101
+ <Stepper steps={RTL_STEPS} activeStep={0} />
102
+ <Stepper steps={RTL_STEPS} activeStep={2} />
103
+ <Stepper steps={RTL_STEPS} activeStep={4} />
104
+ </div>
105
+ ),
106
+ ...withRTLDiff(),
107
+ };
@@ -3,6 +3,7 @@ import { expect, userEvent, within } from 'storybook/test';
3
3
  import { useState } from 'react';
4
4
 
5
5
  import { withVariantConfig } from '../../../.storybook/helpers';
6
+ import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
6
7
  import Field from '../../Field/Field';
7
8
  import Switch from '../Switch';
8
9
 
@@ -83,32 +84,6 @@ export const KeyboardInteraction: Story = {
83
84
  },
84
85
  };
85
86
 
86
- /** Switch rendered in RTL mode. */
87
- export const RTL: Story = {
88
- render: function Render() {
89
- const [unchecked, setUnchecked] = useState(false);
90
- const [checked, setChecked] = useState(true);
91
-
92
- return (
93
- <div style={{ display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px' }}>
94
- <Field label="Unchecked">
95
- <Switch checked={unchecked} onClick={() => setUnchecked((v) => !v)} />
96
- </Field>
97
- <Field label="Checked">
98
- <Switch checked={checked} onClick={() => setChecked((v) => !v)} />
99
- </Field>
100
- <Field label="Disabled (off)">
101
- <Switch checked={false} disabled onClick={() => {}} />
102
- </Field>
103
- <Field label="Disabled (on)">
104
- <Switch checked disabled onClick={() => {}} />
105
- </Field>
106
- </div>
107
- );
108
- },
109
- ...withVariantConfig(['rtl']),
110
- };
111
-
112
87
  /** Switch at 400% zoom for accessibility testing. */
113
88
  export const Zoom400: Story = {
114
89
  render: function Render() {
@@ -121,3 +96,22 @@ export const Zoom400: Story = {
121
96
  },
122
97
  ...withVariantConfig(['400%']),
123
98
  };
99
+
100
+ /**
101
+ * The switch thumb translates horizontally: in LTR checked state it moves right,
102
+ * in RTL checked state it moves left (via [dir="rtl"] CSS). This diff verifies
103
+ * the thumb direction flips correctly between writing modes.
104
+ */
105
+ export const RTLDiff: Story = {
106
+ render: () => (
107
+ <>
108
+ <Field label="Unchecked">
109
+ <Switch checked={false} onClick={() => {}} />
110
+ </Field>
111
+ <Field label="Checked">
112
+ <Switch checked onClick={() => {}} />
113
+ </Field>
114
+ </>
115
+ ),
116
+ ...withRTLDiff(),
117
+ };
@@ -0,0 +1,104 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import { DirectDebits } from '@transferwise/icons';
3
+
4
+ import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
5
+ import Table from '..';
6
+ import { TableHeaderType } from '../Header/TableHeader';
7
+ import { TableRowType, TableRowClickableType } from '../Row/TableRow';
8
+
9
+ const headers: TableHeaderType[] = [
10
+ { header: 'Employee', width: '200px' },
11
+ { header: 'Location' },
12
+ { header: 'Payment', alignment: 'right' },
13
+ { header: 'Status' },
14
+ ];
15
+
16
+ const rows: TableRowType[] = [
17
+ {
18
+ cells: [
19
+ {
20
+ cell: {
21
+ type: 'leading',
22
+ primaryText: 'Bob Brown',
23
+ secondaryText: 'Software Engineer',
24
+ avatar: { profileName: 'Bob Brown', badge: { flagCode: 'GBP' } },
25
+ },
26
+ },
27
+ { cell: { type: 'text', text: 'FR, Paris' } },
28
+ {
29
+ cell: {
30
+ type: 'currency',
31
+ primaryCurrency: { amount: 12345.67, currency: 'eur' },
32
+ secondaryCurrency: { amount: 11000.0, currency: 'gbp' },
33
+ },
34
+ alignment: 'right',
35
+ },
36
+ {
37
+ cell: {
38
+ type: 'status',
39
+ primaryText: 'Cancelled',
40
+ secondaryText: '1 day ago',
41
+ sentiment: 'negative',
42
+ },
43
+ },
44
+ ],
45
+ },
46
+ {
47
+ cells: [
48
+ {
49
+ cell: {
50
+ type: 'leading',
51
+ primaryText: 'Jan Kowalski',
52
+ secondaryText: 'Product Manager',
53
+ avatar: { asset: <DirectDebits />, badge: { status: 'warning' } },
54
+ },
55
+ },
56
+ { cell: { type: 'text', text: 'US, New York' } },
57
+ {
58
+ cell: {
59
+ type: 'currency',
60
+ primaryCurrency: { amount: 23456.78, currency: 'usd' },
61
+ secondaryCurrency: { amount: 21000.0, currency: 'gbp' },
62
+ },
63
+ alignment: 'right',
64
+ },
65
+ {
66
+ cell: {
67
+ type: 'status',
68
+ primaryText: 'Paid',
69
+ secondaryText: 'Yesterday',
70
+ sentiment: 'positive',
71
+ },
72
+ },
73
+ ],
74
+ },
75
+ ];
76
+
77
+ export default {
78
+ component: Table,
79
+ title: 'Content/Table/Tests',
80
+ tags: ['!autodocs', '!manifest'],
81
+ } satisfies Meta<typeof Table>;
82
+
83
+ type Story = StoryObj<typeof Table>;
84
+
85
+ export const RTLDiff: Story = {
86
+ render: () => (
87
+ <>
88
+ <Table data={{ headers, rows }} />
89
+ <Table
90
+ data={{
91
+ headers,
92
+ rows,
93
+ onRowClick: (rowData: TableRowType | TableRowClickableType) => {
94
+ void rowData;
95
+ },
96
+ }}
97
+ />
98
+ <Table data={{ headers, rows }} fullWidth={false} />
99
+ <Table data={{ headers, rows: [] }} />
100
+ <Table data={{ headers, rows }} loading />
101
+ </>
102
+ ),
103
+ ...withRTLDiff(),
104
+ };
@@ -0,0 +1,72 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+
3
+ import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
4
+ import { Width } from '../../common';
5
+ import Tabs from '..';
6
+
7
+ export default {
8
+ component: Tabs,
9
+ title: 'Navigation/Tabs/Tests',
10
+ tags: ['!autodocs', '!manifest'],
11
+ } satisfies Meta<typeof Tabs>;
12
+
13
+ type Story = StoryObj<typeof Tabs>;
14
+
15
+ const defaultTabs = [
16
+ { title: 'First tab', disabled: false, content: <div className="p-a-2">First content</div> },
17
+ { title: 'Second tab', disabled: false, content: <div className="p-a-2">Second content</div> },
18
+ { title: 'Third tab', disabled: false, content: <div className="p-a-2">Third content</div> },
19
+ ];
20
+
21
+ /**
22
+ * The tab list is horizontal: tab order, the active indicator translateX, and
23
+ * swipe direction all flip between LTR and RTL. The indicator uses a negated
24
+ * translateX in RTL mode. This diff verifies the layout mirrors correctly.
25
+ */
26
+ export const RTLDiff: Story = {
27
+ render: () => (
28
+ <>
29
+ <div style={{ marginBottom: 32 }}>
30
+ <h4>Block width — first tab selected</h4>
31
+ <Tabs
32
+ name="rtl-block-first"
33
+ tabs={defaultTabs}
34
+ selected={0}
35
+ headerWidth={Width.BLOCK}
36
+ onTabSelect={() => {}}
37
+ />
38
+ </div>
39
+ <div style={{ marginBottom: 32 }}>
40
+ <h4>Block width — second tab selected</h4>
41
+ <Tabs
42
+ name="rtl-block-second"
43
+ tabs={defaultTabs}
44
+ selected={1}
45
+ headerWidth={Width.BLOCK}
46
+ onTabSelect={() => {}}
47
+ />
48
+ </div>
49
+ <div style={{ marginBottom: 32 }}>
50
+ <h4>Auto width — first tab selected</h4>
51
+ <Tabs
52
+ name="rtl-auto-first"
53
+ tabs={defaultTabs}
54
+ selected={0}
55
+ headerWidth={Width.AUTO}
56
+ onTabSelect={() => {}}
57
+ />
58
+ </div>
59
+ <div style={{ marginBottom: 32 }}>
60
+ <h4>Auto width — second tab selected</h4>
61
+ <Tabs
62
+ name="rtl-auto-second"
63
+ tabs={defaultTabs}
64
+ selected={1}
65
+ headerWidth={Width.AUTO}
66
+ onTabSelect={() => {}}
67
+ />
68
+ </div>
69
+ </>
70
+ ),
71
+ ...withRTLDiff(),
72
+ };
@@ -0,0 +1,48 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+
3
+ import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
4
+ import TextareaWithDisplayFormat from '..';
5
+ import Field from '../../Field/Field';
6
+
7
+ export default {
8
+ component: TextareaWithDisplayFormat,
9
+ title: 'Forms/TextareaWithDisplayFormat/Tests',
10
+ tags: ['!autodocs', '!manifest'],
11
+ } satisfies Meta<typeof TextareaWithDisplayFormat>;
12
+
13
+ type Story = StoryObj<typeof TextareaWithDisplayFormat>;
14
+
15
+ export const RTLDiff: Story = {
16
+ render: () => (
17
+ <>
18
+ <Field id="card-code" label="Gift card code">
19
+ <TextareaWithDisplayFormat
20
+ id="card-code"
21
+ value="1234567890123456"
22
+ maxLength={16}
23
+ displayPattern="**** - **** - ****"
24
+ onChange={() => {}}
25
+ />
26
+ </Field>
27
+ <Field id="iban" label="IBAN">
28
+ <TextareaWithDisplayFormat
29
+ id="iban"
30
+ value="GB29NWBK60161331926819"
31
+ maxLength={22}
32
+ displayPattern="** **** **** **** **** **"
33
+ onChange={() => {}}
34
+ />
35
+ </Field>
36
+ <Field id="empty" label="Empty with placeholder">
37
+ <TextareaWithDisplayFormat
38
+ id="empty"
39
+ value=""
40
+ displayPattern="**** - **** - ****"
41
+ placeholder="Enter code"
42
+ onChange={() => {}}
43
+ />
44
+ </Field>
45
+ </>
46
+ ),
47
+ ...withRTLDiff(),
48
+ };
@@ -0,0 +1,79 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import { userEvent, within } from 'storybook/test';
3
+
4
+ import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
5
+ import Button from '../../Button';
6
+ import Tooltip from '../Tooltip';
7
+
8
+ export default {
9
+ component: Tooltip,
10
+ title: 'Dialogs/Tooltip/Tests/RTL Diff',
11
+ tags: ['!autodocs', '!manifest'],
12
+ } satisfies Meta<typeof Tooltip>;
13
+
14
+ type Story = StoryObj<typeof Tooltip>;
15
+
16
+ const iframeDiff = withRTLDiff({ renderAs: 'iframe' });
17
+
18
+ const containerStyle: React.CSSProperties = {
19
+ display: 'flex',
20
+ justifyContent: 'center',
21
+ alignItems: 'center',
22
+ minHeight: '20rem',
23
+ width: '100%',
24
+ };
25
+
26
+ const hoverTrigger = async ({ canvasElement }: { canvasElement: HTMLElement }) => {
27
+ const canvas = within(canvasElement);
28
+ const button = canvas.queryByRole('button');
29
+ if (!button) return;
30
+ await userEvent.hover(button);
31
+ };
32
+
33
+ export const PositionTop: Story = {
34
+ render: () => (
35
+ <div style={containerStyle}>
36
+ <Tooltip label="Top positioned tooltip" position="top">
37
+ <Button>Top</Button>
38
+ </Tooltip>
39
+ </div>
40
+ ),
41
+ play: hoverTrigger,
42
+ ...iframeDiff,
43
+ };
44
+
45
+ export const PositionBottom: Story = {
46
+ render: () => (
47
+ <div style={containerStyle}>
48
+ <Tooltip label="Bottom positioned tooltip" position="bottom">
49
+ <Button>Bottom</Button>
50
+ </Tooltip>
51
+ </div>
52
+ ),
53
+ play: hoverTrigger,
54
+ ...iframeDiff,
55
+ };
56
+
57
+ export const PositionRight: Story = {
58
+ render: () => (
59
+ <div style={containerStyle}>
60
+ <Tooltip label="Right positioned tooltip" position="right">
61
+ <Button>Right</Button>
62
+ </Tooltip>
63
+ </div>
64
+ ),
65
+ play: hoverTrigger,
66
+ ...iframeDiff,
67
+ };
68
+
69
+ export const PositionLeft: Story = {
70
+ render: () => (
71
+ <div style={containerStyle}>
72
+ <Tooltip label="Left positioned tooltip" position="left">
73
+ <Button>Left</Button>
74
+ </Tooltip>
75
+ </div>
76
+ ),
77
+ play: hoverTrigger,
78
+ ...iframeDiff,
79
+ };
@@ -0,0 +1,90 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import { userEvent, within } from 'storybook/test';
3
+ import { Search as SearchIcon } from '@transferwise/icons';
4
+
5
+ import { forEachColumn, withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
6
+ import { Size } from '../../common';
7
+ import Typeahead from '..';
8
+
9
+ export default {
10
+ component: Typeahead,
11
+ title: 'Forms/Typeahead/Tests/RTL Diff',
12
+ tags: ['!autodocs', '!manifest'],
13
+ } satisfies Meta<typeof Typeahead>;
14
+
15
+ type Story = StoryObj<typeof Typeahead>;
16
+
17
+ const baseOptions = [
18
+ { label: 'British pound', note: 'GBP' },
19
+ { label: 'Euro', note: 'EUR' },
20
+ { label: 'US Dollar', note: 'USD' },
21
+ ];
22
+
23
+ export const TriggerVariants: Story = {
24
+ render: () => (
25
+ <>
26
+ <Typeahead
27
+ id="rtl-addon-md"
28
+ name="rtl-addon-md"
29
+ placeholder="Search currencies…"
30
+ size={Size.MEDIUM}
31
+ addon={<SearchIcon size={24} />}
32
+ clearable
33
+ options={baseOptions}
34
+ initialValue={[{ label: 'British pound', note: 'GBP' }]}
35
+ onChange={() => {}}
36
+ />
37
+ <Typeahead
38
+ id="rtl-addon-lg"
39
+ name="rtl-addon-lg"
40
+ placeholder="Search currencies…"
41
+ size={Size.LARGE}
42
+ addon={<SearchIcon size={24} />}
43
+ clearable
44
+ options={baseOptions}
45
+ initialValue={[{ label: 'Euro', note: 'EUR' }]}
46
+ onChange={() => {}}
47
+ />
48
+ <Typeahead
49
+ id="rtl-multiple"
50
+ name="rtl-multiple"
51
+ placeholder="Search currencies…"
52
+ size={Size.MEDIUM}
53
+ addon={<SearchIcon size={24} />}
54
+ multiple
55
+ clearable
56
+ options={baseOptions}
57
+ initialValue={[
58
+ { label: 'British pound', note: 'GBP' },
59
+ { label: 'Euro', note: 'EUR' },
60
+ ]}
61
+ onChange={() => {}}
62
+ />
63
+ </>
64
+ ),
65
+ ...withRTLDiff({ width: '80%' }),
66
+ };
67
+
68
+ export const OpenDropdown: Story = {
69
+ render: () => (
70
+ <Typeahead
71
+ id="rtl-open"
72
+ name="rtl-open"
73
+ placeholder="Search currencies…"
74
+ size={Size.LARGE}
75
+ addon={<SearchIcon size={24} />}
76
+ clearable
77
+ options={baseOptions}
78
+ onChange={() => {}}
79
+ />
80
+ ),
81
+ play: async ({ canvasElement }) => {
82
+ await forEachColumn(canvasElement, async (column) => {
83
+ const scope = within(column);
84
+ const input = scope.getByRole('combobox');
85
+ await userEvent.click(input);
86
+ await userEvent.keyboard('Brit');
87
+ });
88
+ },
89
+ ...withRTLDiff({ width: '80%' }),
90
+ };
@@ -0,0 +1,53 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import { userEvent, within } from 'storybook/test';
3
+
4
+ import { forEachColumn, withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
5
+ import Upload from '..';
6
+
7
+ export default {
8
+ component: Upload,
9
+ title: 'Forms/Upload/Tests/RTL Diff',
10
+ tags: ['!autodocs', '!manifest'],
11
+ } satisfies Meta<typeof Upload>;
12
+
13
+ type Story = StoryObj<typeof Upload>;
14
+
15
+ export const Sizes: Story = {
16
+ render: () => (
17
+ <>
18
+ <Upload usLabel="Small upload" size="sm" />
19
+ <Upload usLabel="Medium upload" size="md" />
20
+ <Upload usLabel="Large upload" size="lg" />
21
+ </>
22
+ ),
23
+ ...withRTLDiff(),
24
+ };
25
+
26
+ export const WithError: Story = {
27
+ render: () => (
28
+ <Upload
29
+ usAccept="*"
30
+ usLabel="Upload a file"
31
+ usPlaceholder="Maximum filesize is 1B"
32
+ maxSize={1}
33
+ size="md"
34
+ httpOptions={{
35
+ url: 'https://httpbin.org/post',
36
+ method: 'POST',
37
+ }}
38
+ />
39
+ ),
40
+ play: async ({ canvasElement }) => {
41
+ await forEachColumn(canvasElement, async (column) => {
42
+ const fileInput = column.querySelector<HTMLInputElement>('input[type="file"]');
43
+ if (!fileInput) return;
44
+ const file = new File(['content larger than 1 byte'], 'test-file.txt', {
45
+ type: 'text/plain',
46
+ });
47
+ await userEvent.upload(fileInput, file);
48
+ const scope = within(column);
49
+ await scope.findByText(/Maximum filesize is/i, {}, { timeout: 3000 });
50
+ });
51
+ },
52
+ ...withRTLDiff(),
53
+ };