@transferwise/components 0.0.0-experimental-4533565 → 0.0.0-experimental-feb2ddf

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 (121) hide show
  1. package/build/flowNavigation/animatedLabel/AnimatedLabel.js +1 -1
  2. package/build/flowNavigation/animatedLabel/AnimatedLabel.js.map +1 -1
  3. package/build/flowNavigation/animatedLabel/AnimatedLabel.mjs +1 -1
  4. package/build/flowNavigation/animatedLabel/AnimatedLabel.mjs.map +1 -1
  5. package/build/index.js +3 -3
  6. package/build/index.mjs +2 -2
  7. package/build/{listItem/ListItem.js → legacylistItem/LegacyListItem.js} +3 -3
  8. package/build/legacylistItem/LegacyListItem.js.map +1 -0
  9. package/build/{listItem/ListItem.mjs → legacylistItem/LegacyListItem.mjs} +3 -3
  10. package/build/legacylistItem/LegacyListItem.mjs.map +1 -0
  11. package/build/legacylistItem/List.js.map +1 -0
  12. package/build/legacylistItem/List.mjs.map +1 -0
  13. package/build/main.css +60 -57
  14. package/build/phoneNumberInput/PhoneNumberInput.js +1 -1
  15. package/build/phoneNumberInput/PhoneNumberInput.js.map +1 -1
  16. package/build/phoneNumberInput/PhoneNumberInput.mjs +1 -1
  17. package/build/phoneNumberInput/PhoneNumberInput.mjs.map +1 -1
  18. package/build/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.js +2 -2
  19. package/build/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.js.map +1 -1
  20. package/build/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.mjs +2 -2
  21. package/build/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.mjs.map +1 -1
  22. package/build/styles/legacylistItem/LegacyListItem.css +6 -0
  23. package/build/styles/listItem/ListItem.css +185 -3
  24. package/build/styles/main.css +60 -57
  25. package/build/styles/switch/Switch.css +3 -0
  26. package/build/types/index.d.ts +3 -3
  27. package/build/types/index.d.ts.map +1 -1
  28. package/build/types/legacylistItem/LegacyListItem.d.ts +13 -0
  29. package/build/types/legacylistItem/LegacyListItem.d.ts.map +1 -0
  30. package/build/types/legacylistItem/List.d.ts.map +1 -0
  31. package/build/types/legacylistItem/index.d.ts +5 -0
  32. package/build/types/legacylistItem/index.d.ts.map +1 -0
  33. package/build/types/listItem/ListItem.d.ts +38 -8
  34. package/build/types/listItem/ListItem.d.ts.map +1 -1
  35. package/build/types/{item/ItemAdditionalInfo.d.ts → listItem/ListItemAdditionalInfo.d.ts} +1 -1
  36. package/build/types/listItem/ListItemAdditionalInfo.d.ts.map +1 -0
  37. package/build/types/{item/ItemButton.d.ts → listItem/ListItemButton.d.ts} +1 -1
  38. package/build/types/listItem/ListItemButton.d.ts.map +1 -0
  39. package/build/types/{item/ItemCheckbox.d.ts → listItem/ListItemCheckbox.d.ts} +1 -1
  40. package/build/types/listItem/ListItemCheckbox.d.ts.map +1 -0
  41. package/build/types/{item/ItemIconButton.d.ts → listItem/ListItemIconButton.d.ts} +1 -1
  42. package/build/types/listItem/ListItemIconButton.d.ts.map +1 -0
  43. package/build/types/{item/ItemMedia.d.ts → listItem/ListItemMedia.d.ts} +1 -1
  44. package/build/types/listItem/ListItemMedia.d.ts.map +1 -0
  45. package/build/types/{item/ItemNavigation.d.ts → listItem/ListItemNavigation.d.ts} +1 -1
  46. package/build/types/listItem/ListItemNavigation.d.ts.map +1 -0
  47. package/build/types/{item/ItemSwitch.d.ts → listItem/ListItemSwitch.d.ts} +1 -1
  48. package/build/types/listItem/ListItemSwitch.d.ts.map +1 -0
  49. package/build/types/listItem/index.d.ts +4 -3
  50. package/build/types/listItem/index.d.ts.map +1 -1
  51. package/build/types/listItem/prompt/Prompt.d.ts.map +1 -0
  52. package/build/types/listItem/useItemControl.d.ts +5 -0
  53. package/build/types/listItem/useItemControl.d.ts.map +1 -0
  54. package/build/types/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.d.ts +2 -2
  55. package/build/types/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.d.ts.map +1 -1
  56. package/package.json +3 -3
  57. package/src/flowNavigation/animatedLabel/AnimatedLabel.tsx +1 -1
  58. package/src/index.ts +3 -3
  59. package/src/legacylistItem/LegacyListItem.css +6 -0
  60. package/src/legacylistItem/LegacyListItem.less +7 -0
  61. package/src/legacylistItem/LegacyListItem.story.tsx +120 -0
  62. package/src/{listItem/ListItem.tests.story.tsx → legacylistItem/LegacyListItem.tests.story.tsx} +2 -2
  63. package/src/legacylistItem/LegacyListItem.tsx +53 -0
  64. package/src/legacylistItem/index.ts +4 -0
  65. package/src/listItem/ListItem.css +185 -3
  66. package/src/listItem/ListItem.less +180 -5
  67. package/src/listItem/ListItem.story.tsx +250 -97
  68. package/src/listItem/ListItem.tsx +167 -37
  69. package/src/{item/ItemAdditionalInfo.tsx → listItem/ListItemAdditionalInfo.tsx} +2 -2
  70. package/src/{item/ItemButton.spec.tsx → listItem/ListItemButton.spec.tsx} +4 -4
  71. package/src/listItem/index.ts +5 -3
  72. package/src/listItem/useItemControl.tsx +12 -0
  73. package/src/main.css +60 -57
  74. package/src/main.less +2 -2
  75. package/src/phoneNumberInput/PhoneNumberInput.spec.tsx +4 -4
  76. package/src/phoneNumberInput/PhoneNumberInput.story.tsx +22 -0
  77. package/src/phoneNumberInput/PhoneNumberInput.tsx +1 -1
  78. package/src/phoneNumberInput/utils/isValidPhoneNumber/isValidPhoneNumber.ts +4 -3
  79. package/src/switch/Switch.css +3 -0
  80. package/src/switch/Switch.less +1 -0
  81. package/build/listItem/List.js.map +0 -1
  82. package/build/listItem/List.mjs.map +0 -1
  83. package/build/listItem/ListItem.js.map +0 -1
  84. package/build/listItem/ListItem.mjs.map +0 -1
  85. package/build/styles/item/Item.css +0 -188
  86. package/build/types/item/Item.d.ts +0 -43
  87. package/build/types/item/Item.d.ts.map +0 -1
  88. package/build/types/item/ItemAdditionalInfo.d.ts.map +0 -1
  89. package/build/types/item/ItemButton.d.ts.map +0 -1
  90. package/build/types/item/ItemCheckbox.d.ts.map +0 -1
  91. package/build/types/item/ItemIconButton.d.ts.map +0 -1
  92. package/build/types/item/ItemMedia.d.ts.map +0 -1
  93. package/build/types/item/ItemNavigation.d.ts.map +0 -1
  94. package/build/types/item/ItemSwitch.d.ts.map +0 -1
  95. package/build/types/item/index.d.ts +0 -6
  96. package/build/types/item/index.d.ts.map +0 -1
  97. package/build/types/item/prompt/Prompt.d.ts.map +0 -1
  98. package/build/types/item/useItemControl.d.ts +0 -5
  99. package/build/types/item/useItemControl.d.ts.map +0 -1
  100. package/build/types/listItem/List.d.ts.map +0 -1
  101. package/src/item/Item.css +0 -188
  102. package/src/item/Item.less +0 -182
  103. package/src/item/Item.story.tsx +0 -273
  104. package/src/item/Item.tsx +0 -181
  105. package/src/item/index.ts +0 -6
  106. package/src/item/useItemControl.tsx +0 -12
  107. /package/build/{listItem → legacylistItem}/List.js +0 -0
  108. /package/build/{listItem → legacylistItem}/List.mjs +0 -0
  109. /package/build/types/{listItem → legacylistItem}/List.d.ts +0 -0
  110. /package/build/types/{item → listItem}/prompt/Prompt.d.ts +0 -0
  111. /package/src/{listItem/ListItem.spec.tsx → legacylistItem/LegacyListItem.spec.tsx} +0 -0
  112. /package/src/{listItem → legacylistItem}/List.tsx +0 -0
  113. /package/src/{item/ItemButton.tsx → listItem/ListItemButton.tsx} +0 -0
  114. /package/src/{item/ItemCheckbox.tsx → listItem/ListItemCheckbox.tsx} +0 -0
  115. /package/src/{item/ItemIconButton.tsx → listItem/ListItemIconButton.tsx} +0 -0
  116. /package/src/{item/ItemMedia.tsx → listItem/ListItemMedia.tsx} +0 -0
  117. /package/src/{item/ItemNavigation.tsx → listItem/ListItemNavigation.tsx} +0 -0
  118. /package/src/{item/ItemSwitch.tsx → listItem/ListItemSwitch.tsx} +0 -0
  119. /package/src/{item → listItem}/prompt/Prompt.spec.tsx +0 -0
  120. /package/src/{item → listItem}/prompt/Prompt.story.tsx +0 -0
  121. /package/src/{item → listItem}/prompt/Prompt.tsx +0 -0
