@transferwise/components 0.0.0-experimental-a12c97e → 0.0.0-experimental-302d967

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 (87) hide show
  1. package/build/listItem/AvatarLayout/ListItemAvatarLayout.js +2 -0
  2. package/build/listItem/AvatarLayout/ListItemAvatarLayout.js.map +1 -1
  3. package/build/listItem/AvatarLayout/ListItemAvatarLayout.mjs +2 -0
  4. package/build/listItem/AvatarLayout/ListItemAvatarLayout.mjs.map +1 -1
  5. package/build/listItem/AvatarView/ListItemAvatarView.js +2 -0
  6. package/build/listItem/AvatarView/ListItemAvatarView.js.map +1 -1
  7. package/build/listItem/AvatarView/ListItemAvatarView.mjs +2 -0
  8. package/build/listItem/AvatarView/ListItemAvatarView.mjs.map +1 -1
  9. package/build/listItem/Image/ListItemImage.js +6 -2
  10. package/build/listItem/Image/ListItemImage.js.map +1 -1
  11. package/build/listItem/Image/ListItemImage.mjs +6 -2
  12. package/build/listItem/Image/ListItemImage.mjs.map +1 -1
  13. package/build/listItem/ListItem.js +16 -14
  14. package/build/listItem/ListItem.js.map +1 -1
  15. package/build/listItem/ListItem.mjs +16 -14
  16. package/build/listItem/ListItem.mjs.map +1 -1
  17. package/build/listItem/ListItemContext.js.map +1 -1
  18. package/build/listItem/ListItemContext.mjs.map +1 -1
  19. package/build/listItem/Navigation/ListItemNavigation.js +1 -1
  20. package/build/listItem/Navigation/ListItemNavigation.js.map +1 -1
  21. package/build/listItem/Navigation/ListItemNavigation.mjs +1 -1
  22. package/build/listItem/Navigation/ListItemNavigation.mjs.map +1 -1
  23. package/build/listItem/_stories/images/landscape.svg +1 -0
  24. package/build/listItem/_stories/images/portrait.svg +1 -0
  25. package/build/listItem/useListItemMedia.js +21 -0
  26. package/build/listItem/useListItemMedia.js.map +1 -0
  27. package/build/listItem/useListItemMedia.mjs +19 -0
  28. package/build/listItem/useListItemMedia.mjs.map +1 -0
  29. package/build/main.css +64 -101
  30. package/build/styles/avatarView/AvatarView.css +1 -3
  31. package/build/styles/listItem/ListItem.css +63 -98
  32. package/build/styles/listItem/ListItem.grid.css +17 -11
  33. package/build/styles/main.css +64 -101
  34. package/build/switch/Switch.js.map +1 -1
  35. package/build/switch/Switch.mjs.map +1 -1
  36. package/build/types/listItem/AvatarLayout/ListItemAvatarLayout.d.ts.map +1 -1
  37. package/build/types/listItem/AvatarView/ListItemAvatarView.d.ts.map +1 -1
  38. package/build/types/listItem/Image/ListItemImage.d.ts +1 -1
  39. package/build/types/listItem/Image/ListItemImage.d.ts.map +1 -1
  40. package/build/types/listItem/ListItem.d.ts +1 -1
  41. package/build/types/listItem/ListItem.d.ts.map +1 -1
  42. package/build/types/listItem/ListItemContext.d.ts +4 -0
  43. package/build/types/listItem/ListItemContext.d.ts.map +1 -1
  44. package/build/types/listItem/test-utils.d.ts +7 -0
  45. package/build/types/listItem/test-utils.d.ts.map +1 -0
  46. package/build/types/listItem/useListItemMedia.d.ts +6 -0
  47. package/build/types/listItem/useListItemMedia.d.ts.map +1 -0
  48. package/build/types/switch/Switch.d.ts +1 -1
  49. package/build/types/switch/Switch.d.ts.map +1 -1
  50. package/package.json +3 -3
  51. package/src/avatarView/AvatarView.css +1 -3
  52. package/src/avatarView/AvatarView.less +1 -2
  53. package/src/listItem/AdditionalInfo/ListItemAdditionalInfo.story.tsx +8 -58
  54. package/src/listItem/AvatarLayout/ListItemAvatarLayout.story.tsx +0 -3
  55. package/src/listItem/AvatarLayout/ListItemAvatarLayout.tsx +3 -0
  56. package/src/listItem/AvatarView/ListItemAvatarView.story.tsx +3 -21
  57. package/src/listItem/AvatarView/ListItemAvatarView.tsx +3 -0
  58. package/src/listItem/Button/ListItemButton.spec.tsx +10 -35
  59. package/src/listItem/Button/ListItemButton.story.tsx +37 -95
  60. package/src/listItem/Checkbox/ListItemCheckbox.story.tsx +0 -18
  61. package/src/listItem/IconButton/ListItemIconButton.story.tsx +2 -15
  62. package/src/listItem/Image/ListItemImage.spec.tsx +8 -5
  63. package/src/listItem/Image/ListItemImage.story.tsx +23 -6
  64. package/src/listItem/Image/ListItemImage.tsx +12 -4
  65. package/src/listItem/ListItem.css +63 -98
  66. package/src/listItem/ListItem.grid.css +17 -11
  67. package/src/listItem/ListItem.grid.less +24 -16
  68. package/src/listItem/ListItem.less +60 -113
  69. package/src/listItem/ListItem.tsx +24 -18
  70. package/src/listItem/ListItemContext.tsx +5 -0
  71. package/src/listItem/Navigation/ListItemNavigation.story.tsx +2 -5
  72. package/src/listItem/Navigation/ListItemNavigation.tsx +1 -1
  73. package/src/listItem/Radio/ListItemRadio.story.tsx +0 -10
  74. package/src/listItem/Switch/ListItemSwitch.story.tsx +0 -8
  75. package/src/listItem/_stories/ListItem.layout.test.story.tsx +142 -2
  76. package/src/listItem/_stories/ListItem.story.tsx +141 -243
  77. package/src/listItem/_stories/ListItem.variants.test.story.tsx +7 -2
  78. package/src/listItem/_stories/images/landscape.svg +1 -0
  79. package/src/listItem/_stories/images/portrait.svg +1 -0
  80. package/src/listItem/test-utils.tsx +33 -0
  81. package/src/listItem/useListItemMedia.tsx +13 -0
  82. package/src/main.css +64 -101
  83. package/src/switch/Switch.tsx +1 -1
  84. package/build/types/listItem/_stories/helpers.d.ts +0 -27
  85. package/build/types/listItem/_stories/helpers.d.ts.map +0 -1
  86. package/src/listItem/_stories/ListItem.scenarios.story.tsx +0 -209
  87. package/src/listItem/_stories/helpers.tsx +0 -53
