@transferwise/components 0.0.0-experimental-e1870f3 → 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 (106) hide show
  1. package/build/flowNavigation/animatedLabel/AnimatedLabel.js +2 -2
  2. package/build/flowNavigation/animatedLabel/AnimatedLabel.js.map +1 -1
  3. package/build/flowNavigation/animatedLabel/AnimatedLabel.mjs +2 -2
  4. package/build/flowNavigation/animatedLabel/AnimatedLabel.mjs.map +1 -1
  5. package/build/index.js +2 -2
  6. package/build/index.mjs +1 -1
  7. package/build/{listItem → legacylistItem}/List.js +2 -3
  8. package/build/legacylistItem/List.js.map +1 -0
  9. package/build/{listItem → legacylistItem}/List.mjs +2 -3
  10. package/build/legacylistItem/List.mjs.map +1 -0
  11. package/build/listItem/AvatarLayout/ListItemAvatarLayout.js +2 -0
  12. package/build/listItem/AvatarLayout/ListItemAvatarLayout.js.map +1 -1
  13. package/build/listItem/AvatarLayout/ListItemAvatarLayout.mjs +2 -0
  14. package/build/listItem/AvatarLayout/ListItemAvatarLayout.mjs.map +1 -1
  15. package/build/listItem/AvatarView/ListItemAvatarView.js +2 -0
  16. package/build/listItem/AvatarView/ListItemAvatarView.js.map +1 -1
  17. package/build/listItem/AvatarView/ListItemAvatarView.mjs +2 -0
  18. package/build/listItem/AvatarView/ListItemAvatarView.mjs.map +1 -1
  19. package/build/listItem/Image/ListItemImage.js +6 -2
  20. package/build/listItem/Image/ListItemImage.js.map +1 -1
  21. package/build/listItem/Image/ListItemImage.mjs +6 -2
  22. package/build/listItem/Image/ListItemImage.mjs.map +1 -1
  23. package/build/listItem/ListItem.js +16 -14
  24. package/build/listItem/ListItem.js.map +1 -1
  25. package/build/listItem/ListItem.mjs +16 -14
  26. package/build/listItem/ListItem.mjs.map +1 -1
  27. package/build/listItem/ListItemContext.js.map +1 -1
  28. package/build/listItem/ListItemContext.mjs.map +1 -1
  29. package/build/listItem/Navigation/ListItemNavigation.js +1 -1
  30. package/build/listItem/Navigation/ListItemNavigation.js.map +1 -1
  31. package/build/listItem/Navigation/ListItemNavigation.mjs +1 -1
  32. package/build/listItem/Navigation/ListItemNavigation.mjs.map +1 -1
  33. package/build/listItem/_stories/images/landscape.svg +1 -0
  34. package/build/listItem/_stories/images/portrait.svg +1 -0
  35. package/build/listItem/useListItemMedia.js +21 -0
  36. package/build/listItem/useListItemMedia.js.map +1 -0
  37. package/build/listItem/useListItemMedia.mjs +19 -0
  38. package/build/listItem/useListItemMedia.mjs.map +1 -0
  39. package/build/main.css +64 -101
  40. package/build/styles/avatarView/AvatarView.css +1 -3
  41. package/build/styles/listItem/ListItem.css +63 -98
  42. package/build/styles/listItem/ListItem.grid.css +17 -11
  43. package/build/styles/main.css +64 -101
  44. package/build/types/index.d.ts +4 -4
  45. package/build/types/index.d.ts.map +1 -1
  46. package/build/types/legacylistItem/List.d.ts +6 -0
  47. package/build/types/legacylistItem/List.d.ts.map +1 -0
  48. package/build/types/legacylistItem/index.d.ts +2 -0
  49. package/build/types/legacylistItem/index.d.ts.map +1 -1
  50. package/build/types/listItem/AvatarLayout/ListItemAvatarLayout.d.ts.map +1 -1
  51. package/build/types/listItem/AvatarView/ListItemAvatarView.d.ts.map +1 -1
  52. package/build/types/listItem/Image/ListItemImage.d.ts +1 -1
  53. package/build/types/listItem/Image/ListItemImage.d.ts.map +1 -1
  54. package/build/types/listItem/ListItem.d.ts +1 -1
  55. package/build/types/listItem/ListItem.d.ts.map +1 -1
  56. package/build/types/listItem/ListItemContext.d.ts +4 -0
  57. package/build/types/listItem/ListItemContext.d.ts.map +1 -1
  58. package/build/types/listItem/index.d.ts +0 -2
  59. package/build/types/listItem/index.d.ts.map +1 -1
  60. package/build/types/listItem/test-utils.d.ts +7 -0
  61. package/build/types/listItem/test-utils.d.ts.map +1 -0
  62. package/build/types/listItem/useListItemMedia.d.ts +6 -0
  63. package/build/types/listItem/useListItemMedia.d.ts.map +1 -0
  64. package/package.json +1 -1
  65. package/src/avatarView/AvatarView.css +1 -3
  66. package/src/avatarView/AvatarView.less +1 -2
  67. package/src/flowNavigation/animatedLabel/AnimatedLabel.tsx +1 -1
  68. package/src/index.ts +4 -9
  69. package/src/legacylistItem/LegacyListItem.story.tsx +2 -2
  70. package/src/legacylistItem/LegacyListItem.tests.story.tsx +1 -2
  71. package/src/legacylistItem/List.tsx +10 -0
  72. package/src/legacylistItem/index.ts +2 -0
  73. package/src/listItem/AvatarLayout/ListItemAvatarLayout.story.tsx +4 -5
  74. package/src/listItem/AvatarLayout/ListItemAvatarLayout.tsx +3 -0
  75. package/src/listItem/AvatarView/ListItemAvatarView.story.tsx +4 -5
  76. package/src/listItem/AvatarView/ListItemAvatarView.tsx +3 -0
  77. package/src/listItem/Button/ListItemButton.spec.tsx +10 -35
  78. package/src/listItem/Button/ListItemButton.story.tsx +8 -9
  79. package/src/listItem/IconButton/ListItemIconButton.story.tsx +8 -8
  80. package/src/listItem/Image/ListItemImage.spec.tsx +8 -5
  81. package/src/listItem/Image/ListItemImage.story.tsx +23 -6
  82. package/src/listItem/Image/ListItemImage.tsx +12 -4
  83. package/src/listItem/ListItem.css +63 -98
  84. package/src/listItem/ListItem.grid.css +17 -11
  85. package/src/listItem/ListItem.grid.less +24 -16
  86. package/src/listItem/ListItem.less +60 -113
  87. package/src/listItem/ListItem.tsx +24 -18
  88. package/src/listItem/ListItemContext.tsx +5 -0
  89. package/src/listItem/Navigation/ListItemNavigation.tsx +1 -1
  90. package/src/listItem/Prompt/ListItemPrompt.story.tsx +7 -7
  91. package/src/listItem/Switch/ListItemSwitch.story.tsx +2 -3
  92. package/src/listItem/_stories/ListItem.layout.test.story.tsx +142 -2
  93. package/src/listItem/_stories/ListItem.story.tsx +7 -45
  94. package/src/listItem/_stories/ListItem.variants.test.story.tsx +11 -7
  95. package/src/listItem/_stories/images/landscape.svg +1 -0
  96. package/src/listItem/_stories/images/portrait.svg +1 -0
  97. package/src/listItem/index.ts +0 -3
  98. package/src/listItem/test-utils.tsx +33 -0
  99. package/src/listItem/useListItemMedia.tsx +13 -0
  100. package/src/main.css +64 -101
  101. package/build/listItem/List.js.map +0 -1
  102. package/build/listItem/List.mjs.map +0 -1
  103. package/build/types/listItem/List.d.ts +0 -7
  104. package/build/types/listItem/List.d.ts.map +0 -1
  105. package/src/listItem/List.tsx +0 -11
  106. package/src/listItem/_stories/ListItem.scenarios.story.tsx +0 -226
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transferwise/components",
3
- "version": "0.0.0-experimental-e1870f3",
3
+ "version": "0.0.0-experimental-302d967",
4
4
  "description": "Neptune React components",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -19,8 +19,7 @@