@@ -1,120 +1,273 @@
1
- import { action } from '@storybook/addon-actions';
2
- import { Documents, FastFlag } from '@transferwise/icons';
3
- import { ComponentProps } from 'react';
1
+ import ListItem, { Props as ItemProps } from './ListItem';
2
+ import { Meta, StoryObj } from '@storybook/react';
3
+ import React, { useState } from 'react';
4
+ import { FastFlag, MultiCurrency, Plus, Receipt, Savings } from '@transferwise/icons';
4
5
 
5
- import { Button, IconButton } from '..';
6
- import AvatarView from '../avatarView';
7
- import Info from '../info';
8
- import Title from '../title/Title';
9
- import ListItem, { List } from '.';
6
+ import { lorem10, lorem20, lorem40, lorem5 } from '../test-utils';
7
+ import { Flag } from '@wise/art';
8
+ import { List } from '../legacylistItem/List';
9
+ import Money from '../money';
10
+ import Section from '../section';
11
+ import Header from '../header';
10
12
 
11
13
  export default {
12
14
  component: ListItem,
13
- title: 'Other/ListItem',
14
- tags: ['autodocs'],
15
+ title: 'Content/Item',
16
+ } satisfies Meta<ItemProps>;
17
+
18
+ type Story = StoryObj<ItemProps>;
19
+
20
+ // TODO: delete this story later when we have more peace of the List Item puzzle
21
+ export const TempPlayground: Story = {
22
+ render: () => {
23
+ const [checked, setChecked] = useState(false);
24
+
25
+ return (
26
+ <div>
27
+ <ListItem
28
+ media={
29
+ <ListItem.AvatarView badge={{ type: 'action' }}>
30
+ <Flag code="BBD" />
31
+ </ListItem.AvatarView>
32
+ }
33
+ title="Test title"
34
+ subtitle="Test subtitle"
35
+ additionalInfo={<ListItem.AdditionalInfo>{lorem10}</ListItem.AdditionalInfo>}
36
+ />
37
+ <ListItem
38
+ media={
39
+ <ListItem.AvatarLayout
40
+ avatars={[{ asset: <Flag code="BBD" /> }, { asset: <Flag code="MXN" /> }]}
41
+ />
42
+ }
43
+ title="Test title"
44
+ subtitle={lorem10}
45
+ additionalInfo={<ListItem.AdditionalInfo>{lorem20}</ListItem.AdditionalInfo>}
46
+ />
47
+ <ListItem
48
+ media={
49
+ <ListItem.AvatarLayout
50
+ size={72}
51
+ orientation="diagonal"
52
+ avatars={[{ asset: <Flag code="BBD" /> }, { asset: <Flag code="MXN" /> }]}
53
+ />
54
+ }
55
+ title="Test title"
56
+ subtitle={lorem10}
57
+ valueTitle="100 GBP"
58
+ valueSubtitle="100 USD"
59
+ additionalInfo={<ListItem.AdditionalInfo>{lorem20}</ListItem.AdditionalInfo>}
60
+ />
61
+ <ListItem
62
+ media={<ListItem.AvatarView profileType="BUSINESS" notification />}
63
+ title="Test title"
64
+ subtitle="Test subtitle"
65
+ additionalInfo={
66
+ <ListItem.AdditionalInfo
67
+ action={{ label: 'Learn more', href: 'https://wise.com', target: '_blank' }}
68
+ >
69
+ {lorem10}
70
+ </ListItem.AdditionalInfo>
71
+ }
72
+ prompt={<ListItem.Prompt type="negative">You have done something wrong</ListItem.Prompt>}
73
+ />
74
+ <ListItem
75
+ media={<ListItem.AvatarView imgSrc="../avatar-square-dude.webp" selected />}
76
+ title="Test title"
77
+ additionalInfo={<ListItem.AdditionalInfo>{lorem10}</ListItem.AdditionalInfo>}
78
+ prompt={
79
+ <ListItem.Prompt
80
+ type="discount"
81
+ action={{
82
+ href: 'https://wise.com',
83
+ target: '_blank',
84
+ 'aria-label': 'clickable prompt',
85
+ }}
86
+ >
87
+ The whole prompt is secretly clickable
88
+ </ListItem.Prompt>
89
+ }
90
+ />
91
+
92
+ <ListItem
93
+ media={<ListItem.Image src="../apple-pay-logo.svg" />}
94
+ title="Accepting Apple Pay"
95
+ subtitle={lorem10}
96
+ />
97
+
98
+ <ListItem
99
+ media={<ListItem.Image src="../wise-card.svg" />}
100
+ title="Wise Business Card"
101
+ subtitle={lorem5}
102
+ />
103
+
104
+ <ListItem
105
+ title="Test title"
106
+ disabled
107
+ subtitle={lorem10}
108
+ additionalInfo={<ListItem.AdditionalInfo>{lorem20}</ListItem.AdditionalInfo>}
109
+ control={<ListItem.Button priority="secondary-neutral">as Button</ListItem.Button>}
110
+ />
111
+ <ListItem
112
+ title="Test title"
113
+ disabled
114
+ subtitle={lorem10}
115
+ additionalInfo={<ListItem.AdditionalInfo>{lorem20}</ListItem.AdditionalInfo>}
116
+ control={
117
+ <ListItem.IconButton>
118
+ <Plus />
119
+ </ListItem.IconButton>
120
+ }
121
+ />
122
+
123
+ <ListItem
124
+ title="Test title"
125
+ subtitle={lorem10}
126
+ additionalInfo={<ListItem.AdditionalInfo>{lorem20}</ListItem.AdditionalInfo>}
127
+ control={
128
+ <ListItem.Button
129
+ priority="secondary-neutral"
130
+ as="a"
131
+ target="dfdf"
132
+ aria-label="adsfasfd"
133
+ href="wise.com"
134
+ >
135
+ as Link
136
+ </ListItem.Button>
137
+ }
138
+ />
139
+
140
+ <ListItem
141
+ title="Test title"
142
+ subtitle={lorem10}
143
+ additionalInfo={<ListItem.AdditionalInfo>{lorem20}</ListItem.AdditionalInfo>}
144
+ control={<ListItem.Checkbox indeterminate />}
145
+ />
146
+
147
+ <ListItem
148
+ title="Test title"
149
+ subtitle={lorem10}
150
+ additionalInfo={<ListItem.AdditionalInfo>{lorem20}</ListItem.AdditionalInfo>}
151
+ control={
152
+ <ListItem.Switch
153
+ checked={checked}
154
+ onClick={() => {
155
+ setChecked(!checked);
156
+ }}
157
+ />
158
+ }
159
+ />
160
+ </div>
161
+ );
162
+ },
15
163
  };