@@ -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) 0;
10
+ padding: var(--size-12);
11
11
  container-type: inline-size;
12
12
 
13
13
  & + .wds-list-item-spotlight,
@@ -47,6 +47,12 @@
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
+
50
56
  .wds-list-item-additional-info {
51
57
  .ring-offset-0
52
58
  }
@@ -70,102 +76,58 @@
70
76
  }
71
77
  }
72
78
 
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);
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;
79
+ &:not(.disabled, :disabled):hover {
80
+ background-color: var(--color-background-screen-hover);
81
+
82
+ .wds-list-item-control-wrapper {
83
+ .wds-Button {
84
+ background-color: var(--Button-background-hover);
85
+ color: var(--Button-color-hover);
86
+ transition: none;
87
+ }
95
88
  }
96
- }
97
89
  }
98
90
 
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
- }
91
+ &:not(.disabled, :disabled):active {
92
+ background-color: var(--color-background-screen-active);
139
93
  }
140
94
 
141
- &:not(.disabled, :disabled):hover {
142
- .wds-list-item-control-wrapper {
95
+ .wds-list-item-button-control {
143
96
  .wds-Button {
144
- background-color: var(--Button-background-hover);
145
- color: var(--Button-color-hover);
146
97
  transition: none;
147
98
  }
148
99
  }
149
- }
150
100
 
151
- .wds-list-item-button-control {
152
- .wds-Button {
153
- transition: none;
154
- }
101
+ &:has(.wds-list-item-prompt:hover a, .wds-list-item-prompt:hover button) {
102
+ &:hover {
103
+ background-color: unset;
104
+ }
155
105
  }
156
106
  }
157
107
 
158
108
  &-media {
159
109
  grid-area: media;
110
+ display: flex;
111
+ align-items: center;
160
112
 
161
- &-image {
162
- width: var(--item-media-image-size);
163
- height: var(--item-media-image-size);
164
- }
113
+ &-image-wrapper {
114
+ width: var(--wds-list-item-media-size);
115
+ height: var(--wds-list-item-media-size);
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ }
120
+
121
+ &-image {
122
+ width: 100%;
123
+ height: 100%;
124
+ object-fit: contain;
125
+ }
165
126
  }
166
127
 
167
128
  &-body {
168
129
  grid-area: body;
130
+ align-items: center;
169
131
  width: 100%;
170
132
  display: grid;
171
133
  grid-template-columns: var(--wds-list-item-body-left, 1fr) var(--wds-list-item-body-right, max-content);
@@ -173,8 +135,18 @@
173
135
  word-break: break-word;
174
136
  }
175
137
 
176
- &-title, &-title-value {
177
- color: var(--color-content-primary);
138
+ &-title {
139
+ color: var(--color-content-primary);
140
+ }
141
+
142
+ &-value {
143
+ text-align: right;
144
+ }
145
+
146
+ &-body-center {
147
+ display: flex;
148
+ flex-direction: row;
149
+ align-items: center;
178
150
  }
179
151
 
180
152
  &-additional-info {
@@ -183,13 +155,16 @@
183
155
  margin-top: calc(var(--size-4) * -1);
184
156
  }
185
157
 
186
- &-value {
187
- align-items: end;
188
- text-align: right;
189
- }
190
-
191
158
  &-control-wrapper {
192
159
  grid-area: control;
160
+ align-content: center;
161
+ // max-height: var(--wds-list-item-control-wrapper-height);
162
+ }
163
+
164
+ &-navigation {
165
+ .tw-icon-chevron-right {
166
+ color: var(--color-interactive-secondary);
167
+ }
193
168
  }
194
169
 
195
170
  &-control {
@@ -212,9 +187,6 @@
212
187
 
213
188
 
214
189
  &-spotlight {
215
- padding-left: var(--size-12);
216
- padding-right: var(--size-12);
217
-
218
190
  &-active {
219
191
  background-color: var(--color-background-neutral);
220
192
  &:not(.disabled, :disabled):hover {
@@ -227,7 +199,7 @@
227
199
 
228
200
  &-inactive {
229
201
  background-color: color-mix(in srgb, var(--color-background-neutral) 25%, transparent);
230
-
202
+ border: 1px dashed var(--color-border-neutral);
231
203
  &:not(.disabled, :disabled):hover {
232
204
  background-color: color-mix(in srgb, var(--color-background-neutral-hover) 25%, transparent);
233
205
  }
@@ -237,31 +209,6 @@
237
209
  }
238
210
  }
239
211
 
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
-
265
212
  &-prompt {
266
213
  grid-area: prompt;
267
214
 
@@ -4,6 +4,7 @@ import {
4
4
  useId,
5
5
  useMemo,
6
6
  useState,
7
+ useEffect,
7
8
  type PropsWithChildren,
8
9
  type ReactNode,
9
10
  } from 'react';
@@ -22,7 +23,11 @@ import { AvatarView } from './AvatarView';
22
23
  import { Image } from './Image';
23
24
  import { Prompt } from './Prompt';
24
25
  import { PrimitiveAnchor } from '../primitives';
25
- import { ListItemContext, type ListItemContextData } from './ListItemContext';
26
+ import {
27
+ ListItemContext,
28
+ type ListItemContextData,
29
+ type ListItemMediaSize,
30
+ } from './ListItemContext';
26
31
 
27
32
  export type ListItemTypes =
28
33
  | 'non-interactive'
@@ -119,6 +124,8 @@ export const ListItem = ({
119
124
  const idPrefix = useId();
120
125
  const [controlProps, setControlProps] = useState<ListItemControlProps>({});
121
126
  const [controlType, setControlType] = useState<ListItemTypes>('non-interactive');
127
+ const [mediaSize, setMediaSize] = useState<ListItemMediaSize | undefined>();
128
+
122
129
  const ids: ListItemContextData['ids'] = {
123
130
  title: `${idPrefix}_title`,
124
131
  ...(subtitle ? { subtitle: `${idPrefix}_subtitle` } : {}),
@@ -156,11 +163,13 @@ export const ListItem = ({
156
163
  () => ({
157
164
  setControlType,
158
165
  setControlProps,
166
+ setMediaSize,
159
167
  ids,
160
168
  props: { disabled, inverted },
169
+ mediaSize,
161
170
  describedByIds,
162
171
  }),
163
- [describedByIds],
172
+ [describedByIds, mediaSize],
164
173
  );
165
174
  const gridColumnsStyle = {
166
175
  '--wds-list-item-body-left': valueColumnWidth ? `${100 - valueColumnWidth}fr` : '50fr',
@@ -227,12 +236,6 @@ export const ListItem = ({
227
236
  aria-disabled={disabled}
228
237
  aria-labelledby={ariaLabelledby}
229
238
  >
230
- {spotlight === 'inactive' && (
231
- <svg aria-hidden="true" className="wds-list-item-spotlight__border">
232
- <rect />
233
- </svg>
234
- )}
235
-
236
239
  <View
237
240
  {...{
238
241
  isPartiallyInteractive,
@@ -253,7 +256,11 @@ export const ListItem = ({
253
256
  style={valueColumnWidth ? gridColumnsStyle : undefined}
254
257
  >
255
258
  {/* Title + Subtitle + Values - Group */}
256
- <span>
259
+ <span
260
+ className={clsx({
261
+ 'wds-list-item-body-center': title && !subtitle,
262
+ })}
263
+ >
257
264
  {(() => {
258
265
  const titles = [
259
266
  <Body
@@ -280,6 +287,8 @@ export const ListItem = ({
280
287
  <span
281
288
  className={clsx('wds-list-item-value', {
282
289
  'flex-column': valueTitle !== undefined || valueSubtitle !== undefined,
290
+ 'wds-list-item-body-center':
291
+ (valueTitle && !valueSubtitle) || (!valueTitle && valueSubtitle),
283
292
  })}
284
293
  >
285
294
  {(() => {
@@ -318,6 +327,11 @@ export const ListItem = ({
318
327
  className={clsx('wds-list-item-control-wrapper', {
319
328
  'wds-list-item-button-control': controlType === 'button',
320
329
  })}
330
+ style={
331
+ {
332
+ '--wds-list-item-control-wrapper-height': mediaSize ? `${mediaSize}px` : 'auto',
333
+ } as React.CSSProperties
334
+ }
321
335
  >
322
336
  {control}
323
337
  </Body>
@@ -370,7 +384,6 @@ function View({
370
384
  href={(controlProps as ListItemNavigationProps)?.href}
371
385
  target={(controlProps as ListItemNavigationProps)?.target}
372
386
  className={clsx('wds-list-item-view d-flex flex-row', {
373
- 'align-items-center': !subtitle,
374
387
  'wds-list-item-control': controlType === 'navigation',
375
388
  })}
376
389
  disabled={disabled}
@@ -386,13 +399,7 @@ function View({
386
399
  if (isPartiallyInteractive || controlType === 'non-interactive') {
387
400
  return (
388
401
  <div className={clsx('wds-list-item-gridWrapper', className)}>
389
- <div
390
- className={clsx('wds-list-item-view d-flex flex-row', {
391
- 'align-items-center': !subtitle,
392
- })}
393
- >
394
- {children}
395
- </div>
402
+ <div className={clsx('wds-list-item-view d-flex flex-row')}>{children}</div>
396
403
 
397
404
  {renderExtras()}
398
405
  </div>
@@ -407,7 +414,6 @@ function View({
407
414
  <label
408
415
  htmlFor={ids.control}
409
416
  className={clsx('wds-list-item-view', {
410
- 'align-items-center': !subtitle,
411
417
  clickable: !disabled,
412
418
  fullyInteractive: !isPartiallyInteractive,
413
419
  })}
@@ -1,9 +1,13 @@
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;
3
6
 
4
7
  export type ListItemContextData = {
5
8
  setControlType: (type: ListItemTypes) => void;
6
9
  setControlProps: (props: ListItemControlProps) => void;
10
+ setMediaSize: (size: ListItemMediaSize | undefined) => void;
7
11
  ids: {
8
12
  title: string;
9
13
  subtitle?: string;
@@ -14,6 +18,7 @@ export type ListItemContextData = {
14
18
  prompt?: string;
15
19
  };
16
20
  props: Pick<ListItemProps, 'disabled' | 'inverted'>;
21
+ mediaSize?: ListItemAvatarViewProps['size'];
17
22
  describedByIds: string;
18
23
  };
19
24
 
@@ -1,6 +1,4 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-webpack5';
2
- import { fn } from 'storybook/test';
3
- import { lorem10 } from '../../test-utils';
4
2
  import { ListItem } from '../ListItem';
5
3
  import {
6
4
  SB_LIST_ITEM_ADDITIONAL_INFO as INFO,
@@ -8,6 +6,8 @@ import {
8
6
  SB_LIST_ITEM_MEDIA as MEDIA,
9
7
  } from '../_stories/subcomponents';
10
8
  import type { ListItemNavigationProps } from './ListItemNavigation';
9
+ import { lorem10 } from '../../test-utils';
10
+ import { fn } from 'storybook/test';
11
11
 
12
12
  const meta: Meta<ListItemNavigationProps> = {
13
13
  component: ListItem.Navigation,
@@ -62,9 +62,6 @@ export const AsButton: Story = {
62
62
  args: {
63
63
  onClick: fn(),
64
64
  },
65
- parameters: {
66
- controls: { disable: true },
67
- },
68
65
  render: (args) => {
69
66
  return (
70
67
  <ol className="list-unstyled">
@@ -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={24} />;
22
+ const icon = <ChevronRight size={16} />;
23
23
 
24
24
  if (baseItemProps.disabled) return <BackslashCircle size={24} />;
25
25
 
@@ -23,13 +23,6 @@ const meta: Meta<ListItemRadioProps> = {
23
23
  checked: false,
24
24
  onChange: fn(),
25
25
  },
26
- argTypes: {
27
- onChange: {
28
- table: {
29
- type: { summary: '(value: string | number) => void' },
30
- },
31
- },
32
- },
33
26
  } satisfies Meta<ListItemRadioProps>;
34
27
 
35
28
  export default meta;
@@ -55,9 +48,6 @@ export const Playground: Story = {
55
48
  * Radio controls follow the native HTML behaviour and can be grouped using the `name` prop.
56
49
  */
57
50
  export const RadioGroup: Story = {
58
- parameters: {
59
- controls: { disable: true },
60
- },
61
51
  render: function Render() {
62
52
  const [selectedValue, setSelectedValue] = useState('2');
63
53
 
@@ -25,11 +25,6 @@ const meta: Meta<ListItemSwitchProps> = {
25
25
  checked: {
26
26
  control: 'boolean',
27
27
  },
28
- onClick: {
29
- table: {
30
- type: { summary: '(event?: MouseEvent<HTMLButtonElement>) => void' },
31
- },
32
- },
33
28
  },
34
29
  } satisfies Meta<ListItemSwitchProps>;
35
30
 
@@ -53,9 +48,6 @@ export const Playground: Story = {
53
48
  };
54
49
 
55
50
  export const States: Story = {
56
- parameters: {
57
- controls: { disable: true },
58
- },
59
51
  render: (args) => {
60
52
  return (
61
53
  <ol className="list-unstyled">
@@ -2,8 +2,12 @@ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { Bank, FastFlag, MultiCurrency, Receipt, Savings } from '@transferwise/icons';
3
3
  import Link from '../../link';
4
4
  import { ListItem, Props as ItemProps } from '../ListItem';
5
- import { lorem10, lorem5 } from '../../test-utils';
5
+ import { lorem10, lorem20, lorem5 } from '../../test-utils';
6
6
  import { SB_LIST_ITEM_CONTROLS as CONTROLS, SB_LIST_ITEM_MEDIA as MEDIA } from './subcomponents';
7
+ import portraitImage from './images/portrait.svg';
8
+ import landscapeImage from './images/landscape.svg';
9
+ import type { ListItemButtonProps } from '../Button';
10
+ import { ListItemMediaSize } from '../ListItemContext';
7
11
 
8
12
  const withSizedContainer = (width: number) => (Story: any) => (
9
13
  <ol
@@ -20,7 +24,6 @@ const withSizedContainer = (width: number) => (Story: any) => (
20
24
  );
21
25
 
22
26
  export default {
23
- component: ListItem,
24
27
  title: 'Content/ListItem/tests/layout',
25
28
  tags: ['!autodocs'],
26
29
  parameters: {
@@ -187,3 +190,140 @@ export const GapsBetweenItems: Story = {
187
190
  );
188
191
  },
189
192
  };
193
+
194
+ type PreviewStoryArgs = ItemProps & {
195
+ previewImageSize: ListItemMediaSize;
196
+ previewWithLineGuides: boolean;
197
+ previewPrompt: boolean | ItemProps['prompt'];
198
+ };
199
+
200
+ const previewArgGroup = {
201
+ category: 'Preview options',
202
+ type: {
203
+ summary: undefined,
204
+ },
205
+ };
206
+
207
+ const previewArgTypes = {
208
+ previewImageSize: {
209
+ options: [32, 40, 48, 56, 72],
210
+ control: {
211
+ type: 'inline-radio',
212
+ },
213
+ name: 'Preview with image size',
214
+ table: previewArgGroup,
215
+ },
216
+ previewWithLineGuides: {
217
+ control: {
218
+ type: 'boolean',
219
+ },
220
+ name: 'Preview with line guides',
221
+ table: previewArgGroup,
222
+ },
223
+ previewPrompt: {
224
+ control: {
225
+ type: 'boolean',
226
+ },
227
+ mapping: {
228
+ true: <ListItem.Prompt sentiment="positive">This is a prompt</ListItem.Prompt>,
229
+ false: null,
230
+ },
231
+ name: 'Preview with `prompt`',
232
+ table: previewArgGroup,
233
+ },
234
+ } as const;
235
+
236
+ const getPropsForPreview = (args: PreviewStoryArgs) => {
237
+ const { previewImageSize, previewWithLineGuides, previewPrompt, ...props } = args as {
238
+ previewImageSize: ListItemMediaSize;
239
+ previewWithLineGuides: boolean;
240
+ previewPrompt: boolean | ItemProps['prompt'];
241
+ [key: string]: any;
242
+ };
243
+
244
+ return [
245
+ {
246
+ ...props,
247
+ prompt: previewPrompt,
248
+ },
249
+ {
250
+ previewImageSize,
251
+ previewWithLineGuides,
252
+ },
253
+ ] as const;
254
+ };
255
+
256
+ // Create a completely untyped story object
257
+ const ImageAlignmentStory: StoryObj<PreviewStoryArgs> = {
258
+ parameters: {
259
+ controls: { disable: false },
260
+ },
261
+ args: {
262
+ previewImageSize: 48,
263
+ previewWithLineGuides: true,
264
+ previewPrompt: false,
265
+ },
266
+ argTypes: previewArgTypes,
267
+ decorators: [
268
+ (Story: any, { args }: { args: PreviewStoryArgs }) => (
269
+ <>
270
+ {args.previewWithLineGuides && (
271
+ <style
272
+ dangerouslySetInnerHTML={{
273
+ __html: `
274
+ .wds-list-item-media-image-wrapper::after {
275
+ content: '';
276
+ border: 1px dashed red;
277
+ position: absolute;
278
+ left: 0;
279
+ width: 100%;
280
+ height: ${args.previewImageSize}px;
281
+ }
282
+ `,
283
+ }}
284
+ />
285
+ )}
286
+ <Story />
287
+ </>
288
+ ),
289
+ ],
290
+ render: (args: PreviewStoryArgs) => {
291
+ const [props, previewProps] = getPropsForPreview(args);
292
+
293
+ const title = lorem5;
294
+ const subtitle = lorem10;
295
+ const additionalInfo = <ListItem.AdditionalInfo>{lorem20}</ListItem.AdditionalInfo>;
296
+ const control = CONTROLS.button;
297
+ const size = previewProps.previewImageSize;
298
+
299
+ const instances = [
300
+ { title, ...props },
301
+ { title, subtitle, ...props },
302
+ { title, subtitle, additionalInfo, ...props },
303
+ { title, control, ...props },
304
+ { title, subtitle, control, ...props },
305
+ { title, subtitle, additionalInfo, control, ...props },
306
+ ] as const;
307
+
308
+ return (
309
+ <ol className="list-unstyled">
310
+ {instances.map((itemProps, index) => (
311
+ <ListItem
312
+ key={`landscape-${index}`}
313
+ {...itemProps}
314
+ media={<ListItem.Image size={size} src={landscapeImage} alt="landscape image" />}
315
+ />
316
+ ))}
317
+ {instances.map((itemProps, index) => (
318
+ <ListItem
319
+ key={`portrait-${index}`}
320
+ {...itemProps}
321
+ media={<ListItem.Image size={size} src={portraitImage} alt="portrait image" />}
322
+ />
323
+ ))}
324
+ </ol>
325
+ );
326
+ },
327
+ };
328
+
329
+ export const ImageAlignment = ImageAlignmentStory;