@transferwise/components 0.0.0-experimental-15914a5 → 0.0.0-experimental-e1870f3
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.
- package/build/flowNavigation/animatedLabel/AnimatedLabel.js +2 -2
- package/build/flowNavigation/animatedLabel/AnimatedLabel.js.map +1 -1
- package/build/flowNavigation/animatedLabel/AnimatedLabel.mjs +2 -2
- package/build/flowNavigation/animatedLabel/AnimatedLabel.mjs.map +1 -1
- package/build/index.js +2 -2
- package/build/index.mjs +1 -1
- package/build/listItem/AvatarLayout/ListItemAvatarLayout.js +0 -2
- package/build/listItem/AvatarLayout/ListItemAvatarLayout.js.map +1 -1
- package/build/listItem/AvatarLayout/ListItemAvatarLayout.mjs +0 -2
- package/build/listItem/AvatarLayout/ListItemAvatarLayout.mjs.map +1 -1
- package/build/listItem/AvatarView/ListItemAvatarView.js +0 -2
- package/build/listItem/AvatarView/ListItemAvatarView.js.map +1 -1
- package/build/listItem/AvatarView/ListItemAvatarView.mjs +0 -2
- package/build/listItem/AvatarView/ListItemAvatarView.mjs.map +1 -1
- package/build/listItem/Image/ListItemImage.js +1 -3
- package/build/listItem/Image/ListItemImage.js.map +1 -1
- package/build/listItem/Image/ListItemImage.mjs +1 -3
- package/build/listItem/Image/ListItemImage.mjs.map +1 -1
- package/build/{legacylistItem → listItem}/List.js +3 -2
- package/build/listItem/List.js.map +1 -0
- package/build/{legacylistItem → listItem}/List.mjs +3 -2
- package/build/listItem/List.mjs.map +1 -0
- package/build/listItem/ListItem.js +14 -16
- package/build/listItem/ListItem.js.map +1 -1
- package/build/listItem/ListItem.mjs +14 -16
- package/build/listItem/ListItem.mjs.map +1 -1
- package/build/listItem/ListItemContext.js.map +1 -1
- package/build/listItem/ListItemContext.mjs.map +1 -1
- package/build/listItem/Navigation/ListItemNavigation.js +1 -1
- package/build/listItem/Navigation/ListItemNavigation.js.map +1 -1
- package/build/listItem/Navigation/ListItemNavigation.mjs +1 -1
- package/build/listItem/Navigation/ListItemNavigation.mjs.map +1 -1
- package/build/main.css +99 -51
- package/build/styles/avatarView/AvatarView.css +3 -1
- package/build/styles/listItem/ListItem.css +96 -50
- package/build/styles/listItem/ListItem.grid.css +9 -15
- package/build/styles/main.css +99 -51
- package/build/types/index.d.ts +4 -4
- package/build/types/index.d.ts.map +1 -1
- package/build/types/legacylistItem/index.d.ts +0 -2
- package/build/types/legacylistItem/index.d.ts.map +1 -1
- package/build/types/listItem/AvatarLayout/ListItemAvatarLayout.d.ts.map +1 -1
- package/build/types/listItem/AvatarView/ListItemAvatarView.d.ts.map +1 -1
- package/build/types/listItem/Image/ListItemImage.d.ts.map +1 -1
- package/build/types/listItem/List.d.ts +7 -0
- package/build/types/listItem/List.d.ts.map +1 -0
- package/build/types/listItem/ListItem.d.ts.map +1 -1
- package/build/types/listItem/ListItemContext.d.ts +0 -4
- package/build/types/listItem/ListItemContext.d.ts.map +1 -1
- package/build/types/listItem/index.d.ts +2 -0
- package/build/types/listItem/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/avatarView/AvatarView.css +3 -1
- package/src/avatarView/AvatarView.less +2 -1
- package/src/flowNavigation/animatedLabel/AnimatedLabel.tsx +1 -1
- package/src/index.ts +9 -4
- package/src/legacylistItem/LegacyListItem.story.tsx +2 -2
- package/src/legacylistItem/LegacyListItem.tests.story.tsx +2 -1
- package/src/legacylistItem/index.ts +0 -2
- package/src/listItem/AvatarLayout/ListItemAvatarLayout.story.tsx +5 -4
- package/src/listItem/AvatarLayout/ListItemAvatarLayout.tsx +0 -3
- package/src/listItem/AvatarView/ListItemAvatarView.story.tsx +5 -4
- package/src/listItem/AvatarView/ListItemAvatarView.tsx +0 -3
- package/src/listItem/Button/ListItemButton.spec.tsx +35 -10
- package/src/listItem/Button/ListItemButton.story.tsx +9 -8
- package/src/listItem/IconButton/ListItemIconButton.story.tsx +8 -8
- package/src/listItem/Image/ListItemImage.spec.tsx +5 -8
- package/src/listItem/Image/ListItemImage.story.tsx +6 -23
- package/src/listItem/Image/ListItemImage.tsx +1 -4
- package/src/listItem/List.tsx +11 -0
- package/src/listItem/ListItem.css +96 -50
- package/src/listItem/ListItem.grid.css +9 -15
- package/src/listItem/ListItem.grid.less +14 -22
- package/src/listItem/ListItem.less +111 -46
- package/src/listItem/ListItem.tsx +18 -24
- package/src/listItem/ListItemContext.tsx +0 -5
- package/src/listItem/Navigation/ListItemNavigation.tsx +1 -1
- package/src/listItem/Prompt/ListItemPrompt.story.tsx +7 -7
- package/src/listItem/Switch/ListItemSwitch.story.tsx +3 -2
- package/src/listItem/_stories/ListItem.scenarios.story.tsx +226 -0
- package/src/listItem/_stories/ListItem.story.tsx +45 -7
- package/src/listItem/_stories/ListItem.variants.test.story.tsx +7 -11
- package/src/listItem/index.ts +3 -0
- package/src/main.css +99 -51
- package/build/legacylistItem/List.js.map +0 -1
- package/build/legacylistItem/List.mjs.map +0 -1
- package/build/listItem/useListItemMedia.js +0 -21
- package/build/listItem/useListItemMedia.js.map +0 -1
- package/build/listItem/useListItemMedia.mjs +0 -19
- package/build/listItem/useListItemMedia.mjs.map +0 -1
- package/build/types/legacylistItem/List.d.ts +0 -6
- package/build/types/legacylistItem/List.d.ts.map +0 -1
- package/build/types/listItem/test-utils.d.ts +0 -7
- package/build/types/listItem/test-utils.d.ts.map +0 -1
- package/build/types/listItem/useListItemMedia.d.ts +0 -6
- package/build/types/listItem/useListItemMedia.d.ts.map +0 -1
- package/src/legacylistItem/List.tsx +0 -10
- package/src/listItem/test-utils.tsx +0 -33
- package/src/listItem/useListItemMedia.tsx +0 -13
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
border-radius: var(--radius-medium);
|
|
8
8
|
background-color: var(--color-background-screen);
|
|
9
9
|
position: relative;
|
|
10
|
-
padding: var(--size-12);
|
|
10
|
+
padding: var(--size-12) 0;
|
|
11
11
|
container-type: inline-size;
|
|
12
12
|
|
|
13
13
|
& + .wds-list-item-spotlight,
|
|
@@ -47,12 +47,6 @@
|
|
|
47
47
|
outline: none;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
&:has(.wds-list-item-control:focus-visible),
|
|
51
|
-
&:has(input[type="checkbox"]:focus-visible) {
|
|
52
|
-
.ring();
|
|
53
|
-
outline-offset: -1px;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
50
|
.wds-list-item-additional-info {
|
|
57
51
|
.ring-offset-0
|
|
58
52
|
}
|
|
@@ -76,32 +70,88 @@
|
|
|
76
70
|
}
|
|
77
71
|
}
|
|
78
72
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
73
|
+
// spotlighted list item has extra horizontal padding so the focus
|
|
74
|
+
// and hover styles can be rendered within the main container.
|
|
75
|
+
&.wds-list-item-spotlight {
|
|
76
|
+
&:has(.wds-list-item-control:focus-visible),
|
|
77
|
+
&:has(input[type="checkbox"]:focus-visible) {
|
|
78
|
+
.ring();
|
|
79
|
+
outline-offset: -1px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
&:not(.disabled, :disabled) {
|
|
83
|
+
&:hover {
|
|
84
|
+
background-color: var(--color-background-screen-hover);
|
|
88
85
|
}
|
|
86
|
+
|
|
87
|
+
&:active {
|
|
88
|
+
background-color: var(--color-background-screen-active);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
&:has(.wds-list-item-prompt:hover a, .wds-list-item-prompt:hover button) {
|
|
93
|
+
&:hover {
|
|
94
|
+
background-color: unset;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
89
97
|
}
|
|
90
98
|
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
// non-spotlighted list item has no horizontal padding so the focus
|
|
100
|
+
// and hover styles have to be rendered out of bounds.
|
|
101
|
+
&:not(.wds-list-item-spotlight) {
|
|
102
|
+
&:has(.wds-list-item-control:focus-visible),
|
|
103
|
+
&:has(input[type="checkbox"]:focus-visible) {
|
|
104
|
+
&:before {
|
|
105
|
+
.ring();
|
|
106
|
+
content: '';
|
|
107
|
+
position: absolute;
|
|
108
|
+
inset: 0 -8px;
|
|
109
|
+
border-radius: var(--radius-medium);
|
|
110
|
+
outline-offset: -1px;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
&:not(.disabled, :disabled) {
|
|
115
|
+
&:hover,
|
|
116
|
+
&:active {
|
|
117
|
+
&:before {
|
|
118
|
+
content: '';
|
|
119
|
+
position: absolute;
|
|
120
|
+
inset: 0 -8px;
|
|
121
|
+
border-radius: var(--radius-medium);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&:hover:before {
|
|
126
|
+
background-color: var(--color-background-screen-hover);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
&:active:before {
|
|
130
|
+
background-color: var(--color-background-screen-active);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
&:has(.wds-list-item-prompt:hover a, .wds-list-item-prompt:hover button) {
|
|
135
|
+
&:hover:before {
|
|
136
|
+
background-color: unset;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
93
139
|
}
|
|
94
140
|
|
|
95
|
-
.
|
|
141
|
+
&:not(.disabled, :disabled):hover {
|
|
142
|
+
.wds-list-item-control-wrapper {
|
|
96
143
|
.wds-Button {
|
|
144
|
+
background-color: var(--Button-background-hover);
|
|
145
|
+
color: var(--Button-color-hover);
|
|
97
146
|
transition: none;
|
|
98
147
|
}
|
|
99
148
|
}
|
|
149
|
+
}
|
|
100
150
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
151
|
+
.wds-list-item-button-control {
|
|
152
|
+
.wds-Button {
|
|
153
|
+
transition: none;
|
|
154
|
+
}
|
|
105
155
|
}
|
|
106
156
|
}
|
|
107
157
|
|
|
@@ -109,8 +159,8 @@
|
|
|
109
159
|
grid-area: media;
|
|
110
160
|
|
|
111
161
|
&-image {
|
|
112
|
-
width: var(--
|
|
113
|
-
height: var(--
|
|
162
|
+
width: var(--item-media-image-size);
|
|
163
|
+
height: var(--item-media-image-size);
|
|
114
164
|
}
|
|
115
165
|
}
|
|
116
166
|
|
|
@@ -123,18 +173,8 @@
|
|
|
123
173
|
word-break: break-word;
|
|
124
174
|
}
|
|
125
175
|
|
|
126
|
-
&-title {
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
&-value {
|
|
131
|
-
text-align: right;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
&-body-center {
|
|
135
|
-
display: flex;
|
|
136
|
-
flex-direction: row;
|
|
137
|
-
align-items: center;
|
|
176
|
+
&-title, &-title-value {
|
|
177
|
+
color: var(--color-content-primary);
|
|
138
178
|
}
|
|
139
179
|
|
|
140
180
|
&-additional-info {
|
|
@@ -143,16 +183,13 @@
|
|
|
143
183
|
margin-top: calc(var(--size-4) * -1);
|
|
144
184
|
}
|
|
145
185
|
|
|
146
|
-
&-
|
|
147
|
-
|
|
148
|
-
align
|
|
149
|
-
max-height: var(--wds-list-item-control-wrapper-height);
|
|
186
|
+
&-value {
|
|
187
|
+
align-items: end;
|
|
188
|
+
text-align: right;
|
|
150
189
|
}
|
|
151
190
|
|
|
152
|
-
&-
|
|
153
|
-
|
|
154
|
-
color: var(--color-interactive-secondary);
|
|
155
|
-
}
|
|
191
|
+
&-control-wrapper {
|
|
192
|
+
grid-area: control;
|
|
156
193
|
}
|
|
157
194
|
|
|
158
195
|
&-control {
|
|
@@ -175,6 +212,9 @@
|
|
|
175
212
|
|
|
176
213
|
|
|
177
214
|
&-spotlight {
|
|
215
|
+
padding-left: var(--size-12);
|
|
216
|
+
padding-right: var(--size-12);
|
|
217
|
+
|
|
178
218
|
&-active {
|
|
179
219
|
background-color: var(--color-background-neutral);
|
|
180
220
|
&:not(.disabled, :disabled):hover {
|
|
@@ -187,7 +227,7 @@
|
|
|
187
227
|
|
|
188
228
|
&-inactive {
|
|
189
229
|
background-color: color-mix(in srgb, var(--color-background-neutral) 25%, transparent);
|
|
190
|
-
|
|
230
|
+
|
|
191
231
|
&:not(.disabled, :disabled):hover {
|
|
192
232
|
background-color: color-mix(in srgb, var(--color-background-neutral-hover) 25%, transparent);
|
|
193
233
|
}
|
|
@@ -197,6 +237,31 @@
|
|
|
197
237
|
}
|
|
198
238
|
}
|
|
199
239
|
|
|
240
|
+
.wds-list-item-spotlight__border{
|
|
241
|
+
position: absolute;
|
|
242
|
+
inset:0;
|
|
243
|
+
width: 100%;
|
|
244
|
+
height: 100%;
|
|
245
|
+
|
|
246
|
+
rect {
|
|
247
|
+
--wds-list-item-spotlight-borderWidth: 1px;
|
|
248
|
+
--wds-list-item-spotlight-borderWidthOffset: .5px; // half of the border width to center the border
|
|
249
|
+
--wds-list-item-spotlight-strokeDashSize: calc(var(--size-12) * .5);
|
|
250
|
+
|
|
251
|
+
fill: none;
|
|
252
|
+
stroke: var(--color-border-neutral);
|
|
253
|
+
width: calc(100% - var(--wds-list-item-spotlight-borderWidth));
|
|
254
|
+
height: calc(100% - var(--wds-list-item-spotlight-borderWidth));
|
|
255
|
+
x: var(--wds-list-item-spotlight-borderWidthOffset);
|
|
256
|
+
y: var(--wds-list-item-spotlight-borderWidthOffset);
|
|
257
|
+
rx: calc(var(--radius-medium) - var(--wds-list-item-spotlight-borderWidthOffset));
|
|
258
|
+
ry: calc(var(--radius-medium) - var(--wds-list-item-spotlight-borderWidthOffset));
|
|
259
|
+
stroke-dasharray:
|
|
260
|
+
var(--wds-list-item-spotlight-strokeDashSize)
|
|
261
|
+
var(--wds-list-item-spotlight-strokeDashSize);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
200
265
|
&-prompt {
|
|
201
266
|
grid-area: prompt;
|
|
202
267
|
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
useId,
|
|
5
5
|
useMemo,
|
|
6
6
|
useState,
|
|
7
|
-
useEffect,
|
|
8
7
|
type PropsWithChildren,
|
|
9
8
|
type ReactNode,
|
|
10
9
|
} from 'react';
|
|
@@ -23,11 +22,7 @@ import { AvatarView } from './AvatarView';
|
|
|
23
22
|
import { Image } from './Image';
|
|
24
23
|
import { Prompt } from './Prompt';
|
|
25
24
|
import { PrimitiveAnchor } from '../primitives';
|
|
26
|
-
import {
|
|
27
|
-
ListItemContext,
|
|
28
|
-
type ListItemContextData,
|
|
29
|
-
type ListItemMediaSize,
|
|
30
|
-
} from './ListItemContext';
|
|
25
|
+
import { ListItemContext, type ListItemContextData } from './ListItemContext';
|
|
31
26
|
|
|
32
27
|
export type ListItemTypes =
|
|
33
28
|
| 'non-interactive'
|
|
@@ -124,8 +119,6 @@ export const ListItem = ({
|
|
|
124
119
|
const idPrefix = useId();
|
|
125
120
|
const [controlProps, setControlProps] = useState<ListItemControlProps>({});
|
|
126
121
|
const [controlType, setControlType] = useState<ListItemTypes>('non-interactive');
|
|
127
|
-
const [mediaSize, setMediaSize] = useState<ListItemMediaSize | undefined>();
|
|
128
|
-
|
|
129
122
|
const ids: ListItemContextData['ids'] = {
|
|
130
123
|
title: `${idPrefix}_title`,
|
|
131
124
|
...(subtitle ? { subtitle: `${idPrefix}_subtitle` } : {}),
|
|
@@ -163,13 +156,11 @@ export const ListItem = ({
|
|
|
163
156
|
() => ({
|
|
164
157
|
setControlType,
|
|
165
158
|
setControlProps,
|
|
166
|
-
setMediaSize,
|
|
167
159
|
ids,
|
|
168
160
|
props: { disabled, inverted },
|
|
169
|
-
mediaSize,
|
|
170
161
|
describedByIds,
|
|
171
162
|
}),
|
|
172
|
-
[describedByIds
|
|
163
|
+
[describedByIds],
|
|
173
164
|
);
|
|
174
165
|
const gridColumnsStyle = {
|
|
175
166
|
'--wds-list-item-body-left': valueColumnWidth ? `${100 - valueColumnWidth}fr` : '50fr',
|
|
@@ -236,6 +227,12 @@ export const ListItem = ({
|
|
|
236
227
|
aria-disabled={disabled}
|
|
237
228
|
aria-labelledby={ariaLabelledby}
|
|
238
229
|
>
|
|
230
|
+
{spotlight === 'inactive' && (
|
|
231
|
+
<svg aria-hidden="true" className="wds-list-item-spotlight__border">
|
|
232
|
+
<rect />
|
|
233
|
+
</svg>
|
|
234
|
+
)}
|
|
235
|
+
|
|
239
236
|
<View
|
|
240
237
|
{...{
|
|
241
238
|
isPartiallyInteractive,
|
|
@@ -256,11 +253,7 @@ export const ListItem = ({
|
|
|
256
253
|
style={valueColumnWidth ? gridColumnsStyle : undefined}
|
|
257
254
|
>
|
|
258
255
|
{/* Title + Subtitle + Values - Group */}
|
|
259
|
-
<span
|
|
260
|
-
className={clsx({
|
|
261
|
-
'wds-list-item-body-center': title && !subtitle,
|
|
262
|
-
})}
|
|
263
|
-
>
|
|
256
|
+
<span>
|
|
264
257
|
{(() => {
|
|
265
258
|
const titles = [
|
|
266
259
|
<Body
|
|
@@ -287,8 +280,6 @@ export const ListItem = ({
|
|
|
287
280
|
<span
|
|
288
281
|
className={clsx('wds-list-item-value', {
|
|
289
282
|
'flex-column': valueTitle !== undefined || valueSubtitle !== undefined,
|
|
290
|
-
'wds-list-item-body-center':
|
|
291
|
-
(valueTitle && !valueSubtitle) || (!valueTitle && valueSubtitle),
|
|
292
283
|
})}
|
|
293
284
|
>
|
|
294
285
|
{(() => {
|
|
@@ -327,11 +318,6 @@ export const ListItem = ({
|
|
|
327
318
|
className={clsx('wds-list-item-control-wrapper', {
|
|
328
319
|
'wds-list-item-button-control': controlType === 'button',
|
|
329
320
|
})}
|
|
330
|
-
style={
|
|
331
|
-
{
|
|
332
|
-
'--wds-list-item-control-wrapper-height': mediaSize ? `${mediaSize}px` : 'auto',
|
|
333
|
-
} as React.CSSProperties
|
|
334
|
-
}
|
|
335
321
|
>
|
|
336
322
|
{control}
|
|
337
323
|
</Body>
|
|
@@ -384,6 +370,7 @@ function View({
|
|
|
384
370
|
href={(controlProps as ListItemNavigationProps)?.href}
|
|
385
371
|
target={(controlProps as ListItemNavigationProps)?.target}
|
|
386
372
|
className={clsx('wds-list-item-view d-flex flex-row', {
|
|
373
|
+
'align-items-center': !subtitle,
|
|
387
374
|
'wds-list-item-control': controlType === 'navigation',
|
|
388
375
|
})}
|
|
389
376
|
disabled={disabled}
|
|
@@ -399,7 +386,13 @@ function View({
|
|
|
399
386
|
if (isPartiallyInteractive || controlType === 'non-interactive') {
|
|
400
387
|
return (
|
|
401
388
|
<div className={clsx('wds-list-item-gridWrapper', className)}>
|
|
402
|
-
<div
|
|
389
|
+
<div
|
|
390
|
+
className={clsx('wds-list-item-view d-flex flex-row', {
|
|
391
|
+
'align-items-center': !subtitle,
|
|
392
|
+
})}
|
|
393
|
+
>
|
|
394
|
+
{children}
|
|
395
|
+
</div>
|
|
403
396
|
|
|
404
397
|
{renderExtras()}
|
|
405
398
|
</div>
|
|
@@ -414,6 +407,7 @@ function View({
|
|
|
414
407
|
<label
|
|
415
408
|
htmlFor={ids.control}
|
|
416
409
|
className={clsx('wds-list-item-view', {
|
|
410
|
+
'align-items-center': !subtitle,
|
|
417
411
|
clickable: !disabled,
|
|
418
412
|
fullyInteractive: !isPartiallyInteractive,
|
|
419
413
|
})}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { createContext } from 'react';
|
|
2
2
|
import type { ListItemTypes, ListItemControlProps, Props as ListItemProps } from './ListItem';
|
|
3
|
-
import type { ListItemAvatarViewProps } from './AvatarView';
|
|
4
|
-
|
|
5
|
-
export type ListItemMediaSize = 32 | 40 | 48 | 56 | 72;
|
|
6
3
|
|
|
7
4
|
export type ListItemContextData = {
|
|
8
5
|
setControlType: (type: ListItemTypes) => void;
|
|
9
6
|
setControlProps: (props: ListItemControlProps) => void;
|
|
10
|
-
setMediaSize: (size: ListItemMediaSize | undefined) => void;
|
|
11
7
|
ids: {
|
|
12
8
|
title: string;
|
|
13
9
|
subtitle?: string;
|
|
@@ -18,7 +14,6 @@ export type ListItemContextData = {
|
|
|
18
14
|
prompt?: string;
|
|
19
15
|
};
|
|
20
16
|
props: Pick<ListItemProps, 'disabled' | 'inverted'>;
|
|
21
|
-
mediaSize?: ListItemAvatarViewProps['size'];
|
|
22
17
|
describedByIds: string;
|
|
23
18
|
};
|
|
24
19
|
|
|
@@ -19,7 +19,7 @@ export const Navigation = function Navigation({ href = '#', ...props }: ListItem
|
|
|
19
19
|
const { baseItemProps } = useListItemControl('navigation', { href, ...props });
|
|
20
20
|
const { ids, describedByIds } = useContext(ListItemContext);
|
|
21
21
|
const isLink = href !== '#';
|
|
22
|
-
const icon = <ChevronRight size={
|
|
22
|
+
const icon = <ChevronRight size={24} />;
|
|
23
23
|
|
|
24
24
|
if (baseItemProps.disabled) return <BackslashCircle size={24} />;
|
|
25
25
|
|
|
@@ -3,7 +3,7 @@ import { action } from 'storybook/actions';
|
|
|
3
3
|
import { lorem10, lorem5 } from '../../test-utils';
|
|
4
4
|
import Link from '../../link';
|
|
5
5
|
import { Sentiment as Sentiments } from '../../common';
|
|
6
|
-
import { ListItem } from '../..';
|
|
6
|
+
import { List, ListItem } from '../..';
|
|
7
7
|
import { Prompt, ListItemPromptProps } from './ListItemPrompt';
|
|
8
8
|
import {
|
|
9
9
|
SB_LIST_ITEM_CONTROLS as CONTROLS,
|
|
@@ -74,7 +74,7 @@ export const Sentiment: Story = {
|
|
|
74
74
|
knobs: { disable: true },
|
|
75
75
|
},
|
|
76
76
|
render: (args) => (
|
|
77
|
-
<
|
|
77
|
+
<List as="ol">
|
|
78
78
|
<ListItem
|
|
79
79
|
title={lorem5}
|
|
80
80
|
subtitle={lorem10}
|
|
@@ -103,7 +103,7 @@ export const Sentiment: Story = {
|
|
|
103
103
|
control={CONTROLS.switch}
|
|
104
104
|
prompt={<Prompt sentiment={Sentiments.NEGATIVE}>This is a negative prompt.</Prompt>}
|
|
105
105
|
/>
|
|
106
|
-
</
|
|
106
|
+
</List>
|
|
107
107
|
),
|
|
108
108
|
};
|
|
109
109
|
|
|
@@ -122,7 +122,7 @@ export const Interactivity: Story = {
|
|
|
122
122
|
},
|
|
123
123
|
},
|
|
124
124
|
render: (args) => (
|
|
125
|
-
<
|
|
125
|
+
<List as="ol">
|
|
126
126
|
<ListItem
|
|
127
127
|
title={lorem5}
|
|
128
128
|
subtitle={lorem10}
|
|
@@ -154,7 +154,7 @@ export const Interactivity: Story = {
|
|
|
154
154
|
</Prompt>
|
|
155
155
|
}
|
|
156
156
|
/>
|
|
157
|
-
</
|
|
157
|
+
</List>
|
|
158
158
|
),
|
|
159
159
|
};
|
|
160
160
|
|
|
@@ -172,7 +172,7 @@ export const Sizing: Story = {
|
|
|
172
172
|
},
|
|
173
173
|
},
|
|
174
174
|
render: (args) => (
|
|
175
|
-
<
|
|
175
|
+
<List as="ol">
|
|
176
176
|
<ListItem
|
|
177
177
|
title={lorem5}
|
|
178
178
|
subtitle={lorem10}
|
|
@@ -196,6 +196,6 @@ export const Sizing: Story = {
|
|
|
196
196
|
</ListItem.Prompt>
|
|
197
197
|
}
|
|
198
198
|
/>
|
|
199
|
-
</
|
|
199
|
+
</List>
|
|
200
200
|
),
|
|
201
201
|
};
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import type { ListItemSwitchProps } from './ListItemSwitch';
|
|
9
9
|
import { fn } from 'storybook/test';
|
|
10
10
|
import { lorem10, lorem5 } from '../../test-utils';
|
|
11
|
+
import { List } from '../List';
|
|
11
12
|
|
|
12
13
|
const meta: Meta<ListItemSwitchProps> = {
|
|
13
14
|
component: ListItem.Switch,
|
|
@@ -50,7 +51,7 @@ export const Playground: Story = {
|
|
|
50
51
|
export const States: Story = {
|
|
51
52
|
render: (args) => {
|
|
52
53
|
return (
|
|
53
|
-
<
|
|
54
|
+
<List as="ol">
|
|
54
55
|
<ListItem
|
|
55
56
|
title={lorem5}
|
|
56
57
|
subtitle={lorem10}
|
|
@@ -63,7 +64,7 @@ export const States: Story = {
|
|
|
63
64
|
media={MEDIA.avatarSingle}
|
|
64
65
|
control={<ListItem.Switch {...args} />}
|
|
65
66
|
/>
|
|
66
|
-
</
|
|
67
|
+
</List>
|
|
67
68
|
);
|
|
68
69
|
},
|
|
69
70
|
};
|
|
@@ -0,0 +1,226 @@
|
|
|
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 Popover from '../../popover';
|
|
10
|
+
import { SnackbarConsumer, type SnackbarContextType } from '../../snackbar/SnackbarContext';
|
|
11
|
+
import SnackbarProvider from '../../snackbar/SnackbarProvider';
|
|
12
|
+
import Tooltip from '../../tooltip';
|
|
13
|
+
import { Position } from '../../common';
|
|
14
|
+
import { ListItem, type Props as ItemProps } from '../ListItem';
|
|
15
|
+
import {
|
|
16
|
+
SB_LIST_ITEM_ADDITIONAL_INFO as INFO,
|
|
17
|
+
SB_LIST_ITEM_CONTROLS as CONTROLS,
|
|
18
|
+
SB_LIST_ITEM_MEDIA as MEDIA,
|
|
19
|
+
SB_LIST_ITEM_PROMPTS as PROMPTS,
|
|
20
|
+
} from './subcomponents';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Recent storybook versions render the full source including all story parameters which
|
|
24
|
+
* creates noise, this simple workaround only shows the render function.
|
|
25
|
+
* This is a workaround for the issue.
|
|
26
|
+
* @see https://github.com/storybookjs/storybook/issues/22281
|
|
27
|
+
*/
|
|
28
|
+
const storySourceWithoutNoise = (config: Story): Story => {
|
|
29
|
+
const newConfig: Story = {
|
|
30
|
+
...config,
|
|
31
|
+
parameters: {
|
|
32
|
+
...config.parameters,
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
34
|
+
docs: {
|
|
35
|
+
...config.parameters?.docs,
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
37
|
+
source: {
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
39
|
+
...config.parameters?.docs?.source,
|
|
40
|
+
type: 'code' as const,
|
|
41
|
+
transform: (code: string) =>
|
|
42
|
+
// eslint-disable-next-line regexp/no-super-linear-backtracking
|
|
43
|
+
code.replace(/storySourceWithoutNoise\(\{\s+render\s?:\s+(.*)\}\)$/gms, '$1'),
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return newConfig;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default {
|
|
53
|
+
component: ListItem,
|
|
54
|
+
title: 'Content/ListItem/common scenarios',
|
|
55
|
+
tags: ['autodocs'],
|
|
56
|
+
args: {
|
|
57
|
+
title: lorem5,
|
|
58
|
+
subtitle: lorem10,
|
|
59
|
+
media: MEDIA.image,
|
|
60
|
+
additionalInfo: INFO.nonInteractive,
|
|
61
|
+
prompt: PROMPTS.interactive,
|
|
62
|
+
control: CONTROLS.button,
|
|
63
|
+
},
|
|
64
|
+
parameters: {
|
|
65
|
+
docs: {
|
|
66
|
+
toc: true,
|
|
67
|
+
page: () => (
|
|
68
|
+
<>
|
|
69
|
+
<Title>Common scenarios</Title>
|
|
70
|
+
<Subtitle />
|
|
71
|
+
<Description />
|
|
72
|
+
<Stories />
|
|
73
|
+
</>
|
|
74
|
+
),
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
} satisfies Meta<ItemProps>;
|
|
78
|
+
type Story = StoryObj<ItemProps>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Aside from using the main control for triggering a [Modal](?path=/docs/dialogs-modal--docs),
|
|
82
|
+
* it's also possible to toggle it via inline-button as part of the `<ListItem.AdditionalInfo>`
|
|
83
|
+
* or `<ListItem.Prompt>`.
|
|
84
|
+
*/
|
|
85
|
+
export const WithModals: Story = storySourceWithoutNoise({
|
|
86
|
+
render: function WithModals(listItemProps) {
|
|
87
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
88
|
+
|
|
89
|
+
const handleOpening = () => {
|
|
90
|
+
setIsModalOpen(true);
|
|
91
|
+
action('Modal opening')();
|
|
92
|
+
};
|
|
93
|
+
const handleClosing = () => {
|
|
94
|
+
setIsModalOpen(false);
|
|
95
|
+
action('Modal closing')();
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<>
|
|
100
|
+
<ListItem
|
|
101
|
+
{...listItemProps}
|
|
102
|
+
control={
|
|
103
|
+
<ListItem.Button partiallyInteractive onClick={handleOpening}>
|
|
104
|
+
Open modal
|
|
105
|
+
</ListItem.Button>
|
|
106
|
+
}
|
|
107
|
+
additionalInfo={
|
|
108
|
+
<ListItem.AdditionalInfo
|
|
109
|
+
action={{ label: 'button opening a modal', onClick: handleOpening }}
|
|
110
|
+
>
|
|
111
|
+
This is additional information that contains a
|
|
112
|
+
</ListItem.AdditionalInfo>
|
|
113
|
+
}
|
|
114
|
+
prompt={
|
|
115
|
+
<ListItem.Prompt sentiment="positive">
|
|
116
|
+
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
|
|
117
|
+
This prompt also has a <Link onClick={handleOpening}>
|
|
118
|
+
button that opens a modal
|
|
119
|
+
</Link>{' '}
|
|
120
|
+
for your convenience.
|
|
121
|
+
</ListItem.Prompt>
|
|
122
|
+
}
|
|
123
|
+
/>
|
|
124
|
+
<Modal body={lorem100} open={isModalOpen} onClose={handleClosing} />
|
|
125
|
+
</>
|
|
126
|
+
);
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The control or inline buttons can trigger a [Popover](?path=/docs/dialogs-popover--docs).
|
|
132
|
+
*/
|
|
133
|
+
export const WithPopover: Story = {
|
|
134
|
+
render: (listItemProps) => {
|
|
135
|
+
return (
|
|
136
|
+
<ListItem
|
|
137
|
+
{...listItemProps}
|
|
138
|
+
control={
|
|
139
|
+
<Popover preferredPlacement={Position.BOTTOM} title={lorem5} content={lorem20}>
|
|
140
|
+
<ListItem.IconButton partiallyInteractive>
|
|
141
|
+
<InfoCircle />
|
|
142
|
+
</ListItem.IconButton>
|
|
143
|
+
</Popover>
|
|
144
|
+
}
|
|
145
|
+
prompt={PROMPTS.nonInteractive}
|
|
146
|
+
/>
|
|
147
|
+
);
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The control or inline buttons can trigger a [Tooltip](?path=/docs/dialogs-tooltip--docs).
|
|
153
|
+
*/
|
|
154
|
+
export const WithTooltip: Story = {
|
|
155
|
+
render: (listItemProps) => {
|
|
156
|
+
return (
|
|
157
|
+
<ListItem
|
|
158
|
+
{...listItemProps}
|
|
159
|
+
control={
|
|
160
|
+
<Tooltip position={Position.BOTTOM} label={lorem10}>
|
|
161
|
+
<ListItem.IconButton partiallyInteractive>
|
|
162
|
+
<InfoCircle />
|
|
163
|
+
</ListItem.IconButton>
|
|
164
|
+
</Tooltip>
|
|
165
|
+
}
|
|
166
|
+
prompt={PROMPTS.nonInteractive}
|
|
167
|
+
/>
|
|
168
|
+
);
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* ListItem is often used as a simple copy and paste button augmented with a [Snackbar](?path=/docs/dialogs-snackbar--docs). <br />
|
|
174
|
+
*
|
|
175
|
+
* **NB** The snippet below uses a simplified example implementation of a clipboard handler,
|
|
176
|
+
* which may not be suitable or sufficient for your needs.
|
|
177
|
+
*/
|
|
178
|
+
export const CopyAndPasteWithSnackbar: Story = storySourceWithoutNoise({
|
|
179
|
+
render: function CopyAndPaste() {
|
|
180
|
+
const title = 'Artie Choke';
|
|
181
|
+
const subtitle = 'Account holder';
|
|
182
|
+
|
|
183
|
+
const handleCopy = async (createSnackbar: SnackbarContextType['createSnackbar']) =>
|
|
184
|
+
navigator.clipboard
|
|
185
|
+
.writeText(`${subtitle}: ${title}`)
|
|
186
|
+
.catch((err) => {
|
|
187
|
+
console.error('Failed to copy text: ', err);
|
|
188
|
+
})
|
|
189
|
+
.then(() => {
|
|
190
|
+
createSnackbar({
|
|
191
|
+
text: `${subtitle} copied to clipboard.`,
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<ListItem
|
|
197
|
+
title={title}
|
|
198
|
+
subtitle={subtitle}
|
|
199
|
+
inverted
|
|
200
|
+
control={
|
|
201
|
+
<SnackbarProvider>
|
|
202
|
+
<SnackbarConsumer>
|
|
203
|
+
{({ createSnackbar }) => {
|
|
204
|
+
return (
|
|
205
|
+
<ListItem.IconButton
|
|
206
|
+
partiallyInteractive
|
|
207
|
+
onClick={async () => handleCopy(createSnackbar)}
|
|
208
|
+
>
|
|
209
|
+
<Documents />
|
|
210
|
+
</ListItem.IconButton>
|
|
211
|
+
);
|
|
212
|
+
}}
|
|
213
|
+
</SnackbarConsumer>
|
|
214
|
+
</SnackbarProvider>
|
|
215
|
+
}
|
|
216
|
+
media={
|
|
217
|
+
<ListItem.AvatarView
|
|
218
|
+
imgSrc="../avatar-square-dude.webp"
|
|
219
|
+
profileName="John Smith"
|
|
220
|
+
size={48}
|
|
221
|
+
/>
|
|
222
|
+
}
|
|
223
|
+
/>
|
|
224
|
+
);
|
|
225
|
+
},
|
|
226
|
+
});
|