16
164
 
17
- const Template = ({
18
- title = 'Wise',
19
- value = 'We’re building the world’s most international account',
20
- ...props
21
- }: Partial<ComponentProps<typeof ListItem>>) => {
22
- return <ListItem title={title} value={value} {...props} />;
165
+ export const ItemRadio: Story = {
166
+ render: () => (
167
+ <div>
168
+ {/* Basic */}
169
+ {/* with additional info */}
170
+ {/* with prompt */}
171
+ {/* Disabled */}
172
+ {/* Spotlight */}
173
+ {/* Active */}
174
+ </div>
175
+ ),
23
176
  };
24
177
 
25
- export const Basic = () => {
26
- return (
27
- <List>
28
- <Template />
29
- </List>
30
- );
178
+ export const ItemButton: Story = {
179
+ render: () => (
180
+ <div>
181
+ {/* Basic */}
182
+ {/* with additional info */}
183
+ {/* with prompt */}
184
+ {/* partially interactive */}
185
+ {/* Disabled */}
186
+ {/* Spotlight */}
187
+ {/* Active */}
188
+ </div>
189
+ ),
31
190
  };
32
191
 
33
- export const Variants = () => {
34
- return (
192
+ export const ItemNonInteractive: Story = {
193
+ render: () => (
35
194
  <div>
36
- <div className="m-t-4 m-b-2">
37
- <Title type="title-body">With Icon Avatar</Title>
195
+ <Section>
196
+ <Header title="Transcation Details" />
38
197
  <List>
39
- <Template media={<AvatarView imgSrc="../tapestry-01.png" />} />
40
- </List>
41
- </div>
42
- <div className="m-t-4 m-b-2">
43
- <Title type="title-body">With Contact Avatar</Title>
44
- <List>
45
- <Template
46
- title="Sandra Pepper"
47
- value="Personal account"
48
- media={<AvatarView profileName="Super Pepa" badge={{ icon: <FastFlag /> }} />}
49
- />
50
- </List>
51
- </div>
52
- <div className="m-t-4 m-b-2">
53
- <Title type="title-body">With Avatar and Action</Title>
54
- <List>
55
- <Template
56
- title="Account holder"
57
- value="Sandra Pepper"
58
- media={<AvatarView profileName="Super Pepa" badge={{ icon: <FastFlag /> }} />}
59
- action={
60
- <Button v2 size="sm">
61
- Share details
62
- </Button>
198
+ <ListItem
199
+ media={
200
+ <ListItem.AvatarView>
201
+ <MultiCurrency />
202
+ </ListItem.AvatarView>
63
203
  }
204
+ title="Estimated costs"
205
+ subtitle="Other providers"
206
+ valueTitle={<Money amount={100} currency="GBP" />}
64
207
  />
65
- </List>
66
- </div>
67
- <div className="m-t-4 m-b-2">
68
- <Title type="title-body">Text Only List</Title>
69
- <List>
70
- <Template title="Sort code" value="23-14-71" />
71
- <Template title="Account number" value="12345678" />
72
- </List>
73
- </div>
74
- <div className="m-t-4 m-b-2">
75
- <Title type="title-body">Text With Action Button</Title>
76
- <List>
77
- <Template
78
- title="SWIFT/BIC"
79
- value="••• •••"
80
- action={
81
- <Button v2 size="sm" onClick={() => action('clicked')}>
82
- Reveal
83
- </Button>
208
+ <ListItem
209
+ media={
210
+ <ListItem.AvatarView>
211
+ <FastFlag />
212
+ </ListItem.AvatarView>
84
213
  }
214
+ title="Total paid"
215
+ subtitle="Wise"
216
+ valueTitle={<Money amount={12.15} currency="GBP" />}
217
+ valueSubtitle={<Money amount={100.9} currency="GBP" />}
85
218
  />
86
- </List>
87
- </div>
88
- <div className="m-t-4 m-b-2">
89
- <Title type="title-body">Text With Other Action</Title>
90
- <List>
91
- <Template
92
- title="Account number"
93
- value="123456789"
94
- action={
95
- <IconButton size={24} aria-label="Copy" priority="minimal">
96
- <Documents />
97
- </IconButton>
219
+ <ListItem
220
+ media={
221
+ <ListItem.AvatarView>
222
+ <Receipt />
223
+ </ListItem.AvatarView>
98
224
  }
225
+ title="Total fees"
226
+ subtitle={lorem5}
227
+ valueSubtitle={<Money amount={5.5} currency="GBP" />}
99
228
  />
100
- </List>
101
- </div>
102
- <div className="m-t-4 m-b-2">
103
- <Title type="title-body">Text With Info Action</Title>
104
- <List>
105
- <Template
106
- title="Routing number"
107
- value="987654"
108
- action={
109
- <Info
110
- size="lg"
111
- aria-label="More information"
112
- content="Use this routing number to receive ACH and Wire transfers."
113
- />
229
+ <ListItem
230
+ media={
231
+ <ListItem.AvatarView>
232
+ <Savings />
233
+ </ListItem.AvatarView>
114
234
  }
235
+ title="You saved"
236
+ valueSubtitle={<Money amount={1000} currency="GBP" />}
115
237
  />
116
238
  </List>
117
- </div>
239
+ </Section>
240
+
241
+ <ListItem
242
+ as="div"
243
+ title="Bank name and address"
244
+ subtitle="Bank Ltd, 2 Street Boulevard, Singapore, 1213423"
245
+ additionalInfo={
246
+ <ListItem.AdditionalInfo
247
+ action={{ label: 'Learn more', href: 'https://wise.com', target: '_blank' }}
248
+ >
249
+ This is our partner bank in Singapore.
250
+ </ListItem.AdditionalInfo>
251
+ }
252
+ />
253
+ <ListItem
254
+ as="div"
255
+ title="Bank name and address"
256
+ subtitle="Capital Ltd, 2 Canal Street, London, E14 111"
257
+ additionalInfo={
258
+ <ListItem.AdditionalInfo
259
+ action={{ label: 'Learn more', href: 'https://wise.com', target: '_blank' }}
260
+ />
261
+ }
262
+ />
263
+ {/* Basic */}
264
+ {/* with additional info */}
265
+ {/* with prompt */}
266
+ {/* Disabled */}
267
+ {/* Spotlight */}
268
+ {/* Active */}
118
269
  </div>
119
- );
270
+ ),
120
271
  };
272
+
273
+ // add intaces with dark mode
@@ -1,53 +1,183 @@
1
+ import { createContext, ReactNode, useId, useMemo, useState } from 'react';
2
+ import { Typography } from '../common';
3
+ import Body from '../body';
4
+ import { AdditionalInfo } from './ListItemAdditionalInfo';
5
+ import { IconButton } from './ListItemIconButton';
6
+ import { Checkbox } from './ListItemCheckbox';
7
+ import { Navigation } from './ListItemNavigation';
1
8
  import { clsx } from 'clsx';
2
- import { ElementType, FC, ReactNode } from 'react';
9
+ import { Button } from './ListItemButton';
10
+ import { Switch } from './ListItemSwitch';
11
+ import { AvatarLayout, AvatarView, Image } from './ListItemMedia';
12
+ import Prompt from './prompt/Prompt';
3
13
 
4
- import Body from '../body/Body';
14
+ export type ListItemTypes =
15
+ | 'non-interactive'
16
+ | 'navigation'
17
+ | 'radio'
18
+ | 'checkbox'
19
+ | 'switch'
20
+ | 'button'
21
+ | 'icon-button';
5
22
 
6
- export type ListItemProps = {
23
+ export type Props = {
24
+ as?: 'li' | 'div' | 'span';
25
+ inverted?: boolean;
26
+ disabled?: boolean;
27
+ spotlight?: 'active' | 'inactive';
7
28
  title: ReactNode;
8
- value: ReactNode;
29
+ subtitle?: ReactNode;
30
+ additionalInfo?: ReactNode;
31
+ valueTitle?: ReactNode;
32
+ valueSubtitle?: ReactNode;
9
33
  media?: ReactNode;
10
- action?: ReactNode;
11
- className?: string;
12
- isContainerAligned?: boolean;
13
- as?: ElementType;
34
+ control?: ReactNode;
35
+ prompt?: ReactNode;
14
36
  };
15
37
 
16
- const ListItem: FC<ListItemProps> = ({
17
- media,
18
- action,
19
- className,
38
+ export type ListItemContextData = {
39
+ setControlType: (type: ListItemTypes) => void;
40
+ ids: {
41
+ label: string;
42
+ additionalInfo: string;
43
+ value: string;
44
+ control: string;
45
+ prompt: string;
46
+ };
47
+ props: Pick<Props, 'as' | 'disabled' | 'inverted'>;
48
+ };
49
+
50
+ // @ts-expect-error for now let's mock it with `null` value
51
+ // but actually by default we should specify `setControlType('none')`
52
+ export const ListItemContext = createContext<ListItemContextData>(null);
53
+
54
+ export const ListItem = ({
55
+ as: View = 'li',
20
56
  title,
21
- value,
22
- isContainerAligned,
23
- as = 'li',
24
- ...rest
25
- }) => {
26
- const Element = as;
57
+ subtitle,
58
+ additionalInfo,
59
+ prompt,
60
+ inverted,
61
+ media,
62
+ spotlight = undefined,
63
+ valueTitle,
64
+ valueSubtitle,
65
+ control = null,
66
+ disabled,
67
+ }: Props) => {
68
+ /*
69
+ const returnType = (): ReactNode => {
70
+ switch (type) {
71
+ case 'Navigation':
72
+ return <Chevron orientation={Position.RIGHT} disabled />;
73
+ case 'Radio':
74
+ return <RadioButton name="Hello" checked />;
75
+ case 'Checkbox':
76
+ return <CheckboxButton name="Hello" checked />;
77
+ case 'Switch':
78
+ return <Switch onClick={() => console.log('clicked')} />;
79
+ case 'Button':
80
+ return <Button v2>Hello</Button>;
81
+ case 'IconButton':
82
+ return (
83
+ <IconButton size={40} priority="minimal">
84
+ <InfoCircle />
85
+ </IconButton>
86
+ );
87
+ }
88
+ };
89
+ */
90
+ const idPrefix = useId();
91
+
92
+ const [controlType, setControlType] = useState<ListItemTypes>('non-interactive');
93
+ const ids = {
94
+ label: `${idPrefix}_label`,
95
+ value: `${idPrefix}_value`,
96
+ control: `${idPrefix}_control`,
97
+ prompt: `${idPrefix}_prompt`,
98
+ additionalInfo: `${idPrefix}_additional-info`,
99
+ };
100
+
101
+ const listItemContext = useMemo(
102
+ () => ({ setControlType, ids, props: { as: View, disabled, inverted } }),
103
+ [],
104
+ );
27
105
 
28
106
  return (
29
- <Element
30
- className={clsx('np-list-item d-flex align-items-center p-y-2', className, {
31
- 'p-x-2': !isContainerAligned,
32
- })}
33
- {...rest}
34
- >
35
- {media}
36
-
37
- <div className={clsx('d-flex flex-column', { 'p-l-2': !!media, 'p-r-2': !!action })}>
38
- <Body type="body-default" className="text-secondary m-b-0">
39
- {title}
40
- </Body>
41
- {value && (
42
- <Body type="body-large" className="text-primary text-overflow-wrap m-b-0">
43
- {value}
44
- </Body>
107
+ <ListItemContext.Provider value={listItemContext}>
108
+ <View
109
+ className={clsx(
110
+ 'wds-list-item',
111
+ { 'wds-list-item-interactive': controlType !== 'non-interactive' },
112
+ `wds-list-item-${controlType}`,
113
+ 'd-flex flex-row',
114
+ { 'align-items-center': !subtitle },
115
+ {
116
+ [`wds-list-item-spotlight-${spotlight}`]: !!spotlight,
117
+ },
45
118
  )}
46
- </div>
119
+ aria-describedby={[ids.additionalInfo].join(' ')}
120
+ >
121
+ {media && <div className="wds-list-item-media">{media}</div>}
47
122
 
48
- {action ? <div className="np-list-item__action m-l-auto">{action}</div> : null}
49
- </Element>
123
+ {/* Title + Subtitle + Values + Additional Info - Group */}
124
+ <div className="wds-list-item-body">
125
+ {/* Title + Subtitle + Values - Group */}
126
+ <div className="d-flex justify-content-between">
127
+ <span>
128
+ {/* @ts-expect-error div can have role and aria-lavel props */}
129
+ <Body
130
+ type={Typography.BODY_LARGE_BOLD}
131
+ className="wds-list-item-title"
132
+ // for a11y this needs to be a header but for SEO it shouldn't be `h*` tag
133
+ // so we enable header semantics via `role` and `aria-level` attrs
134
+ role="heading"
135
+ aria-level="4"
136
+ >
137
+ {title}
138
+ </Body>
139
+ <Body className="wds-list-item-subtitle">{subtitle}</Body>
140
+ </span>
141
+ {(valueTitle || valueSubtitle) && (
142
+ <span
143
+ id={ids.value}
144
+ className={clsx('wds-list-item-value', 'd-flex align-items-center', {
145
+ 'flex-column': valueTitle !== undefined && valueSubtitle !== undefined,
146
+ })}
147
+ >
148
+ {valueTitle && (
149
+ <Body type={Typography.BODY_LARGE_BOLD} className="wds-list-item-title-value">
150
+ {valueTitle}
151
+ </Body>
152
+ )}
153
+ {valueSubtitle && (
154
+ <Body className="wds-list-item-subtitle-value">{valueSubtitle}</Body>
155
+ )}
156
+ </span>
157
+ )}
158
+ </div>
159
+
160
+ {/* Additional Info and Prompt here */}
161
+ {Boolean(subtitle) && additionalInfo}
162
+ </div>
163
+ {control === null ? null : <Body className="wds-list-item-control">{control}</Body>}
164
+ {prompt}
165
+ </View>
166
+ </ListItemContext.Provider>
50
167
  );
51
168
  };
52
169
 
170
+ /* eslint-disable functional/immutable-data */
171
+ ListItem.Image = Image;
172
+ ListItem.AvatarView = AvatarView;
173
+ ListItem.AvatarLayout = AvatarLayout;
174
+ ListItem.AdditionalInfo = AdditionalInfo;
175
+ ListItem.Checkbox = Checkbox;
176
+ ListItem.IconButton = IconButton;
177
+ ListItem.Navigation = Navigation;
178
+ ListItem.Button = Button;
179
+ ListItem.Switch = Switch;
180
+ ListItem.Prompt = Prompt;
181
+ /* eslint-enable functional/immutable-data */
182
+
53
183
  export default ListItem;
@@ -1,5 +1,5 @@
1
1
  import { PropsWithChildren, useContext } from 'react';
2
- import { ItemContext, ItemContextData } from './Item';
2
+ import { ListItemContext, ListItemContextData } from './ListItem';
3
3
  import Body from '../body';
4
4
  import Link, { LinkProps } from '../link';
5
5
  import { Typography } from '../common';
@@ -9,7 +9,7 @@ export type ItemAdditionalInfoProps = PropsWithChildren<{
9
9
  }>;
10
10
 
11
11
  export const AdditionalInfo = function ({ children, action }: ItemAdditionalInfoProps) {
12
- const { ids } = useContext<ItemContextData>(ItemContext);
12
+ const { ids } = useContext<ListItemContextData>(ListItemContext);
13
13
 
14
14
  return (
15
15
  <Body
@@ -1,7 +1,7 @@
1
1
  import { render, screen, mockMatchMedia } from '../test-utils';
2
- import { Button as ItemButton } from './ItemButton';
2
+ import { Button as ItemButton } from './ListItemButton';
3
3
  import { ButtonPriority } from '../button/Button.types';
4
- import { ItemContext } from './Item';
4
+ import { ListItemContext } from './ListItem';
5
5
 
6
6
  mockMatchMedia();
7
7
 
@@ -10,7 +10,7 @@ describe('ItemButton', () => {
10
10
 
11
11
  const renderWithItemContext = (ui: React.ReactNode) => {
12
12
  return render(
13
- <ItemContext.Provider
13
+ <ListItemContext.Provider
14
14
  value={{
15
15
  setControlType: mockSetControlType,
16
16
  ids: {
@@ -24,7 +24,7 @@ describe('ItemButton', () => {
24
24
  }}
25
25
  >
26
26
  {ui}
27
- </ItemContext.Provider>,
27
+ </ListItemContext.Provider>,
28
28
  );
29
29
  };
30
30
 
@@ -1,4 +1,6 @@
1
- export { List } from './List';
2
- export type { ListProps } from './List';
1
+ export type { Props as ItemProps } from './ListItem';
2
+ export type { ItemAdditionalInfoProps } from './ListItemAdditionalInfo';
3
+ export type { ItemCheckboxProps } from './ListItemCheckbox';
4
+ export type { ItemImageProps, ItemAvatarViewProps, ItemAvatarLayoutProps } from './ListItemMedia';
5
+
3
6
  export { default } from './ListItem';
4
- export type { ListItemProps } from './ListItem';
@@ -0,0 +1,12 @@
1
+ import { useContext, useEffect } from 'react';
2
+ import { ListItemContext, ListItemContextData, ListItemTypes } from './ListItem';
3
+
4
+ export function useItemControl(controlType: ListItemTypes) {
5
+ const { setControlType, props: baseItemProps } = useContext<ListItemContextData>(ListItemContext);
6
+
7
+ useEffect(() => {
8
+ setControlType(controlType);
9
+ }, [controlType, setControlType]);
10
+
11
+ return { baseItemProps };
12
+ }