@transferwise/components 46.103.1 → 46.105.0

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 (250) hide show
  1. package/build/header/Header.js +60 -43
  2. package/build/header/Header.js.map +1 -1
  3. package/build/header/Header.mjs +57 -43
  4. package/build/header/Header.mjs.map +1 -1
  5. package/build/i18n/cs.json +2 -0
  6. package/build/i18n/cs.json.js +2 -0
  7. package/build/i18n/cs.json.js.map +1 -1
  8. package/build/i18n/cs.json.mjs +2 -0
  9. package/build/i18n/cs.json.mjs.map +1 -1
  10. package/build/i18n/es.json +2 -0
  11. package/build/i18n/es.json.js +2 -0
  12. package/build/i18n/es.json.js.map +1 -1
  13. package/build/i18n/es.json.mjs +2 -0
  14. package/build/i18n/es.json.mjs.map +1 -1
  15. package/build/i18n/th.json +2 -0
  16. package/build/i18n/th.json.js +2 -0
  17. package/build/i18n/th.json.js.map +1 -1
  18. package/build/i18n/th.json.mjs +2 -0
  19. package/build/i18n/th.json.mjs.map +1 -1
  20. package/build/index.js +3 -1
  21. package/build/index.js.map +1 -1
  22. package/build/index.mjs +2 -1
  23. package/build/index.mjs.map +1 -1
  24. package/build/inputs/SelectInput.js +1 -1
  25. package/build/inputs/SelectInput.js.map +1 -1
  26. package/build/inputs/SelectInput.mjs +1 -1
  27. package/build/listItem/AdditionalInfo/ListItemAdditionalInfo.js +56 -0
  28. package/build/listItem/AdditionalInfo/ListItemAdditionalInfo.js.map +1 -0
  29. package/build/listItem/AdditionalInfo/ListItemAdditionalInfo.mjs +54 -0
  30. package/build/listItem/AdditionalInfo/ListItemAdditionalInfo.mjs.map +1 -0
  31. package/build/listItem/AvatarLayout/ListItemAvatarLayout.js +23 -0
  32. package/build/listItem/AvatarLayout/ListItemAvatarLayout.js.map +1 -0
  33. package/build/listItem/AvatarLayout/ListItemAvatarLayout.mjs +21 -0
  34. package/build/listItem/AvatarLayout/ListItemAvatarLayout.mjs.map +1 -0
  35. package/build/listItem/AvatarView/ListItemAvatarView.js +23 -0
  36. package/build/listItem/AvatarView/ListItemAvatarView.js.map +1 -0
  37. package/build/listItem/AvatarView/ListItemAvatarView.mjs +21 -0
  38. package/build/listItem/AvatarView/ListItemAvatarView.mjs.map +1 -0
  39. package/build/listItem/Button/ListItemButton.js +43 -0
  40. package/build/listItem/Button/ListItemButton.js.map +1 -0
  41. package/build/listItem/Button/ListItemButton.mjs +41 -0
  42. package/build/listItem/Button/ListItemButton.mjs.map +1 -0
  43. package/build/listItem/Checkbox/ListItemCheckbox.js +30 -0
  44. package/build/listItem/Checkbox/ListItemCheckbox.js.map +1 -0
  45. package/build/listItem/Checkbox/ListItemCheckbox.mjs +28 -0
  46. package/build/listItem/Checkbox/ListItemCheckbox.mjs.map +1 -0
  47. package/build/listItem/IconButton/ListItemIconButton.js +56 -0
  48. package/build/listItem/IconButton/ListItemIconButton.js.map +1 -0
  49. package/build/listItem/IconButton/ListItemIconButton.mjs +54 -0
  50. package/build/listItem/IconButton/ListItemIconButton.mjs.map +1 -0
  51. package/build/listItem/Image/ListItemImage.js +31 -0
  52. package/build/listItem/Image/ListItemImage.js.map +1 -0
  53. package/build/listItem/Image/ListItemImage.mjs +29 -0
  54. package/build/listItem/Image/ListItemImage.mjs.map +1 -0
  55. package/build/listItem/ListItem.js +311 -0
  56. package/build/listItem/ListItem.js.map +1 -0
  57. package/build/listItem/ListItem.mjs +306 -0
  58. package/build/listItem/ListItem.mjs.map +1 -0
  59. package/build/listItem/ListItemContext.js +8 -0
  60. package/build/listItem/ListItemContext.js.map +1 -0
  61. package/build/listItem/ListItemContext.mjs +6 -0
  62. package/build/listItem/ListItemContext.mjs.map +1 -0
  63. package/build/listItem/Navigation/ListItemNavigation.js +44 -0
  64. package/build/listItem/Navigation/ListItemNavigation.js.map +1 -0
  65. package/build/listItem/Navigation/ListItemNavigation.mjs +42 -0
  66. package/build/listItem/Navigation/ListItemNavigation.mjs.map +1 -0
  67. package/build/listItem/Prompt/ListItemPrompt.js +59 -0
  68. package/build/listItem/Prompt/ListItemPrompt.js.map +1 -0
  69. package/build/listItem/Prompt/ListItemPrompt.mjs +54 -0
  70. package/build/listItem/Prompt/ListItemPrompt.mjs.map +1 -0
  71. package/build/listItem/Radio/ListItemRadio.js +30 -0
  72. package/build/listItem/Radio/ListItemRadio.js.map +1 -0
  73. package/build/listItem/Radio/ListItemRadio.mjs +28 -0
  74. package/build/listItem/Radio/ListItemRadio.mjs.map +1 -0
  75. package/build/listItem/Switch/ListItemSwitch.js +30 -0
  76. package/build/listItem/Switch/ListItemSwitch.js.map +1 -0
  77. package/build/listItem/Switch/ListItemSwitch.mjs +28 -0
  78. package/build/listItem/Switch/ListItemSwitch.mjs.map +1 -0
  79. package/build/listItem/useListItemControl.js +22 -0
  80. package/build/listItem/useListItemControl.js.map +1 -0
  81. package/build/listItem/useListItemControl.mjs +20 -0
  82. package/build/listItem/useListItemControl.mjs.map +1 -0
  83. package/build/listItem/useListItemMedia.js +21 -0
  84. package/build/listItem/useListItemMedia.js.map +1 -0
  85. package/build/listItem/useListItemMedia.mjs +19 -0
  86. package/build/listItem/useListItemMedia.mjs.map +1 -0
  87. package/build/main.css +794 -14
  88. package/build/styles/header/Header.css +21 -14
  89. package/build/styles/listItem/ListItem.css +773 -0
  90. package/build/styles/listItem/ListItem.grid.css +370 -0
  91. package/build/styles/listItem/Prompt/ListItemPrompt.css +157 -0
  92. package/build/styles/main.css +794 -14
  93. package/build/title/Title.js +10 -4
  94. package/build/title/Title.js.map +1 -1
  95. package/build/title/Title.mjs +6 -4
  96. package/build/title/Title.mjs.map +1 -1
  97. package/build/types/header/Header.d.ts +27 -11
  98. package/build/types/header/Header.d.ts.map +1 -1
  99. package/build/types/header/index.d.ts +1 -0
  100. package/build/types/header/index.d.ts.map +1 -1
  101. package/build/types/index.d.ts +3 -0
  102. package/build/types/index.d.ts.map +1 -1
  103. package/build/types/listItem/AdditionalInfo/ListItemAdditionalInfo.d.ts +15 -0
  104. package/build/types/listItem/AdditionalInfo/ListItemAdditionalInfo.d.ts.map +1 -0
  105. package/build/types/listItem/AdditionalInfo/index.d.ts +3 -0
  106. package/build/types/listItem/AdditionalInfo/index.d.ts.map +1 -0
  107. package/build/types/listItem/AvatarLayout/ListItemAvatarLayout.d.ts +18 -0
  108. package/build/types/listItem/AvatarLayout/ListItemAvatarLayout.d.ts.map +1 -0
  109. package/build/types/listItem/AvatarLayout/index.d.ts +3 -0
  110. package/build/types/listItem/AvatarLayout/index.d.ts.map +1 -0
  111. package/build/types/listItem/AvatarView/ListItemAvatarView.d.ts +16 -0
  112. package/build/types/listItem/AvatarView/ListItemAvatarView.d.ts.map +1 -0
  113. package/build/types/listItem/AvatarView/index.d.ts +3 -0
  114. package/build/types/listItem/AvatarView/index.d.ts.map +1 -0
  115. package/build/types/listItem/Button/ListItemButton.d.ts +20 -0
  116. package/build/types/listItem/Button/ListItemButton.d.ts.map +1 -0
  117. package/build/types/listItem/Button/index.d.ts +3 -0
  118. package/build/types/listItem/Button/index.d.ts.map +1 -0
  119. package/build/types/listItem/Checkbox/ListItemCheckbox.d.ts +14 -0
  120. package/build/types/listItem/Checkbox/ListItemCheckbox.d.ts.map +1 -0
  121. package/build/types/listItem/Checkbox/index.d.ts +3 -0
  122. package/build/types/listItem/Checkbox/index.d.ts.map +1 -0
  123. package/build/types/listItem/IconButton/ListItemIconButton.d.ts +18 -0
  124. package/build/types/listItem/IconButton/ListItemIconButton.d.ts.map +1 -0
  125. package/build/types/listItem/IconButton/index.d.ts +3 -0
  126. package/build/types/listItem/IconButton/index.d.ts.map +1 -0
  127. package/build/types/listItem/Image/ListItemImage.d.ts +25 -0
  128. package/build/types/listItem/Image/ListItemImage.d.ts.map +1 -0
  129. package/build/types/listItem/Image/index.d.ts +3 -0
  130. package/build/types/listItem/Image/index.d.ts.map +1 -0
  131. package/build/types/listItem/ListItem.d.ts +111 -0
  132. package/build/types/listItem/ListItem.d.ts.map +1 -0
  133. package/build/types/listItem/ListItemContext.d.ts +21 -0
  134. package/build/types/listItem/ListItemContext.d.ts.map +1 -0
  135. package/build/types/listItem/Navigation/ListItemNavigation.d.ts +15 -0
  136. package/build/types/listItem/Navigation/ListItemNavigation.d.ts.map +1 -0
  137. package/build/types/listItem/Navigation/index.d.ts +3 -0
  138. package/build/types/listItem/Navigation/index.d.ts.map +1 -0
  139. package/build/types/listItem/Prompt/ListItemPrompt.d.ts +16 -0
  140. package/build/types/listItem/Prompt/ListItemPrompt.d.ts.map +1 -0
  141. package/build/types/listItem/Prompt/index.d.ts +3 -0
  142. package/build/types/listItem/Prompt/index.d.ts.map +1 -0
  143. package/build/types/listItem/Radio/ListItemRadio.d.ts +14 -0
  144. package/build/types/listItem/Radio/ListItemRadio.d.ts.map +1 -0
  145. package/build/types/listItem/Radio/index.d.ts +3 -0
  146. package/build/types/listItem/Radio/index.d.ts.map +1 -0
  147. package/build/types/listItem/Switch/ListItemSwitch.d.ts +14 -0
  148. package/build/types/listItem/Switch/ListItemSwitch.d.ts.map +1 -0
  149. package/build/types/listItem/Switch/index.d.ts +3 -0
  150. package/build/types/listItem/Switch/index.d.ts.map +1 -0
  151. package/build/types/listItem/_stories/helpers.d.ts +27 -0
  152. package/build/types/listItem/_stories/helpers.d.ts.map +1 -0
  153. package/build/types/listItem/_stories/subcomponents.d.ts +18 -0
  154. package/build/types/listItem/_stories/subcomponents.d.ts.map +1 -0
  155. package/build/types/listItem/index.d.ts +14 -0
  156. package/build/types/listItem/index.d.ts.map +1 -0
  157. package/build/types/listItem/test-utils.d.ts +7 -0
  158. package/build/types/listItem/test-utils.d.ts.map +1 -0
  159. package/build/types/listItem/useListItemControl.d.ts +5 -0
  160. package/build/types/listItem/useListItemControl.d.ts.map +1 -0
  161. package/build/types/listItem/useListItemMedia.d.ts +6 -0
  162. package/build/types/listItem/useListItemMedia.d.ts.map +1 -0
  163. package/build/types/title/Title.d.ts +4 -5
  164. package/build/types/title/Title.d.ts.map +1 -1
  165. package/package.json +3 -3
  166. package/src/button/Button.spec.tsx +25 -1
  167. package/src/button/Button.story.tsx +1 -0
  168. package/src/header/Header.accessibility.docs.mdx +85 -0
  169. package/src/header/Header.css +21 -14
  170. package/src/header/Header.less +17 -10
  171. package/src/header/Header.spec.tsx +68 -50
  172. package/src/header/Header.story.tsx +190 -36
  173. package/src/header/Header.tsx +96 -65
  174. package/src/header/index.ts +1 -0
  175. package/src/i18n/cs.json +2 -0
  176. package/src/i18n/es.json +2 -0
  177. package/src/i18n/th.json +2 -0
  178. package/src/iconButton/iconButton.spec.tsx +31 -0
  179. package/src/index.ts +16 -0
  180. package/src/legacylistItem/LegacyListItem.story.tsx +1 -1
  181. package/src/legacylistItem/LegacyListItem.tests.story.tsx +2 -1
  182. package/src/list/List.story.tsx +13 -3
  183. package/src/listItem/AdditionalInfo/ListItemAdditionalInfo.spec.tsx +56 -0
  184. package/src/listItem/AdditionalInfo/ListItemAdditionalInfo.story.tsx +198 -0
  185. package/src/listItem/AdditionalInfo/ListItemAdditionalInfo.tsx +36 -0
  186. package/src/listItem/AdditionalInfo/index.ts +2 -0
  187. package/src/listItem/AvatarLayout/ListItemAvatarLayout.spec.tsx +59 -0
  188. package/src/listItem/AvatarLayout/ListItemAvatarLayout.story.tsx +124 -0
  189. package/src/listItem/AvatarLayout/ListItemAvatarLayout.tsx +27 -0
  190. package/src/listItem/AvatarLayout/index.ts +2 -0
  191. package/src/listItem/AvatarView/ListItemAvatarView.spec.tsx +75 -0
  192. package/src/listItem/AvatarView/ListItemAvatarView.story.tsx +339 -0
  193. package/src/listItem/AvatarView/ListItemAvatarView.tsx +27 -0
  194. package/src/listItem/AvatarView/index.ts +2 -0
  195. package/src/listItem/Button/ListItemButton.spec.tsx +90 -0
  196. package/src/listItem/Button/ListItemButton.story.tsx +473 -0
  197. package/src/listItem/Button/ListItemButton.tsx +56 -0
  198. package/src/listItem/Button/index.ts +2 -0
  199. package/src/listItem/Checkbox/ListItemCheckbox.spec.tsx +82 -0
  200. package/src/listItem/Checkbox/ListItemCheckbox.story.tsx +128 -0
  201. package/src/listItem/Checkbox/ListItemCheckbox.tsx +33 -0
  202. package/src/listItem/Checkbox/index.ts +2 -0
  203. package/src/listItem/IconButton/ListItemIconButton.spec.tsx +131 -0
  204. package/src/listItem/IconButton/ListItemIconButton.story.tsx +284 -0
  205. package/src/listItem/IconButton/ListItemIconButton.tsx +73 -0
  206. package/src/listItem/IconButton/index.ts +2 -0
  207. package/src/listItem/Image/ListItemImage.spec.tsx +30 -0
  208. package/src/listItem/Image/ListItemImage.story.tsx +80 -0
  209. package/src/listItem/Image/ListItemImage.tsx +46 -0
  210. package/src/listItem/Image/index.ts +2 -0
  211. package/src/listItem/ListItem.css +773 -0
  212. package/src/listItem/ListItem.grid.css +370 -0
  213. package/src/listItem/ListItem.grid.less +622 -0
  214. package/src/listItem/ListItem.less +291 -0
  215. package/src/listItem/ListItem.spec.tsx +1511 -0
  216. package/src/listItem/ListItem.tsx +440 -0
  217. package/src/listItem/ListItemContext.tsx +26 -0
  218. package/src/listItem/Navigation/ListItemNavigation.spec.tsx +67 -0
  219. package/src/listItem/Navigation/ListItemNavigation.story.tsx +114 -0
  220. package/src/listItem/Navigation/ListItemNavigation.tsx +39 -0
  221. package/src/listItem/Navigation/index.ts +2 -0
  222. package/src/listItem/Prompt/ListItemPrompt.css +157 -0
  223. package/src/listItem/Prompt/ListItemPrompt.less +134 -0
  224. package/src/listItem/Prompt/ListItemPrompt.spec.tsx +36 -0
  225. package/src/listItem/Prompt/ListItemPrompt.story.tsx +204 -0
  226. package/src/listItem/Prompt/ListItemPrompt.tsx +32 -0
  227. package/src/listItem/Prompt/index.ts +2 -0
  228. package/src/listItem/Radio/ListItemRadio.spec.tsx +66 -0
  229. package/src/listItem/Radio/ListItemRadio.story.tsx +111 -0
  230. package/src/listItem/Radio/ListItemRadio.tsx +33 -0
  231. package/src/listItem/Radio/index.ts +2 -0
  232. package/src/listItem/Switch/ListItemSwitch.spec.tsx +47 -0
  233. package/src/listItem/Switch/ListItemSwitch.story.tsx +79 -0
  234. package/src/listItem/Switch/ListItemSwitch.tsx +33 -0
  235. package/src/listItem/Switch/index.ts +2 -0
  236. package/src/listItem/_stories/ListItem.focus.test.story.tsx +265 -0
  237. package/src/listItem/_stories/ListItem.layout.test.story.tsx +374 -0
  238. package/src/listItem/_stories/ListItem.scenarios.story.tsx +228 -0
  239. package/src/listItem/_stories/ListItem.story.tsx +774 -0
  240. package/src/listItem/_stories/ListItem.variants.test.story.tsx +274 -0
  241. package/src/listItem/_stories/helpers.tsx +53 -0
  242. package/src/listItem/_stories/subcomponents.tsx +141 -0
  243. package/src/listItem/index.ts +14 -0
  244. package/src/listItem/test-utils.tsx +33 -0
  245. package/src/listItem/useListItemControl.tsx +18 -0
  246. package/src/listItem/useListItemMedia.tsx +16 -0
  247. package/src/main.css +794 -14
  248. package/src/main.less +1 -0
  249. package/src/primitives/PrimitiveAnchor/test/PrimitiveAnchor.spec.tsx +15 -4
  250. package/src/title/Title.tsx +25 -12