19
19
  right: 0;
20
20
  }
21
21
  .np-avatar-view .np-avatar-view-content {
22
- color: #37517e;
23
- color: var(--color-content-primary);
22
+ color: var(--color-interactive-primary);
24
23
  }
25
24
  .np-avatar-view-interactive {
26
25
  cursor: pointer;
@@ -28,7 +27,6 @@
28
27
  .np-avatar-view-interactive .np-circle {
29
28
  background-color: rgba(134,167,189,0.10196);
30
29
  background-color: var(--color-background-neutral);
31
- color: var(--color-interactive-primary);
32
30
  }
33
31
  .np-avatar-view-non-interactive .np-circle {
34
32
  background-color: transparent;
@@ -2,14 +2,13 @@
2
2
 
3
3
  .np-avatar-view {
4
4
  .np-avatar-view-content {
5
- color: var(--color-content-primary);
5
+ color: var(--color-interactive-primary);
6
6
  }
7
7
 
8
8
  &-interactive {
9
9
  cursor: pointer;
10
10
  .np-circle {
11
11
  background-color: var(--color-background-neutral);
12
- color: var(--color-interactive-primary);
13
12
  }
14
13
  }
15
14
 
@@ -6,7 +6,7 @@ import BottomSheet from '../../common/bottomSheet';
6
6
  import Option from '../../common/Option';
7
7
  import { Check, ChevronDown } from '@transferwise/icons';
8
8
  import { OverlayIdContext, OverlayIdProvider } from '../../provider/overlay/OverlayIdProvider';
9
- import { List } from '../../listItem';
9
+ import { List } from '../../legacylistItem';
10
10
 
11
11
  export interface AnimatedLabelProps {
12
12
  activeLabel: number;
package/src/index.ts CHANGED
@@ -50,7 +50,7 @@ export type {
50
50
  export type { TextAreaProps } from './inputs/TextArea';
51
51
  export type { InstructionsListProps } from './instructionsList';
52
52
  export type { LabelProps, LabelOptionalProps, LabelDescriptionProps } from './label/Label';
53
- export type { LegacyListItemProps } from './legacylistItem';
53
+ export type { ListProps, LegacyListItemProps } from './legacylistItem';
54
54
  export type { LoaderProps } from './loader';
55
55
  export type { MarkdownProps } from './markdown';
56
56
  export type { ModalProps } from './modal';
@@ -108,12 +108,7 @@ export type {
108
108
  TableCellStatus,
109
109
  TableCellType,
110
110
  } from './table';
111
- export type {
112
- ListItemProps,
113
- ListProps,
114
- ListItemAdditionalInfoProps,
115
- ListItemCheckboxProps,
116
- } from './listItem';
111
+ export type { ListItemProps, ListItemAdditionalInfoProps, ListItemCheckboxProps } from './listItem';
117
112
 
118
113
  /**
119
114
  * Components
@@ -171,7 +166,7 @@ export { TextArea } from './inputs/TextArea';
171
166
  export { default as InstructionsList } from './instructionsList';
172
167
  export { Label } from './label/Label';
173
168
  export { default as Link } from './link';
174
- export { default as LegacyListItem } from './legacylistItem';
169
+ export { List, default as LegacyListItem } from './legacylistItem';
175
170
  export { default as Loader } from './loader';
176
171
  export { default as Logo } from './logo';
177
172
  export { default as Markdown } from './markdown';
@@ -215,7 +210,7 @@ export { default as Typeahead } from './typeahead';
215
210
  export { default as Upload } from './upload';
216
211
  export { default as UploadInput } from './uploadInput';
217
212
  export { default as Table } from './table';
218
- export { default as ListItem, List } from './listItem';
213
+ export { default as ListItem } from './listItem';
219
214
 
220
215
  /**
221
216
  * Hooks
@@ -2,11 +2,11 @@ import { action } from 'storybook/actions';
2
2
  import { Documents, FastFlag } from '@transferwise/icons';
3
3
  import { ComponentProps } from 'react';
4
4
 
5
- import { Button, IconButton, List } from '..';
5
+ import { Button, IconButton } from '..';
6
6
  import AvatarView from '../avatarView';
7
7
  import Info from '../info';
8
8
  import Title from '../title/Title';
9
- import LegacyListItem from '.';
9
+ import LegacyListItem, { List } from '.';
10
10
 
11
11
  export default {
12
12
  component: LegacyListItem,
@@ -1,8 +1,7 @@
1
1
  import { FastFlag } from '@transferwise/icons';
2
2
  import Button from '../button';
3
3
  import AvatarView from '../avatarView';
4
- import LegacyListItem, { type LegacyListItemProps } from '.';
5
- import { List } from '../listItem';
4
+ import LegacyListItem, { List, type LegacyListItemProps } from '.';
6
5
 
7
6
  export default {
8
7
  component: LegacyListItem,
@@ -0,0 +1,10 @@
1
+ import { clsx } from 'clsx';
2
+
3
+ export interface ListProps {
4
+ className?: string;
5
+ children?: React.ReactNode;
6
+ }
7
+
8
+ export function List({ className, children }: ListProps) {
9
+ return <ul className={clsx(className, 'list-unstyled')}>{children}</ul>;
10
+ }
@@ -1,2 +1,4 @@
1
+ export { List } from './List';
2
+ export type { ListProps } from './List';
1
3
  export { default } from './LegacyListItem';
2
4
  export type { LegacyListItemProps } from './LegacyListItem';
@@ -2,7 +2,6 @@ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { Flag } from '@wise/art';
3
3
  import { lorem10 } from '../../test-utils';
4
4
  import { ListItem } from '../ListItem';
5
- import { List } from '../List';
6
5
  import {
7
6
  SB_LIST_ITEM_ADDITIONAL_INFO as INFO,
8
7
  SB_LIST_ITEM_CONTROLS as CONTROLS,
@@ -66,7 +65,7 @@ export const Playground: Story = {
66
65
  export const Orientations: Story = {
67
66
  render: (args) => {
68
67
  return (
69
- <List as="ol">
68
+ <ol className="list-unstyled">
70
69
  <ListItem
71
70
  title="Horizontal orientation"
72
71
  subtitle={lorem10}
@@ -80,7 +79,7 @@ export const Orientations: Story = {
80
79
  media={<ListItem.AvatarLayout {...args} orientation="diagonal" />}
81
80
  control={CONTROLS.iconButton}
82
81
  />
83
- </List>
82
+ </ol>
84
83
  );
85
84
  },
86
85
  };
@@ -101,7 +100,7 @@ export const Sizes: Story = {
101
100
  const orientations = ['horizontal', 'diagonal'] as const;
102
101
 
103
102
  return (
104
- <List as="ol">
103
+ <ol className="list-unstyled">
105
104
  {orientations.map((orientation) =>
106
105
  sizes.map((size) => (
107
106
  <ListItem
@@ -113,7 +112,7 @@ export const Sizes: Story = {
113
112
  />
114
113
  )),
115
114
  )}
116
- </List>
115
+ </ol>
117
116
  );
118
117
  },
119
118
  };
@@ -1,5 +1,6 @@
1
1
  import AvatarLayoutComp, { type AvatarLayoutProps } from '../../avatarLayout';
2
2
  import { clsx } from 'clsx';
3
+ import { useListItemMedia } from '../useListItemMedia';
3
4
 
4
5
  type SizeProp = { size?: 32 | 40 | 48 | 56 | 72 };
5
6
 
@@ -13,6 +14,8 @@ export type ListItemAvatarLayoutProps = Omit<AvatarLayoutProps, 'size' | 'intera
13
14
  * Please refer to the [Design documentation](https://wise.design/components/list-item#avatar) for details.
14
15
  */
15
16
  export const AvatarLayout = ({ className, size = 48, ...props }: ListItemAvatarLayoutProps) => {
17
+ useListItemMedia(size);
18
+
16
19
  return (
17
20
  <AvatarLayoutComp
18
21
  {...props}
@@ -8,7 +8,6 @@ import {
8
8
  import { ListItemAvatarViewProps } from './ListItemAvatarView';
9
9
  import { lorem10 } from '../../test-utils';
10
10
  import { ProfileType } from '../../common';
11
- import { List } from '../List';
12
11
 
13
12
  const SIZES = [32, 40, 48, 56, 72] as const;
14
13
  const BADGES = {
@@ -213,7 +212,7 @@ export const Sizes: Story = {
213
212
  },
214
213
  render: (args) => {
215
214
  return (
216
- <List as="ol">
215
+ <ol className="list-unstyled">
217
216
  {SIZES.map((size) => (
218
217
  <ListItem
219
218
  key={size}
@@ -227,7 +226,7 @@ export const Sizes: Story = {
227
226
  control={CONTROLS.iconButton}
228
227
  />
229
228
  ))}
230
- </List>
229
+ </ol>
231
230
  );
232
231
  },
233
232
  };
@@ -249,7 +248,7 @@ export const Notification: Story = {
249
248
  },
250
249
  render: (args) => {
251
250
  return (
252
- <List as="ol">
251
+ <ol className="list-unstyled">
253
252
  {SIZES.map((size) => (
254
253
  <ListItem
255
254
  key={size}
@@ -263,7 +262,7 @@ export const Notification: Story = {
263
262
  control={CONTROLS.iconButton}
264
263
  />
265
264
  ))}
266
- </List>
265
+ </ol>
267
266
  );
268
267
  },
269
268
  };
@@ -1,5 +1,6 @@
1
1
  import { clsx } from 'clsx';
2
2
  import AvatarViewComp, { AvatarViewProps } from '../../avatarView';
3
+ import { useListItemMedia } from '../useListItemMedia';
3
4
 
4
5
  export type ListItemAvatarViewProps = Omit<AvatarViewProps, 'size' | 'interactive'> & {
5
6
  size?: 32 | 40 | 48 | 56 | 72;
@@ -13,6 +14,8 @@ export type ListItemAvatarViewProps = Omit<AvatarViewProps, 'size' | 'interactiv
13
14
  * Please refer to the [Design documentation](https://wise.design/components/list-item#avatar) for details.
14
15
  */
15
16
  export const AvatarView = ({ className, size = 48, ...props }: ListItemAvatarViewProps) => {
17
+ useListItemMedia(size);
18
+
16
19
  return (
17
20
  <AvatarViewComp
18
21
  {...props}
@@ -1,49 +1,24 @@
1
- import { render, screen, mockMatchMedia } from '../../test-utils';
1
+ import { screen, mockMatchMedia } from '../../test-utils';
2
2
  import { Button as ItemButton } from './ListItemButton';
3
3
  import { ButtonPriority } from '../../button/Button.types';
4
- import { ListItemContext } from '../ListItemContext';
4
+ import { renderWithListItemContext, clearListItemMocks, mockSetControlType } from '../test-utils';
5
5
 
6
6
  mockMatchMedia();
7
7
 
8
8
  describe('ItemButton', () => {
9
- const mockSetControlType = jest.fn();
10
- const mockSetControlProps = jest.fn();
11
-
12
- const renderWithItemContext = (ui: React.ReactNode) => {
13
- return render(
14
- <ListItemContext.Provider
15
- value={{
16
- setControlType: mockSetControlType,
17
- setControlProps: mockSetControlProps,
18
- ids: {
19
- title: 'title',
20
- additionalInfo: 'additional Info',
21
- valueTitle: 'value title',
22
- control: 'control',
23
- prompt: 'prompt',
24
- },
25
- props: {},
26
- describedByIds: 'described-by-ids',
27
- }}
28
- >
29
- {ui}
30
- </ListItemContext.Provider>,
31
- );
32
- };
33
-
34
9
  beforeEach(() => {
35
- jest.clearAllMocks();
10
+ clearListItemMocks();
36
11
  });
37
12
 
38
13
  it('always sets v2 to true', () => {
39
- renderWithItemContext(<ItemButton priority="primary">Test Button</ItemButton>);
14
+ renderWithListItemContext(<ItemButton priority="primary">Test Button</ItemButton>);
40
15
  const button = screen.getByRole('button');
41
16
  expect(button).toBeInTheDocument();
42
17
  expect(mockSetControlType).toHaveBeenCalledWith('button');
43
18
  });
44
19
 
45
20
  it('always sets size to "sm"', () => {
46
- renderWithItemContext(<ItemButton priority="primary">Test Button</ItemButton>);
21
+ renderWithListItemContext(<ItemButton priority="primary">Test Button</ItemButton>);
47
22
  const button = screen.getByRole('button');
48
23
  expect(button).toHaveClass('wds-Button--small');
49
24
  });
@@ -51,21 +26,21 @@ describe('ItemButton', () => {
51
26
  it('supports all priorities', () => {
52
27
  const priorities: ButtonPriority[] = ['primary', 'secondary', 'tertiary'];
53
28
  priorities.forEach((priority) => {
54
- renderWithItemContext(<ItemButton priority={priority}>Test {priority}</ItemButton>);
29
+ renderWithListItemContext(<ItemButton priority={priority}>Test {priority}</ItemButton>);
55
30
  const button = screen.getByRole('button', { name: `Test ${priority}` });
56
31
  expect(button).toBeInTheDocument();
57
32
  });
58
33
  });
59
34
 
60
35
  it('renders as a button by default', () => {
61
- renderWithItemContext(<ItemButton>Click me</ItemButton>);
36
+ renderWithListItemContext(<ItemButton>Click me</ItemButton>);
62
37
  const button = screen.getByRole('button');
63
38
  expect(button).toBeInTheDocument();
64
39
  expect(button.tagName).toBe('BUTTON');
65
40
  });
66
41
 
67
42
  it('renders as an anchor when href is provided', () => {
68
- renderWithItemContext(<ItemButton href="https://example.com">Go to Example</ItemButton>);
43
+ renderWithListItemContext(<ItemButton href="https://example.com">Go to Example</ItemButton>);
69
44
  const link = screen.getByRole('link', { name: 'Go to Example' });
70
45
  expect(link).toBeInTheDocument();
71
46
  expect(link.tagName).toBe('A');
@@ -73,14 +48,14 @@ describe('ItemButton', () => {
73
48
  });
74
49
 
75
50
  it('spreads additional props to the button', () => {
76
- renderWithItemContext(<ItemButton aria-label="Custom Button">Custom</ItemButton>);
51
+ renderWithListItemContext(<ItemButton aria-label="Custom Button">Custom</ItemButton>);
77
52
  const button = screen.getByRole('button', { name: 'Custom Button' });
78
53
  expect(button).toBeInTheDocument();
79
54
  expect(button).toHaveAttribute('aria-label', 'Custom Button');
80
55
  });
81
56
 
82
57
  it('spreads additional props to the anchor', () => {
83
- renderWithItemContext(
58
+ renderWithListItemContext(
84
59
  <ItemButton href="https://example.com" target="_blank" aria-label="Custom Link">
85
60
  Custom Link
86
61
  </ItemButton>,
@@ -7,7 +7,6 @@ import {
7
7
  SB_LIST_ITEM_MEDIA as MEDIA,
8
8
  } from '../_stories/subcomponents';
9
9
  import type { ListItemButtonProps } from './ListItemButton';
10
- import { List } from '../List';
11
10
 
12
11
  /**
13
12
  * Convenience controls for previewing rich markup,
@@ -182,7 +181,7 @@ export const Interactivity: Story = {
182
181
  },
183
182
  render: (args: ListItemButtonProps) => {
184
183
  return (
185
- <List as="ol">
184
+ <ol className="list-unstyled">
186
185
  <ListItem
187
186
  title="Fully interactive ListItem"
188
187
  subtitle="Button in fully interactive context"
@@ -196,7 +195,7 @@ export const Interactivity: Story = {
196
195
  media={MEDIA.avatarSingle}
197
196
  control={<ListItem.Button {...args} partiallyInteractive />}
198
197
  />
199
- </List>
198
+ </ol>
200
199
  );
201
200
  },
202
201
  };
@@ -231,7 +230,7 @@ export const AsAnchor: Story = {
231
230
  export const Sentiment: Story = {
232
231
  render: (args: ListItemButtonProps) => {
233
232
  return (
234
- <List as="ol">
233
+ <ol className="list-unstyled">
235
234
  <ListItem
236
235
  title="Default Sentiment - Primary"
237
236
  subtitle="Default sentiment with primary priority"
@@ -272,7 +271,7 @@ export const Sentiment: Story = {
272
271
  </ListItem.Button>
273
272
  }
274
273
  />
275
- </List>
274
+ </ol>
276
275
  );
277
276
  },
278
277
  args: {
@@ -288,7 +287,7 @@ export const Sentiment: Story = {
288
287
  export const Priority: Story = {
289
288
  render: (args: ListItemButtonProps) => {
290
289
  return (
291
- <List as="ol">
290
+ <ol className="list-unstyled">
292
291
  <ListItem
293
292
  title="Primary Priority"
294
293
  subtitle="Primary priority (default)"
@@ -329,7 +328,7 @@ export const Priority: Story = {
329
328
  </ListItem.Button>
330
329
  }
331
330
  />
332
- </List>
331
+ </ol>
333
332
  );
334
333
  },
335
334
  args: {
@@ -369,7 +368,7 @@ const addonProps = {
369
368
  export const WithIcons: Story = {
370
369
  render: (args: ListItemButtonProps) => {
371
370
  return (
372
- <List as="ol">
371
+ <ol className="list-unstyled">
373
372
  <ListItem
374
373
  title="With Start Icon"
375
374
  subtitle="Button with icon before the label"
@@ -400,7 +399,7 @@ export const WithIcons: Story = {
400
399
  </ListItem.Button>
401
400
  }
402
401
  />
403
- </List>
402
+ </ol>
404
403
  );
405
404
  },
406
405
  args: {
@@ -1,6 +1,6 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-webpack5';
2
- import ListItem, { List } from '..';
3
- import { Edit } from '@transferwise/icons';
2
+ import { Edit, Heart } from '@transferwise/icons';
3
+ import { ListItem } from '../ListItem';
4
4
  import {
5
5
  SB_LIST_ITEM_ADDITIONAL_INFO as INFO,
6
6
  SB_LIST_ITEM_MEDIA as MEDIA,
@@ -114,7 +114,7 @@ export const Interactivity: Story = {
114
114
  },
115
115
  render: (args: ListItemIconButtonProps) => {
116
116
  return (
117
- <List as="ol">
117
+ <ol className="list-unstyled">
118
118
  <ListItem
119
119
  control={<ListItem.IconButton {...args} />}
120
120
  title="Fully interactive ListItem"
@@ -132,7 +132,7 @@ export const Interactivity: Story = {
132
132
  additionalInfo={INFO.interactive}
133
133
  prompt={PROMPTS.interactive}
134
134
  />
135
- </List>
135
+ </ol>
136
136
  );
137
137
  },
138
138
  };
@@ -145,7 +145,7 @@ export const Interactivity: Story = {
145
145
  export const Sentiment: Story = {
146
146
  render: (args) => {
147
147
  return (
148
- <List as="ol">
148
+ <ol className="list-unstyled">
149
149
  <ListItem
150
150
  control={<ListItem.IconButton {...args} priority="primary" type="default" />}
151
151
  title="Default Sentiment - Primary"
@@ -170,7 +170,7 @@ export const Sentiment: Story = {
170
170
  subtitle="Negative sentiment with secondary priority"
171
171
  media={MEDIA.avatarSingle}
172
172
  />
173
- </List>
173
+ </ol>
174
174
  );
175
175
  },
176
176
  };
@@ -183,7 +183,7 @@ export const Sentiment: Story = {
183
183
  export const Priority: Story = {
184
184
  render: (args) => {
185
185
  return (
186
- <List as="ol">
186
+ <ol className="list-unstyled">
187
187
  <ListItem
188
188
  title="Primary Priority"
189
189
  subtitle="Primary priority (default)"
@@ -208,7 +208,7 @@ export const Priority: Story = {
208
208
  media={MEDIA.avatarSingle}
209
209
  control={<ListItem.IconButton {...args} priority="minimal" />}
210
210
  />
211
- </List>
211
+ </ol>
212
212
  );
213
213
  },
214
214
  };
@@ -1,14 +1,17 @@
1
1
  import { render, screen } from '../../test-utils';
2
- import { ListItem } from '../ListItem';
2
+ import { ListItem, type Props as ListItemProps } from '../ListItem';
3
+
4
+ const renderWithMedia = (media: ListItemProps['media']) =>
5
+ render(<ListItem title="Test Title" media={media} />);
3
6
 
4
7
  describe('ListItem.Image', () => {
5
8
  it('renders image with presentation role when no alt text', () => {
6
- render(<ListItem.Image src="test-image.jpg" />);
9
+ renderWithMedia(<ListItem.Image src="test-image.jpg" />);
7
10
  expect(screen.getByRole('presentation')).toHaveAttribute('src', 'test-image.jpg');
8
11
  });
9
12
 
10
13
  it('renders image with graphics-symbol role when alt text provided', () => {
11
- render(<ListItem.Image src="test-image.jpg" alt="Test image description" />);
14
+ renderWithMedia(<ListItem.Image src="test-image.jpg" alt="Test image description" />);
12
15
 
13
16
  const image = screen.getByRole('graphics-symbol');
14
17
  expect(image).toHaveAttribute('alt', 'Test image description');
@@ -16,12 +19,12 @@ describe('ListItem.Image', () => {
16
19
  });
17
20
 
18
21
  it('renders image with loading prop', () => {
19
- render(<ListItem.Image src="test-image.jpg" loading="lazy" />);
22
+ renderWithMedia(<ListItem.Image src="test-image.jpg" loading="lazy" />);
20
23
  expect(screen.getByRole('presentation')).toHaveAttribute('loading', 'lazy');
21
24
  });
22
25
 
23
26
  it('renders image with className prop', () => {
24
- render(<ListItem.Image src="test-image.jpg" className="custom-image-class" />);
27
+ renderWithMedia(<ListItem.Image src="test-image.jpg" className="custom-image-class" />);
25
28
  expect(screen.getByRole('presentation')).toHaveClass('custom-image-class');
26
29
  });
27
30
  });
@@ -6,6 +6,12 @@ import {
6
6
  } from '../_stories/subcomponents';
7
7
  import { ListItemImageProps } from './ListItemImage';
8
8
 
9
+ const IMAGE_URLS = {
10
+ portrait: 'https://cdn-icons-png.flaticon.com/512/656/656404.png',
11
+ landscape:
12
+ 'https://wise.com/public-resources/assets/spend/card-asset/medium/v2/personal_green_wise_2023_active.svg',
13
+ };
14
+
9
15
  export default {
10
16
  component: ListItem.Image,
11
17
  title: 'Content/ListItem/ListItem.Image',
@@ -15,22 +21,33 @@ export default {
15
21
  },
16
22
  },
17
23
  args: {
18
- src: 'https://wise.com/public-resources/assets/spend/card-asset/medium/v2/personal_green_wise_2023_active.svg',
19
- size: undefined,
24
+ src: IMAGE_URLS.landscape,
25
+ size: 72,
20
26
  loading: undefined,
21
27
  alt: undefined,
28
+ imageOrientation: 'landscape' as keyof typeof IMAGE_URLS,
22
29
  },
23
- } satisfies Meta<ListItemImageProps>;
30
+ argTypes: {
31
+ imageOrientation: {
32
+ control: { type: 'radio' },
33
+ options: ['portrait', 'landscape'],
34
+ description: 'Switch between portrait and landscape images',
35
+ },
36
+ },
37
+ } satisfies Meta<ListItemImageProps & { imageOrientation: keyof typeof IMAGE_URLS }>;
24
38
 
25
- type Story = StoryObj<ListItemImageProps>;
39
+ type Story = StoryObj<ListItemImageProps & { imageOrientation: keyof typeof IMAGE_URLS }>;
26
40
 
27
41
  export const Playground: Story = {
28
- render: (args: ListItemImageProps) => {
42
+ render: (args: ListItemImageProps & { imageOrientation: keyof typeof IMAGE_URLS }) => {
43
+ const { imageOrientation, ...imageProps } = args;
44
+ const src = IMAGE_URLS[imageOrientation];
45
+
29
46
  return (
30
47
  <ListItem
31
48
  title="List item title"
32
49
  subtitle="Subtitle goes here"
33
- media={<ListItem.Image {...args} />}
50
+ media={<ListItem.Image {...imageProps} src={src} />}
34
51
  control={CONTROLS.partialButton}
35
52
  additionalInfo={INFO.nonInteractive}
36
53
  />
@@ -1,5 +1,6 @@
1
1
  import ImageComp, { type ImageProps } from '../../image';
2
2
  import { clsx } from 'clsx';
3
+ import { useListItemMedia } from '../useListItemMedia';
3
4
 
4
5
  type SizeProp = {};
5
6
 
@@ -23,17 +24,24 @@ export type ListItemImageProps = Omit<ImageProps, 'stretch' | 'shrink' | 'id' |
23
24
  *
24
25
  * > **NB**: This component is [not intended for use with illustrations](https://wise.design/foundations/illustration#scale).
25
26
  */
26
- export const Image = ({ alt = '', size = 48, ...props }: ListItemImageProps) => {
27
+ export const Image = ({ alt = '', size = 48, className, ...props }: ListItemImageProps) => {
28
+ useListItemMedia(size);
29
+
27
30
  return (
28
31
  <div
29
- className={clsx('wds-list-item-media-image')}
32
+ className={clsx('wds-list-item-media-image-wrapper')}
30
33
  style={
31
34
  {
32
- '--item-media-image-size': `${size}px`,
35
+ '--wds-list-item-media-size': `${size}px`,
33
36
  } as React.CSSProperties
34
37
  }
35
38
  >
36
- <ImageComp {...props} alt={alt} role={alt ? 'graphics-symbol' : 'presentation'} />
39
+ <ImageComp
40
+ {...props}
41
+ className={clsx(className, 'wds-list-item-media-image')}
42
+ alt={alt}
43
+ role={alt ? 'graphics-symbol' : 'presentation'}
44
+ />
37
45
  </div>
38
46
  );
39
47
  };