@transferwise/components 46.164.0 → 46.165.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 (102) hide show
  1. package/build/ListItem/ListItem.js +2 -0
  2. package/build/ListItem/ListItem.js.map +1 -1
  3. package/build/ListItem/ListItem.mjs +2 -0
  4. package/build/ListItem/ListItem.mjs.map +1 -1
  5. package/build/Snackbar/Snackbar.js +98 -101
  6. package/build/Snackbar/Snackbar.js.map +1 -1
  7. package/build/Snackbar/Snackbar.mjs +99 -102
  8. package/build/Snackbar/Snackbar.mjs.map +1 -1
  9. package/build/main.css +5 -0
  10. package/build/styles/ListItem/ListItem.css +5 -0
  11. package/build/styles/main.css +5 -0
  12. package/build/types/Body/Body.d.ts +2 -2
  13. package/build/types/Carousel/_stories/constants.d.ts +25 -0
  14. package/build/types/Carousel/_stories/constants.d.ts.map +1 -1
  15. package/build/types/IconButton/IconButton.d.ts +1 -1
  16. package/build/types/ListItem/ListItem.d.ts +6 -1
  17. package/build/types/ListItem/ListItem.d.ts.map +1 -1
  18. package/build/types/ListItem/_stories/variants/helpers.d.ts +38 -1
  19. package/build/types/ListItem/_stories/variants/helpers.d.ts.map +1 -1
  20. package/build/types/MoneyInput/MoneyInput.d.ts +1 -1
  21. package/build/types/MoneyInput/MoneyInput.d.ts.map +1 -1
  22. package/build/types/Snackbar/Snackbar.d.ts +1 -15
  23. package/build/types/Snackbar/Snackbar.d.ts.map +1 -1
  24. package/build/types/Title/Title.d.ts +2 -2
  25. package/build/types/Upload/Steps/UploadImageStep/UploadImageStep.d.ts +1 -1
  26. package/build/types/UploadInput/UploadItem/UploadItemLink.d.ts +1 -1
  27. package/package.json +1 -1
  28. package/src/Accordion/_stories/Accordion.test.story.tsx +74 -48
  29. package/src/AvatarLayout/_stories/AvatarLayout.test.story.tsx +46 -30
  30. package/src/Button/LegacyButton/_stories/LegacyButton.test.story.tsx +29 -19
  31. package/src/Button/_stories/Button.test.story.tsx +96 -62
  32. package/src/Calendar/_stories/Calendar.test.story.tsx +34 -21
  33. package/src/Carousel/_stories/Carousel.test.story.tsx +18 -8
  34. package/src/Carousel/_stories/constants.tsx +29 -4
  35. package/src/Checkbox/_stories/Checkbox.test.story.tsx +22 -13
  36. package/src/CheckboxButton/_stories/CheckboxButton.test.story.tsx +23 -14
  37. package/src/Chips/_stories/Chips.test.story.tsx +51 -36
  38. package/src/DateInput/_stories/DateInput.rtl.test.story.tsx +62 -12
  39. package/src/DateLookup/_stories/DateLookup.rtl.test.story.tsx +2 -2
  40. package/src/Decision/_stories/Decision.test.story.tsx +74 -58
  41. package/src/DefinitionList/_stories/DefinitionList.test.story.tsx +58 -42
  42. package/src/Drawer/_stories/Drawer.rtl.test.story.tsx +65 -38
  43. package/src/ExpressiveMoneyInput/_stories/ExpressiveMoneyInput.rtl.test.story.tsx +107 -74
  44. package/src/Field/_stories/Field.test.story.tsx +18 -18
  45. package/src/FlowNavigation/_stories/FlowNavigation.rtl.test.story.tsx +81 -51
  46. package/src/Header/_stories/Header.test.story.tsx +30 -18
  47. package/src/Info/_stories/Info.rtl.test.story.tsx +124 -50
  48. package/src/InputWithDisplayFormat/_stories/InputWithDisplayFormat.test.story.tsx +4 -4
  49. package/src/Inputs/InputGroup/_stories/InputGroup.test.story.tsx +9 -8
  50. package/src/Inputs/SearchInput/_stories/SearchInput.test.story.tsx +5 -3
  51. package/src/Inputs/SelectInput/_stories/SelectInput.rtl.test.story.tsx +108 -79
  52. package/src/Inputs/TextArea/_stories/TextArea.test.story.tsx +10 -10
  53. package/src/InstructionsList/_stories/InstructionsList.test.story.tsx +43 -16
  54. package/src/Label/_stories/Label.test.story.tsx +7 -7
  55. package/src/Link/_stories/Link.test.story.tsx +27 -13
  56. package/src/ListItem/ListItem.css +5 -0
  57. package/src/ListItem/ListItem.less +4 -0
  58. package/src/ListItem/ListItem.test.tsx +17 -0
  59. package/src/ListItem/ListItem.tsx +7 -0
  60. package/src/ListItem/_stories/ListItem.layout.test.story.tsx +23 -0
  61. package/src/ListItem/_stories/ListItem.rtl.test.story.tsx +126 -6
  62. package/src/ListItem/_stories/ListItem.story.tsx +8 -0
  63. package/src/ListItem/_stories/variants/helpers.tsx +174 -126
  64. package/src/Loader/_stories/Loader.test.story.tsx +17 -10
  65. package/src/Modal/_stories/Modal.rtl.test.story.tsx +58 -39
  66. package/src/MoneyInput/_stories/MoneyInput.rtl.test.story.tsx +21 -3
  67. package/src/Nudge/_stories/Nudge.test.story.tsx +70 -21
  68. package/src/PhoneNumberInput/_stories/PhoneNumberInput.rtl.test.story.tsx +1 -1
  69. package/src/Popover/_stories/Popover.rtl.test.story.tsx +92 -49
  70. package/src/ProgressBar/_stories/ProgressBar.test.story.tsx +6 -6
  71. package/src/PromoCard/_stories/PromoCard.test.story.tsx +53 -29
  72. package/src/PromoCard/_stories/PromoCardGroup.test.story.tsx +60 -36
  73. package/src/Prompt/ActionPrompt/_stories/ActionPrompt.test.story.tsx +74 -38
  74. package/src/Prompt/CriticalBanner/_stories/CriticalBanner.test.story.tsx +68 -31
  75. package/src/Prompt/InfoPrompt/_stories/InfoPrompt.test.story.tsx +52 -22
  76. package/src/Prompt/InlinePrompt/_stories/InlinePrompt.test.story.tsx +36 -21
  77. package/src/Radio/_stories/Radio.test.story.tsx +60 -46
  78. package/src/RadioGroup/_stories/RadioGroup.test.story.tsx +70 -51
  79. package/src/Section/_stories/Section.test.story.tsx +30 -14
  80. package/src/SegmentedControl/_stories/SegmentedControl.test.story.tsx +38 -15
  81. package/src/SlidingPanel/_stories/SlidingPanel.rtl.test.story.tsx +58 -34
  82. package/src/Snackbar/Snackbar.test.tsx +16 -0
  83. package/src/Snackbar/Snackbar.tsx +91 -88
  84. package/src/Snackbar/_stories/Snackbar.rtl.test.story.tsx +24 -12
  85. package/src/Stepper/_stories/Stepper.test.story.tsx +27 -9
  86. package/src/Switch/_stories/Switch.test.story.tsx +20 -12
  87. package/src/Table/_stories/Table.test.story.tsx +107 -86
  88. package/src/Tabs/_stories/Tabs.test.story.tsx +92 -52
  89. package/src/Tag/_stories/Tag.test.story.tsx +27 -16
  90. package/src/TextareaWithDisplayFormat/_stories/TextareaWithDisplayFormat.test.story.tsx +4 -4
  91. package/src/Tooltip/_stories/Tooltip.rtl.test.story.tsx +68 -25
  92. package/src/Typeahead/_stories/Typeahead.rtl.test.story.tsx +4 -4
  93. package/src/Upload/_stories/Upload.rtl.test.story.tsx +49 -26
  94. package/src/UploadInput/_stories/UploadInput.test.story.tsx +63 -41
  95. package/src/_deprecated/Alert/_stories/Alert.test.story.tsx +45 -24
  96. package/src/_deprecated/Card/_stories/Card.test.story.tsx +47 -23
  97. package/src/_deprecated/CriticalBanner/_stories/CriticalCommsBanner.test.story.tsx +21 -15
  98. package/src/_deprecated/InlineAlert/_stories/InlineAlert.test.story.tsx +23 -12
  99. package/src/_deprecated/Select/_stories/Select.rtl.test.story.tsx +78 -42
  100. package/src/common/BottomSheet/_stories/BottomSheet.test.story.tsx +38 -20
  101. package/src/common/Panel/_stories/Panel.rtl.test.story.tsx +135 -51
  102. package/src/main.css +5 -0
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
- import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
2
+ import { useRtlDiffI18n, withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
3
3
  import Link from '..';
4
4
 
5
5
  export default {
@@ -11,16 +11,30 @@ export default {
11
11
  type Story = StoryObj<typeof Link>;
12
12
 
13
13
  export const RTLDiff: Story = {
14
- render: () => (
15
- <>
16
- <Link href="#" target="_blank">
17
- External link with navigate-away icon
18
- </Link>
19
- <br />
20
- <Link href="#" target="_blank" type="link-large">
21
- Large external link with navigate-away icon
22
- </Link>
23
- </>
24
- ),
25
- ...withRTLDiff(),
14
+ render: () => {
15
+ const i18n = useRtlDiffI18n();
16
+ return (
17
+ <>
18
+ <Link href="#" target="_blank">
19
+ {i18n.externalLink}
20
+ </Link>
21
+ <br />
22
+ <Link href="#" target="_blank" type="link-large">
23
+ {i18n.largeExternalLink}
24
+ </Link>
25
+ </>
26
+ );
27
+ },
28
+ ...withRTLDiff({
29
+ i18n: {
30
+ externalLink: {
31
+ en: 'External link with navigate-away icon',
32
+ ar: 'رابط خارجي مع أيقونة الانتقال',
33
+ },
34
+ largeExternalLink: {
35
+ en: 'Large external link with navigate-away icon',
36
+ ar: 'رابط خارجي كبير مع أيقونة الانتقال',
37
+ },
38
+ },
39
+ }),
26
40
  };
@@ -397,6 +397,11 @@
397
397
  padding-inline: 0;
398
398
  container-type: inline-size;
399
399
  }
400
+ .wds-list-item-paddingY-24 {
401
+ padding-block: 24px;
402
+ padding-block: var(--size-24);
403
+ padding-inline: 0;
404
+ }
400
405
  .wds-list-item + .wds-list-item-spotlight,
401
406
  .wds-list-item-spotlight + .wds-list-item {
402
407
  margin-block-start: 16px;
@@ -11,6 +11,10 @@
11
11
  padding: var(--size-12) 0;
12
12
  container-type: inline-size;
13
13
 
14
+ &-paddingY-24 {
15
+ padding: var(--size-24) 0;
16
+ }
17
+
14
18
  & + .wds-list-item-spotlight,
15
19
  .wds-list-item-spotlight + & {
16
20
  margin-top: var(--size-16);
@@ -1513,6 +1513,23 @@ describe('ListItem', () => {
1513
1513
  });
1514
1514
  });
1515
1515
 
1516
+ describe('paddingY', () => {
1517
+ it('uses the default vertical padding when not set', () => {
1518
+ render(<ListItem title="Test Title" />);
1519
+ expect(screen.getByRole('listitem')).not.toHaveClass('wds-list-item-paddingY-24');
1520
+ });
1521
+
1522
+ it('uses the default vertical padding when set to 12', () => {
1523
+ render(<ListItem title="Test Title" paddingY={12} />);
1524
+ expect(screen.getByRole('listitem')).not.toHaveClass('wds-list-item-paddingY-24');
1525
+ });
1526
+
1527
+ it('uses the larger vertical padding when set to 24', () => {
1528
+ render(<ListItem title="Test Title" paddingY={24} />);
1529
+ expect(screen.getByRole('listitem')).toHaveClass('wds-list-item-paddingY-24');
1530
+ });
1531
+ });
1532
+
1516
1533
  describe('disable states', () => {
1517
1534
  describe('short-lived', () => {
1518
1535
  it('should override existing prompt if disabled and disabledPromptMessage are set', () => {
@@ -59,6 +59,11 @@ export type ListItemProps = {
59
59
  * Highlights the list item as an action to be taken or already taken. <br />
60
60
  */
61
61
  spotlight?: 'active' | 'inactive';
62
+ /**
63
+ * Vertical padding of the list item, in pixels.
64
+ * @default 12
65
+ */
66
+ paddingY?: 12 | 24;
62
67
  title: ReactNode;
63
68
  subtitle?: ReactNode;
64
69
  /**
@@ -112,6 +117,7 @@ export const ListItem = ({
112
117
  inverted,
113
118
  media,
114
119
  spotlight,
120
+ paddingY = 12,
115
121
  valueTitle,
116
122
  valueSubtitle,
117
123
  control = null,
@@ -232,6 +238,7 @@ export const ListItem = ({
232
238
  'wds-list-item-partially-interactive': isPartiallyInteractive,
233
239
  [`wds-list-item-spotlight wds-list-item-spotlight-${spotlight}`]:
234
240
  isFullyInteractive && !!spotlight,
241
+ 'wds-list-item-paddingY-24': paddingY === 24,
235
242
  disabled: disabled && !isPartiallyInteractive,
236
243
  'disabled--has-prompt-reason':
237
244
  !disabledPromptMessage && disabled && !isPartiallyInteractive,
@@ -1,5 +1,6 @@
1
1
  import { Fragment } from 'react';
2
2
  import { Meta, StoryObj } from '@storybook/react-webpack5';
3
+ import { Plane } from '@transferwise/icons';
3
4
  import List from '../../List';
4
5
  import { ListItem, type ListItemProps } from '../ListItem';
5
6
  import { lorem10, lorem20, lorem5 } from '../../test-utils';
@@ -61,6 +62,28 @@ export const GapsBetweenItems: Story = {
61
62
  },
62
63
  };
63
64
 
65
+ export const PaddingY: Story = {
66
+ render: () => {
67
+ const props = {
68
+ title: lorem5,
69
+ subtitle: lorem10,
70
+ media: <ListItem.AvatarLayout size={32} avatars={[{ asset: <Plane /> }, { asset: '7+' }]} />,
71
+ control: CONTROLS.navigation,
72
+ };
73
+ return (
74
+ <List>
75
+ {([12, 24] as const).map((paddingY) => (
76
+ <Fragment key={paddingY}>
77
+ <ListItem {...props} paddingY={paddingY} />
78
+ <ListItem {...props} paddingY={paddingY} spotlight="active" />
79
+ <ListItem {...props} paddingY={paddingY} spotlight="inactive" />
80
+ </Fragment>
81
+ ))}
82
+ </List>
83
+ );
84
+ },
85
+ };
86
+
64
87
  type PreviewStoryArgs = ListItemProps & {
65
88
  previewImageSize: ListItemMediaSize;
66
89
  previewWithLineGuides: boolean;
@@ -1,24 +1,144 @@
1
+ import { ReactNode } from 'react';
1
2
  import { Meta } from '@storybook/react-webpack5';
2
- import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
3
+ import { useRtlDiffI18n, withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
4
+ import Link from '../../Link';
5
+ import { lorem20 } from '../../test-utils';
3
6
  import { ListItem, type ListItemProps } from '../ListItem';
4
- import { VariantStory } from './variants/helpers';
7
+ import {
8
+ SB_LIST_ITEM_CONTROLS as CONTROLS,
9
+ SB_LIST_ITEM_MEDIA as MEDIA,
10
+ SB_ListItem_ControlType as ControlType,
11
+ } from './subcomponents';
12
+ import { VariantList, type VariantContent, type VariantStoryProps } from './variants/helpers';
13
+
14
+ /** Arabic stand-in for `lorem20`, of comparable word count so wrapping behaviour stays comparable. */
15
+ const arabicFiller =
16
+ 'نص تجريبي يُستخدم لعرض شكل التصميم النهائي قبل إضافة المحتوى الفعلي، ويساعد في التحقق من سلوك التغليف والمحاذاة عند تجاوز السطر الواحد.';
17
+
18
+ const i18nMap = {
19
+ title: { en: 'This is a title', ar: 'هذا عنوان' },
20
+ subtitle: { en: 'And it also has a subtitle', ar: 'وله أيضًا عنوان فرعي' },
21
+ longTitle: { en: `This is a long title. ${lorem20}`, ar: `هذا عنوان طويل. ${arabicFiller}` },
22
+ longSubtitle: {
23
+ en: `And it also has a long subtitle. ${lorem20}`,
24
+ ar: `وله أيضًا عنوان فرعي طويل. ${arabicFiller}`,
25
+ },
26
+ valueTitleLine1: { en: 'This value title', ar: 'عنوان القيمة هذا' },
27
+ valueTitleLine2: { en: 'is made to span', ar: 'مُصمم ليمتد' },
28
+ valueTitleLine3: { en: 'across few lines', ar: 'على عدة أسطر' },
29
+ additionalInfo: {
30
+ en: 'Which is augmented with additional info.',
31
+ ar: 'والذي تمت إضافة معلومات إضافية إليه.',
32
+ },
33
+ additionalInfoAction: { en: 'Details about the subject.', ar: 'تفاصيل حول الموضوع.' },
34
+ prompt: { en: 'Finally, there is a prompt.', ar: 'وأخيرًا، هناك تنبيه.' },
35
+ promptStart: { en: 'Finally, there is a prompt', ar: 'وأخيرًا، هناك تنبيه' },
36
+ promptLink: { en: 'referencing some details', ar: 'يشير إلى بعض التفاصيل' },
37
+ promptEnd: { en: 'for your convenience.', ar: 'لتسهيل الأمر عليك.' },
38
+ controlLabel: { en: 'Click me', ar: 'انقر هنا' },
39
+ };
40
+
41
+ type ListItemI18n = Record<keyof typeof i18nMap, string>;
42
+
43
+ /**
44
+ * Amounts keep Latin digits and ISO currency codes in both columns — a bidi run inside RTL copy is
45
+ * exactly what these stories need to exercise.
46
+ */
47
+ const getContent = (i18n: ListItemI18n): VariantContent => ({
48
+ text: {
49
+ title: i18n.title,
50
+ subtitle: i18n.subtitle,
51
+ valueTitle: '100 GBP',
52
+ valueSubtitle: '200 USD',
53
+ longTitle: i18n.longTitle,
54
+ longSubtitle: i18n.longSubtitle,
55
+ longValueTitle: (
56
+ <>
57
+ {i18n.valueTitleLine1}
58
+ <br />
59
+ {i18n.valueTitleLine2}
60
+ <br />
61
+ {i18n.valueTitleLine3}
62
+ </>
63
+ ),
64
+ },
65
+ media: MEDIA.avatarSingle,
66
+ additionalInfo: {
67
+ interactive: (
68
+ <ListItem.AdditionalInfo
69
+ action={{
70
+ label: i18n.additionalInfoAction,
71
+ href: 'https://wise.com',
72
+ target: '_blank',
73
+ }}
74
+ >
75
+ {i18n.additionalInfo}
76
+ </ListItem.AdditionalInfo>
77
+ ),
78
+ nonInteractive: <ListItem.AdditionalInfo>{i18n.additionalInfo}</ListItem.AdditionalInfo>,
79
+ },
80
+ prompt: {
81
+ interactive: (
82
+ <ListItem.Prompt sentiment="positive">
83
+ {i18n.promptStart}{' '}
84
+ <Link href="https://wise.com" target="_blank" rel="noreferrer">
85
+ {i18n.promptLink}
86
+ </Link>{' '}
87
+ {i18n.promptEnd}
88
+ </ListItem.Prompt>
89
+ ),
90
+ nonInteractive: <ListItem.Prompt sentiment="positive">{i18n.prompt}</ListItem.Prompt>,
91
+ },
92
+ });
93
+
94
+ /** Only the four controls with visible copy need overriding; the rest are icon- or input-only. */
95
+ const getControls = (i18n: ListItemI18n): Record<ControlType, ReactNode> => ({
96
+ ...CONTROLS,
97
+ button: <ListItem.Button onClick={() => {}}>{i18n.controlLabel}</ListItem.Button>,
98
+ buttonAsLink: <ListItem.Button href="https://wise.com">{i18n.controlLabel}</ListItem.Button>,
99
+ partialButton: (
100
+ <ListItem.Button partiallyInteractive onClick={() => {}}>
101
+ {i18n.controlLabel}
102
+ </ListItem.Button>
103
+ ),
104
+ partialButtonAsLink: (
105
+ <ListItem.Button partiallyInteractive href="https://wise.com">
106
+ {i18n.controlLabel}
107
+ </ListItem.Button>
108
+ ),
109
+ });
110
+
111
+ const LocalisedVariantList = ({ controlType, variant }: VariantStoryProps) => {
112
+ const i18n = useRtlDiffI18n<ListItemI18n>();
113
+
114
+ return (
115
+ <VariantList
116
+ controlType={controlType}
117
+ variant={variant}
118
+ content={getContent(i18n)}
119
+ controls={getControls(i18n)}
120
+ />
121
+ );
122
+ };
5
123
 
6
124
  export default {
7
125
  component: ListItem,
8
126
  title: 'Content/ListItem/Tests/RTL Diff',
9
127
  tags: ['!autodocs', '!manifest'],
10
- ...withRTLDiff({ width: 'stretch' }),
128
+ ...withRTLDiff({ width: 'stretch', i18n: i18nMap }),
11
129
  } satisfies Meta<ListItemProps>;
12
130
 
13
131
  const Default = {
14
- ...VariantStory,
132
+ render: ({ controlType, variant }: VariantStoryProps) => (
133
+ <LocalisedVariantList controlType={controlType} variant={variant} />
134
+ ),
15
135
  parameters: { variants: [] },
16
- args: { variant: 'rtl' },
136
+ args: { variant: 'rtl' as const },
17
137
  };
18
138
 
19
139
  export const Button = {
20
140
  ...Default,
21
- args: { ...Default.args, variant: 'button' },
141
+ args: { ...Default.args, controlType: 'button' },
22
142
  };
23
143
  export const ButtonAsLink = {
24
144
  ...Default,
@@ -60,6 +60,7 @@ export default {
60
60
  disabled: false,
61
61
  disabledPromptMessage: undefined,
62
62
  spotlight: undefined,
63
+ paddingY: undefined,
63
64
  inverted: false,
64
65
  valueColumnWidth: undefined,
65
66
  },
@@ -73,6 +74,13 @@ export default {
73
74
  inactive: 'inactive',
74
75
  },
75
76
  },
77
+ paddingY: {
78
+ control: 'radio',
79
+ options: ['unset (undefined)', 12, 24],
80
+ mapping: {
81
+ 'unset (undefined)': undefined,
82
+ },
83
+ },
76
84
  },
77
85
  } satisfies Meta<ListItemProps>;
78
86
 
@@ -1,3 +1,4 @@
1
+ import { ReactNode } from 'react';
1
2
  import { StoryObj } from '@storybook/react-webpack5';
2
3
  import List from '../../../List';
3
4
  import { ListItem, type ListItemProps } from '../../ListItem';
@@ -13,140 +14,187 @@ import {
13
14
  type VariantPartial = { title: string } & Partial<ListItemProps>;
14
15
  type Variant = 'default' | 'dark' | 'rtl' | 'forest-green' | 'bright-green' | 'mobile' | '400%';
15
16
 
16
- const { title, subtitle, valueTitle, valueSubtitle, longTitle, longSubtitle, longValueTitle } =
17
- TEXT;
18
- const prompt = PROMPT.nonInteractive;
19
- const media = MEDIA.avatarSingle;
17
+ const PARTIALLY_INTERACTIVE: ControlType[] = [
18
+ 'partialButton',
19
+ 'partialButtonAsLink',
20
+ 'partialIconButton',
21
+ 'partialIconButtonAsLink',
22
+ ];
20
23
 
21
- type VariantStoryProps = {
24
+ export type VariantText = {
25
+ title: string;
26
+ subtitle: string;
27
+ valueTitle: string;
28
+ valueSubtitle: string;
29
+ longTitle: string;
30
+ longSubtitle: string;
31
+ longValueTitle: ReactNode;
32
+ };
33
+
34
+ /**
35
+ * Every piece of copy and every slot the instance matrix is built from. Extracted so a story can
36
+ * render the same matrix with different content — the RTL diff swaps in localised versions.
37
+ */
38
+ export type VariantContent = {
39
+ text: VariantText;
40
+ media: ReactNode;
41
+ additionalInfo: { interactive: ReactNode; nonInteractive: ReactNode };
42
+ prompt: { interactive: ReactNode; nonInteractive: ReactNode };
43
+ };
44
+
45
+ export const DEFAULT_VARIANT_CONTENT: VariantContent = {
46
+ text: TEXT,
47
+ media: MEDIA.avatarSingle,
48
+ additionalInfo: INFO,
49
+ prompt: PROMPT,
50
+ };
51
+
52
+ export const getVariantInstances = (
53
+ controlType: ControlType,
54
+ { text, media, additionalInfo: info, prompt: prompts }: VariantContent,
55
+ ): VariantPartial[] => {
56
+ const { title, subtitle, valueTitle, valueSubtitle, longTitle, longSubtitle, longValueTitle } =
57
+ text;
58
+ const isInteractive = PARTIALLY_INTERACTIVE.includes(controlType);
59
+ const additionalInfo = isInteractive ? info.interactive : info.nonInteractive;
60
+ const prompt = prompts.nonInteractive;
61
+
62
+ return [
63
+ { title },
64
+ { title: longTitle },
65
+ { title, valueTitle },
66
+ { title, valueTitle: longValueTitle },
67
+ { title: longTitle, valueTitle },
68
+ { title, subtitle },
69
+ { title, subtitle: longSubtitle },
70
+ { title, additionalInfo },
71
+ { title, valueTitle, valueSubtitle },
72
+ { title, valueTitle: longValueTitle, valueSubtitle },
73
+ { title, subtitle, inverted: true },
74
+ { title, subtitle, valueTitle },
75
+ { title, subtitle, valueTitle, inverted: true },
76
+ { title, subtitle, valueTitle, valueSubtitle },
77
+ {
78
+ title: longTitle,
79
+ subtitle: longSubtitle,
80
+ valueTitle,
81
+ valueSubtitle,
82
+ additionalInfo: info.nonInteractive,
83
+ },
84
+ { title, subtitle, valueTitle, valueSubtitle, inverted: true },
85
+ { media, title },
86
+ { media, title: longTitle },
87
+ { media, title, valueTitle },
88
+ { media, title: longTitle, valueTitle },
89
+ { media, title: longTitle, valueSubtitle },
90
+ { media, title, valueTitle: longValueTitle },
91
+ { media, title, valueTitle: longValueTitle, valueSubtitle },
92
+ { media, title, subtitle },
93
+ { media, title: longTitle, subtitle },
94
+ { media, title, valueTitle, valueSubtitle },
95
+ { media, title, valueTitle },
96
+ { media, title, valueSubtitle },
97
+ { media, title, subtitle },
98
+ { media, title, additionalInfo },
99
+ { media, title, subtitle, valueTitle },
100
+ { media, title, subtitle, valueTitle, valueSubtitle },
101
+ { media, title, subtitle, additionalInfo: info.nonInteractive },
102
+ isInteractive ? { media, title, subtitle, additionalInfo: info.interactive } : null,
103
+ { media, title, subtitle, additionalInfo, valueTitle },
104
+ { media, title, subtitle, additionalInfo, valueTitle, valueSubtitle },
105
+ {
106
+ media,
107
+ title: longTitle,
108
+ subtitle: longSubtitle,
109
+ valueTitle,
110
+ valueSubtitle,
111
+ additionalInfo: info.nonInteractive,
112
+ },
113
+ { media, title, subtitle, additionalInfo, valueTitle, valueSubtitle, prompt },
114
+ {
115
+ media,
116
+ title,
117
+ subtitle,
118
+ additionalInfo,
119
+ valueTitle,
120
+ valueSubtitle,
121
+ prompt: prompts.interactive,
122
+ },
123
+ {
124
+ media,
125
+ title,
126
+ subtitle,
127
+ additionalInfo,
128
+ valueTitle,
129
+ valueSubtitle,
130
+ prompt: prompts.interactive,
131
+ inverted: true,
132
+ },
133
+ {
134
+ media,
135
+ title,
136
+ subtitle,
137
+ additionalInfo,
138
+ valueTitle,
139
+ valueSubtitle,
140
+ prompt: prompts.interactive,
141
+ spotlight: 'active',
142
+ },
143
+ {
144
+ media,
145
+ title,
146
+ subtitle,
147
+ additionalInfo,
148
+ valueTitle,
149
+ valueSubtitle,
150
+ prompt: prompts.interactive,
151
+ spotlight: 'inactive',
152
+ },
153
+ {
154
+ media,
155
+ title,
156
+ subtitle,
157
+ additionalInfo,
158
+ valueTitle,
159
+ valueSubtitle,
160
+ prompt: prompts.interactive,
161
+ spotlight: 'inactive',
162
+ disabled: true,
163
+ },
164
+ ].filter(Boolean) as VariantPartial[];
165
+ };
166
+
167
+ export type VariantStoryProps = {
22
168
  controlType: ControlType;
23
169
  variant?: Variant;
24
170
  };
25
171
 
172
+ type VariantListProps = VariantStoryProps & {
173
+ content?: VariantContent;
174
+ controls?: Record<ControlType, ReactNode>;
175
+ };
176
+
177
+ export const VariantList = ({
178
+ controlType,
179
+ variant = 'default',
180
+ content = DEFAULT_VARIANT_CONTENT,
181
+ controls = CONTROLS,
182
+ }: VariantListProps) => (
183
+ <List>
184
+ {getVariantInstances(controlType, content).map((instance, index) => (
185
+ <ListItem
186
+ key={`${variant}-${controlType}-${index}`}
187
+ control={controls[controlType]}
188
+ {...instance}
189
+ />
190
+ ))}
191
+ </List>
192
+ );
193
+
26
194
  type VariantStory = StoryObj<VariantStoryProps>;
27
195
 
28
196
  export const VariantStory: VariantStory = {
29
- render: ({ controlType, variant = 'default' }) => {
30
- const control = CONTROLS[controlType];
31
- const isInteractive = [
32
- 'partialButton',
33
- 'partialButtonAsLink',
34
- 'partialIconButton',
35
- 'partialIconButtonAsLink',
36
- ].includes(controlType);
37
- const additionalInfo = isInteractive ? INFO.interactive : INFO.nonInteractive;
38
- const instances = [
39
- { title },
40
- { title: longTitle },
41
- { title, valueTitle },
42
- { title, valueTitle: longValueTitle },
43
- { title: longTitle, valueTitle },
44
- { title, subtitle },
45
- { title, subtitle: longSubtitle },
46
- { title, additionalInfo },
47
- { title, valueTitle, valueSubtitle },
48
- { title, valueTitle: longValueTitle, valueSubtitle },
49
- { title, subtitle, inverted: true },
50
- { title, subtitle, valueTitle },
51
- { title, subtitle, valueTitle, inverted: true },
52
- { title, subtitle, valueTitle, valueSubtitle },
53
- {
54
- title: longTitle,
55
- subtitle: longSubtitle,
56
- valueTitle,
57
- valueSubtitle,
58
- additionalInfo: INFO.nonInteractive,
59
- },
60
- { title, subtitle, valueTitle, valueSubtitle, inverted: true },
61
- { media, title },
62
- { media, title: longTitle },
63
- { media, title, valueTitle },
64
- { media, title: longTitle, valueTitle },
65
- { media, title: longTitle, valueSubtitle },
66
- { media, title, valueTitle: longValueTitle },
67
- { media, title, valueTitle: longValueTitle, valueSubtitle },
68
- { media, title, subtitle },
69
- { media, title: longTitle, subtitle },
70
- { media, title, valueTitle, valueSubtitle },
71
- { media, title, valueTitle },
72
- { media, title, valueSubtitle },
73
- { media, title, subtitle },
74
- { media, title, additionalInfo },
75
- { media, title, subtitle, valueTitle },
76
- { media, title, subtitle, valueTitle, valueSubtitle },
77
- { media, title, subtitle, additionalInfo: INFO.nonInteractive },
78
- isInteractive ? { media, title, subtitle, additionalInfo: INFO.interactive } : null,
79
- { media, title, subtitle, additionalInfo, valueTitle },
80
- { media, title, subtitle, additionalInfo, valueTitle, valueSubtitle },
81
- {
82
- media,
83
- title: longTitle,
84
- subtitle: longSubtitle,
85
- valueTitle,
86
- valueSubtitle,
87
- additionalInfo: INFO.nonInteractive,
88
- },
89
- { media, title, subtitle, additionalInfo, valueTitle, valueSubtitle, prompt },
90
- {
91
- media,
92
- title,
93
- subtitle,
94
- additionalInfo,
95
- valueTitle,
96
- valueSubtitle,
97
- prompt: PROMPT.interactive,
98
- },
99
- {
100
- media,
101
- title,
102
- subtitle,
103
- additionalInfo,
104
- valueTitle,
105
- valueSubtitle,
106
- prompt: PROMPT.interactive,
107
- inverted: true,
108
- },
109
- {
110
- media,
111
- title,
112
- subtitle,
113
- additionalInfo,
114
- valueTitle,
115
- valueSubtitle,
116
- prompt: PROMPT.interactive,
117
- spotlight: 'active',
118
- },
119
- {
120
- media,
121
- title,
122
- subtitle,
123
- additionalInfo,
124
- valueTitle,
125
- valueSubtitle,
126
- prompt: PROMPT.interactive,
127
- spotlight: 'inactive',
128
- },
129
- {
130
- media,
131
- title,
132
- subtitle,
133
- additionalInfo,
134
- valueTitle,
135
- valueSubtitle,
136
- prompt: PROMPT.interactive,
137
- spotlight: 'inactive',
138
- disabled: true,
139
- },
140
- ].filter(Boolean) as VariantPartial[];
141
-
142
- return (
143
- <List>
144
- {instances.map((instance, index) => (
145
- <ListItem key={`${variant}-${controlType}-${index}`} control={control} {...instance} />
146
- ))}
147
- </List>
148
- );
149
- },
197
+ render: ({ controlType, variant }) => <VariantList controlType={controlType} variant={variant} />,
150
198
  parameters: {
151
199
  variants: ['default'],
152
200
  },
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
- import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
2
+ import { useRtlDiffI18n, withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
3
3
  import Loader from '..';
4
4
 
5
5
  export default {
@@ -11,13 +11,20 @@ export default {
11
11
  type Story = StoryObj<typeof Loader>;
12
12
 
13
13
  export const RTLDiff: Story = {
14
- render: () => (
15
- <>
16
- <Loader size="sm" displayInstantly />
17
- <Loader size="md" displayInstantly />
18
- <Loader size="sm" displayInstantly label="Sending your money" />
19
- <Loader size="md" displayInstantly label="Sending your money" />
20
- </>
21
- ),
22
- ...withRTLDiff(),
14
+ render: () => {
15
+ const i18n = useRtlDiffI18n();
16
+ return (
17
+ <>
18
+ <Loader size="sm" displayInstantly />
19
+ <Loader size="md" displayInstantly />
20
+ <Loader size="sm" displayInstantly label={i18n.label} />
21
+ <Loader size="md" displayInstantly label={i18n.label} />
22
+ </>
23
+ );
24
+ },
25
+ ...withRTLDiff({
26
+ i18n: {
27
+ label: { en: 'Sending your money', ar: 'جارٍ إرسال أموالك' },
28
+ },
29
+ }),
23
30
  };