@@ -0,0 +1,374 @@
1
+ import { Fragment } from 'react';
2
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
3
+ import { Bank, FastFlag, MultiCurrency, Receipt, Savings } from '@transferwise/icons';
4
+ import Link from '../../link';
5
+ import List from '../../list';
6
+ import { ListItem, type ListItemProps } from '../ListItem';
7
+ import { lorem10, lorem20, lorem5 } from '../../test-utils';
8
+ import portraitImage from '../../test-utils/assets/placeholder-landscape.svg';
9
+ import landscapeImage from '../../test-utils/assets/placeholder-portrait.svg';
10
+ import { SB_LIST_ITEM_CONTROLS as CONTROLS, SB_LIST_ITEM_MEDIA as MEDIA } from './subcomponents';
11
+ import { ListItemMediaSize } from '../ListItemContext';
12
+
13
+ const withSizedContainer = (width: number) => (Story: any) => (
14
+ <List
15
+ className="list-unstyled"
16
+ style={{
17
+ width,
18
+ display: 'flex',
19
+ flexDirection: 'column',
20
+ gap: 16,
21
+ }}
22
+ >
23
+ <Story />
24
+ </List>
25
+ );
26
+
27
+ export default {
28
+ title: 'Content/ListItem/tests/layout',
29
+ tags: ['!autodocs'],
30
+ parameters: {
31
+ controls: { disable: true },
32
+ actions: { disable: true },
33
+ a11y: { disable: true },
34
+ knobs: { disable: true },
35
+ },
36
+ } satisfies Meta<ListItemProps>;
37
+ type Story = StoryObj<ListItemProps>;
38
+
39
+ const variants = [
40
+ <ListItem
41
+ key="button"
42
+ title="Button only"
43
+ control={
44
+ <ListItem.Button priority="secondary-neutral" onClick={() => {}}>
45
+ Click me
46
+ </ListItem.Button>
47
+ }
48
+ />,
49
+ <ListItem
50
+ key="media"
51
+ title="With media"
52
+ subtitle="Short subtitle"
53
+ additionalInfo={<ListItem.AdditionalInfo>{lorem5}</ListItem.AdditionalInfo>}
54
+ media={
55
+ <ListItem.AvatarView>
56
+ <FastFlag />
57
+ </ListItem.AvatarView>
58
+ }
59
+ />,
60
+ <ListItem key="value" title="With value" valueTitle="100 GBP" valueSubtitle="100 USD" />,
61
+ <ListItem
62
+ key="prompt"
63
+ title="With prompt"
64
+ prompt={
65
+ <ListItem.Prompt sentiment="positive">
66
+ This is a prompt with <Link href="https://wise.com">a link</Link>.
67
+ </ListItem.Prompt>
68
+ }
69
+ />,
70
+ <ListItem
71
+ key="button-media"
72
+ title="Button + media"
73
+ media={
74
+ <ListItem.AvatarView>
75
+ <MultiCurrency />
76
+ </ListItem.AvatarView>
77
+ }
78
+ control={
79
+ <ListItem.Button priority="primary" onClick={() => {}}>
80
+ Action
81
+ </ListItem.Button>
82
+ }
83
+ />,
84
+ <ListItem
85
+ key="button-value"
86
+ title="Button + value"
87
+ valueTitle="42 EUR"
88
+ control={
89
+ <ListItem.Button priority="secondary-neutral" onClick={() => {}}>
90
+ Pay
91
+ </ListItem.Button>
92
+ }
93
+ />,
94
+ <ListItem
95
+ key="media-value"
96
+ valueColumnWidth={80}
97
+ title="Media + value"
98
+ subtitle="this column is 20% wide"
99
+ media={
100
+ <ListItem.AvatarView>
101
+ <Bank />
102
+ </ListItem.AvatarView>
103
+ }
104
+ valueTitle="1,000 USD"
105
+ />,
106
+ <ListItem
107
+ key="prompt-value"
108
+ title="Prompt + value"
109
+ valueTitle="10 GBP"
110
+ prompt={<ListItem.Prompt sentiment="warning">Warning prompt!</ListItem.Prompt>}
111
+ />,
112
+ <ListItem
113
+ key="media-prompt"
114
+ title="Media + prompt"
115
+ media={
116
+ <ListItem.AvatarView>
117
+ <Receipt />
118
+ </ListItem.AvatarView>
119
+ }
120
+ prompt={<ListItem.Prompt sentiment="positive">Discount available!</ListItem.Prompt>}
121
+ />,
122
+ <ListItem
123
+ key="most"
124
+ title="Everything but button"
125
+ subtitle="No control"
126
+ media={
127
+ <ListItem.AvatarView>
128
+ <Savings />
129
+ </ListItem.AvatarView>
130
+ }
131
+ valueTitle="999 GBP"
132
+ prompt={
133
+ <ListItem.Prompt sentiment="positive">
134
+ <Link href="https://wise.com">See details</Link>
135
+ </ListItem.Prompt>
136
+ }
137
+ />,
138
+ <ListItem
139
+ key="all"
140
+ title="All together"
141
+ subtitle="Everything in one"
142
+ media={
143
+ <ListItem.AvatarView>
144
+ <Savings />
145
+ </ListItem.AvatarView>
146
+ }
147
+ valueTitle="999 GBP"
148
+ prompt={
149
+ <ListItem.Prompt sentiment="positive">
150
+ <Link href="https://wise.com">See details</Link>
151
+ </ListItem.Prompt>
152
+ }
153
+ control={
154
+ <ListItem.Button priority="secondary-neutral" onClick={() => {}}>
155
+ Go
156
+ </ListItem.Button>
157
+ }
158
+ />,
159
+ ];
160
+
161
+ export const Under320: Story = {
162
+ render: () => <>{variants}</>,
163
+ decorators: [withSizedContainer(320)],
164
+ };
165
+
166
+ export const Between321And399: Story = {
167
+ render: () => <>{variants}</>,
168
+ decorators: [withSizedContainer(360)],
169
+ };
170
+
171
+ export const Over400: Story = {
172
+ render: () => <>{variants}</>,
173
+ decorators: [withSizedContainer(400)],
174
+ };
175
+
176
+ export const LongButton: Story = {
177
+ render: () => (
178
+ <ListItem
179
+ title="Additional info button better align left"
180
+ additionalInfo={
181
+ <ListItem.AdditionalInfo
182
+ action={{
183
+ label: 'Additional info Additional info Additional info (as button)',
184
+ onClick: () => {},
185
+ }}
186
+ />
187
+ }
188
+ control={
189
+ <ListItem.Button onClick={() => {}}>Click me Click me Click me Click me</ListItem.Button>
190
+ }
191
+ />
192
+ ),
193
+ decorators: [withSizedContainer(400)],
194
+ };
195
+
196
+ export const GapsBetweenItems: Story = {
197
+ render: () => {
198
+ const props = {
199
+ title: lorem5,
200
+ subtitle: lorem10,
201
+ media: MEDIA.image,
202
+ control: CONTROLS.switch,
203
+ };
204
+ return (
205
+ <List>
206
+ <ListItem {...props} />
207
+ <ListItem {...props} spotlight="active" />
208
+ <ListItem {...props} spotlight="inactive" />
209
+ <ListItem {...props} />
210
+ </List>
211
+ );
212
+ },
213
+ };
214
+
215
+ type PreviewStoryArgs = ListItemProps & {
216
+ previewImageSize: ListItemMediaSize;
217
+ previewWithLineGuides: boolean;
218
+ previewPrompt: boolean | ListItemProps['prompt'];
219
+ };
220
+
221
+ const previewArgGroup = {
222
+ category: 'Preview options',
223
+ type: {
224
+ summary: undefined,
225
+ },
226
+ };
227
+
228
+ const previewArgTypes = {
229
+ previewImageSize: {
230
+ options: [32, 40, 48, 56, 72],
231
+ control: {
232
+ type: 'inline-radio',
233
+ },
234
+ name: 'Preview with image size',
235
+ table: previewArgGroup,
236
+ },
237
+ previewWithLineGuides: {
238
+ control: {
239
+ type: 'boolean',
240
+ },
241
+ name: 'Preview with line guides',
242
+ table: previewArgGroup,
243
+ },
244
+ previewPrompt: {
245
+ control: {
246
+ type: 'boolean',
247
+ },
248
+ mapping: {
249
+ true: <ListItem.Prompt sentiment="positive">This is a prompt</ListItem.Prompt>,
250
+ false: null,
251
+ },
252
+ name: 'Preview with `prompt`',
253
+ table: previewArgGroup,
254
+ },
255
+ } as const;
256
+
257
+ const getPropsForPreview = (args: PreviewStoryArgs) => {
258
+ const { previewImageSize, previewWithLineGuides, previewPrompt, ...props } = args as {
259
+ previewImageSize: ListItemMediaSize;
260
+ previewWithLineGuides: boolean;
261
+ previewPrompt: boolean | ListItemProps['prompt'];
262
+ title: ListItemProps['title'];
263
+ subtitle: ListItemProps['subtitle'];
264
+ [key: string]: any;
265
+ };
266
+
267
+ return [
268
+ {
269
+ ...props,
270
+ prompt: previewPrompt,
271
+ },
272
+ {
273
+ previewImageSize,
274
+ previewWithLineGuides,
275
+ },
276
+ ] as const;
277
+ };
278
+
279
+ // Create a completely untyped story object
280
+ const ImageAlignmentStory: StoryObj<PreviewStoryArgs> = {
281
+ parameters: {
282
+ controls: { disable: false },
283
+ },
284
+ args: {
285
+ title: lorem5,
286
+ subtitle: lorem10,
287
+ previewImageSize: 48,
288
+ previewWithLineGuides: true,
289
+ previewPrompt: false,
290
+ },
291
+ argTypes: previewArgTypes,
292
+ decorators: [
293
+ (Story: any, { args }: { args: PreviewStoryArgs }) => (
294
+ <>
295
+ {args.previewWithLineGuides && (
296
+ <style
297
+ dangerouslySetInnerHTML={{
298
+ __html: `
299
+ .wds-list-item-media-image-wrapper::before{
300
+ content: '';
301
+ border: 1px dashed blue;
302
+ position: absolute;
303
+ width: ${args.previewImageSize}px;
304
+ height: ${args.previewImageSize}px;
305
+ }
306
+ .wds-list-item-media-image-wrapper::after {
307
+ content: '';
308
+ border: 1px dashed red;
309
+ position: absolute;
310
+ left: 0;
311
+ width: 100%;
312
+ height: ${args.previewImageSize}px;
313
+ }
314
+ `,
315
+ }}
316
+ />
317
+ )}
318
+ <List
319
+ className="list-unstyled"
320
+ style={{ display: 'grid', gridTemplateColumns: '340px 400px', gap: '16px' }}
321
+ >
322
+ <Story />
323
+ </List>
324
+ </>
325
+ ),
326
+ ],
327
+ render: (args: PreviewStoryArgs) => {
328
+ const [{ title, subtitle, ...props }, previewProps] = getPropsForPreview(args);
329
+
330
+ const additionalInfo = <ListItem.AdditionalInfo>{lorem20}</ListItem.AdditionalInfo>;
331
+ const control = CONTROLS.button;
332
+ const size = previewProps.previewImageSize;
333
+
334
+ const instances = [
335
+ { title, ...props },
336
+ { title, subtitle, ...props },
337
+ { title, subtitle, additionalInfo, ...props },
338
+ { title, control, ...props },
339
+ { title, subtitle, control, ...props },
340
+ { title, subtitle, additionalInfo, control, ...props },
341
+ ] as const;
342
+
343
+ return (
344
+ <>
345
+ {instances.map((itemProps, index) => (
346
+ <Fragment key={`landscape-${index}`}>
347
+ <ListItem
348
+ {...itemProps}
349
+ media={<ListItem.Image size={size} src={landscapeImage} alt="landscape image" />}
350
+ />
351
+ <ListItem
352
+ {...itemProps}
353
+ media={<ListItem.Image size={size} src={landscapeImage} alt="landscape image" />}
354
+ />
355
+ </Fragment>
356
+ ))}
357
+ {instances.map((itemProps, index) => (
358
+ <Fragment key={`portrait-${index}`}>
359
+ <ListItem
360
+ {...itemProps}
361
+ media={<ListItem.Image size={size} src={portraitImage} alt="portrait image" />}
362
+ />
363
+ <ListItem
364
+ {...itemProps}
365
+ media={<ListItem.Image size={size} src={portraitImage} alt="portrait image" />}
366
+ />
367
+ </Fragment>
368
+ ))}
369
+ </>
370
+ );
371
+ },
372
+ };
373
+
374
+ export const ImageAlignment = ImageAlignmentStory;
@@ -0,0 +1,228 @@
1
+ import { useState } from 'react';
2
+ import { Title, Subtitle, Description, Stories } from '@storybook/addon-docs/blocks';
3
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
4
+ import { action } from 'storybook/actions';
5
+ import { InfoCircle, Documents } from '@transferwise/icons';
6
+ import { lorem10, lorem100, lorem20, lorem5 } from '../../test-utils';
7
+ import Modal from '../../modal';
8
+ import Link from '../../link';
9
+ import List from '../../list';
10
+ import Popover from '../../popover';
11
+ import { SnackbarConsumer, type SnackbarContextType } from '../../snackbar/SnackbarContext';
12
+ import SnackbarProvider from '../../snackbar/SnackbarProvider';
13
+ import Tooltip from '../../tooltip';
14
+ import { Position } from '../../common';
15
+ import { ListItem, type ListItemProps } from '../ListItem';
16
+ import { storySourceWithoutNoise } from './helpers';
17
+ import {
18
+ SB_LIST_ITEM_ADDITIONAL_INFO as INFO,
19
+ SB_LIST_ITEM_CONTROLS as CONTROLS,
20
+ SB_LIST_ITEM_MEDIA as MEDIA,
21
+ SB_LIST_ITEM_PROMPTS as PROMPTS,
22
+ } from './subcomponents';
23
+
24
+ export default {
25
+ component: ListItem,
26
+ title: 'Content/ListItem/common scenarios',
27
+ tags: ['autodocs'],
28
+ args: {
29
+ title: lorem5,
30
+ subtitle: lorem10,
31
+ media: MEDIA.image,
32
+ additionalInfo: INFO.nonInteractive,
33
+ prompt: PROMPTS.interactive,
34
+ control: CONTROLS.button,
35
+ },
36
+ parameters: {
37
+ docs: {
38
+ toc: true,
39
+ page: () => (
40
+ <>
41
+ <Title>Common scenarios</Title>
42
+ <Subtitle />
43
+ <Description />
44
+ <Stories />
45
+ </>
46
+ ),
47
+ },
48
+ },
49
+ } satisfies Meta<ListItemProps>;
50
+ type Story = StoryObj<ListItemProps>;
51
+
52
+ /**
53
+ * Aside from using the main control for triggering a [Modal](?path=/docs/dialogs-modal--docs),
54
+ * it's also possible to toggle it via inline-button as part of the `<ListItem.AdditionalInfo>`
55
+ * or `<ListItem.Prompt>`.
56
+ */
57
+ export const WithModals: Story = storySourceWithoutNoise({
58
+ parameters: {
59
+ controls: { disable: true },
60
+ },
61
+ render: function WithModals(listItemProps) {
62
+ const [isModalOpen, setIsModalOpen] = useState(false);
63
+
64
+ const handleOpening = () => {
65
+ setIsModalOpen(true);
66
+ action('Modal opening')();
67
+ };
68
+ const handleClosing = () => {
69
+ setIsModalOpen(false);
70
+ action('Modal closing')();
71
+ };
72
+
73
+ return (
74
+ <List>
75
+ <ListItem
76
+ {...listItemProps}
77
+ control={
78
+ <ListItem.Button partiallyInteractive onClick={handleOpening}>
79
+ Open modal
80
+ </ListItem.Button>
81
+ }
82
+ additionalInfo={
83
+ <ListItem.AdditionalInfo
84
+ action={{ label: 'button opening a modal', onClick: handleOpening }}
85
+ >
86
+ This is additional information that contains a
87
+ </ListItem.AdditionalInfo>
88
+ }
89
+ prompt={
90
+ <ListItem.Prompt sentiment="positive">
91
+ {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
92
+ This prompt also has a <Link onClick={handleOpening}>
93
+ button that opens a modal
94
+ </Link>{' '}
95
+ for your convenience.
96
+ </ListItem.Prompt>
97
+ }
98
+ />
99
+ <Modal body={lorem100} open={isModalOpen} onClose={handleClosing} />
100
+ </List>
101
+ );
102
+ },
103
+ });
104
+
105
+ /**
106
+ * The control or inline buttons can trigger a [Popover](?path=/docs/dialogs-popover--docs).
107
+ */
108
+ export const WithPopover: Story = {
109
+ parameters: {
110
+ controls: { disable: true },
111
+ },
112
+ render: (listItemProps) => {
113
+ return (
114
+ <List>
115
+ <ListItem
116
+ {...listItemProps}
117
+ control={
118
+ <Popover preferredPlacement={Position.BOTTOM} title={lorem5} content={lorem20}>
119
+ <ListItem.IconButton
120
+ partiallyInteractive
121
+ aria-label="Learn more about {enter subject here}"
122
+ >
123
+ <InfoCircle />
124
+ </ListItem.IconButton>
125
+ </Popover>
126
+ }
127
+ prompt={PROMPTS.nonInteractive}
128
+ />
129
+ </List>
130
+ );
131
+ },
132
+ };
133
+
134
+ /**
135
+ * The control or inline buttons can trigger a [Tooltip](?path=/docs/dialogs-tooltip--docs).
136
+ */
137
+ export const WithTooltip: Story = {
138
+ parameters: {
139
+ controls: { disable: true },
140
+ },
141
+ render: (listItemProps) => {
142
+ return (
143
+ <List>
144
+ <ListItem
145
+ {...listItemProps}
146
+ control={
147
+ <Tooltip position={Position.BOTTOM} label={lorem10}>
148
+ <ListItem.IconButton
149
+ partiallyInteractive
150
+ aria-label="Learn more about {enter subject here}"
151
+ >
152
+ <InfoCircle />
153
+ </ListItem.IconButton>
154
+ </Tooltip>
155
+ }
156
+ prompt={PROMPTS.nonInteractive}
157
+ />
158
+ </List>
159
+ );
160
+ },
161
+ };
162
+
163
+ /**
164
+ * ListItem is often used as a simple copy and paste button augmented with a [Snackbar](?path=/docs/dialogs-snackbar--docs). <br />
165
+ *
166
+ * **NB** The snippet below uses a simplified example implementation of a clipboard handler,
167
+ * which may not be suitable or sufficient for your needs.
168
+ */
169
+ export const CopyAndPasteWithSnackbar: Story = storySourceWithoutNoise({
170
+ parameters: {
171
+ controls: { disable: true },
172
+ },
173
+ args: {
174
+ prompt: undefined,
175
+ additionalInfo: undefined,
176
+ },
177
+ render: function CopyAndPaste(listItemProps) {
178
+ const title = 'Artie Choke';
179
+ const subtitle = 'Account holder';
180
+
181
+ const handleCopy = async (createSnackbar: SnackbarContextType['createSnackbar']) =>
182
+ navigator.clipboard
183
+ .writeText(`${subtitle}: ${title}`)
184
+ .catch((err) => {
185
+ console.error('Failed to copy text: ', err);
186
+ })
187
+ .then(() => {
188
+ createSnackbar({
189
+ text: `${subtitle} copied to clipboard.`,
190
+ });
191
+ });
192
+
193
+ return (
194
+ <List>
195
+ <ListItem
196
+ {...listItemProps}
197
+ title={title}
198
+ subtitle={subtitle}
199
+ inverted
200
+ control={
201
+ <SnackbarProvider>
202
+ <SnackbarConsumer>
203
+ {({ createSnackbar }) => {
204
+ return (
205
+ <ListItem.IconButton
206
+ aria-label={`Copy ${subtitle} to clipboard`}
207
+ partiallyInteractive
208
+ onClick={async () => handleCopy(createSnackbar)}
209
+ >
210
+ <Documents />
211
+ </ListItem.IconButton>
212
+ );
213
+ }}
214
+ </SnackbarConsumer>
215
+ </SnackbarProvider>
216
+ }
217
+ media={
218
+ <ListItem.AvatarView
219
+ imgSrc="../avatar-square-dude.webp"
220
+ profileName="John Smith"
221
+ size={48}
222
+ />
223
+ }
224
+ />
225
+ </List>
226
+ );
227
+ },
228
+ });