@transferwise/components 46.103.1 → 46.105.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/header/Header.js +60 -43
- package/build/header/Header.js.map +1 -1
- package/build/header/Header.mjs +57 -43
- package/build/header/Header.mjs.map +1 -1
- package/build/i18n/cs.json +2 -0
- package/build/i18n/cs.json.js +2 -0
- package/build/i18n/cs.json.js.map +1 -1
- package/build/i18n/cs.json.mjs +2 -0
- package/build/i18n/cs.json.mjs.map +1 -1
- package/build/i18n/es.json +2 -0
- package/build/i18n/es.json.js +2 -0
- package/build/i18n/es.json.js.map +1 -1
- package/build/i18n/es.json.mjs +2 -0
- package/build/i18n/es.json.mjs.map +1 -1
- package/build/i18n/th.json +2 -0
- package/build/i18n/th.json.js +2 -0
- package/build/i18n/th.json.js.map +1 -1
- package/build/i18n/th.json.mjs +2 -0
- package/build/i18n/th.json.mjs.map +1 -1
- package/build/index.js +3 -1
- package/build/index.js.map +1 -1
- package/build/index.mjs +2 -1
- package/build/index.mjs.map +1 -1
- package/build/inputs/SelectInput.js +1 -1
- package/build/inputs/SelectInput.js.map +1 -1
- package/build/inputs/SelectInput.mjs +1 -1
- package/build/listItem/AdditionalInfo/ListItemAdditionalInfo.js +56 -0
- package/build/listItem/AdditionalInfo/ListItemAdditionalInfo.js.map +1 -0
- package/build/listItem/AdditionalInfo/ListItemAdditionalInfo.mjs +54 -0
- package/build/listItem/AdditionalInfo/ListItemAdditionalInfo.mjs.map +1 -0
- package/build/listItem/AvatarLayout/ListItemAvatarLayout.js +23 -0
- package/build/listItem/AvatarLayout/ListItemAvatarLayout.js.map +1 -0
- package/build/listItem/AvatarLayout/ListItemAvatarLayout.mjs +21 -0
- package/build/listItem/AvatarLayout/ListItemAvatarLayout.mjs.map +1 -0
- package/build/listItem/AvatarView/ListItemAvatarView.js +23 -0
- package/build/listItem/AvatarView/ListItemAvatarView.js.map +1 -0
- package/build/listItem/AvatarView/ListItemAvatarView.mjs +21 -0
- package/build/listItem/AvatarView/ListItemAvatarView.mjs.map +1 -0
- package/build/listItem/Button/ListItemButton.js +43 -0
- package/build/listItem/Button/ListItemButton.js.map +1 -0
- package/build/listItem/Button/ListItemButton.mjs +41 -0
- package/build/listItem/Button/ListItemButton.mjs.map +1 -0
- package/build/listItem/Checkbox/ListItemCheckbox.js +30 -0
- package/build/listItem/Checkbox/ListItemCheckbox.js.map +1 -0
- package/build/listItem/Checkbox/ListItemCheckbox.mjs +28 -0
- package/build/listItem/Checkbox/ListItemCheckbox.mjs.map +1 -0
- package/build/listItem/IconButton/ListItemIconButton.js +56 -0
- package/build/listItem/IconButton/ListItemIconButton.js.map +1 -0
- package/build/listItem/IconButton/ListItemIconButton.mjs +54 -0
- package/build/listItem/IconButton/ListItemIconButton.mjs.map +1 -0
- package/build/listItem/Image/ListItemImage.js +31 -0
- package/build/listItem/Image/ListItemImage.js.map +1 -0
- package/build/listItem/Image/ListItemImage.mjs +29 -0
- package/build/listItem/Image/ListItemImage.mjs.map +1 -0
- package/build/listItem/ListItem.js +311 -0
- package/build/listItem/ListItem.js.map +1 -0
- package/build/listItem/ListItem.mjs +306 -0
- package/build/listItem/ListItem.mjs.map +1 -0
- package/build/listItem/ListItemContext.js +8 -0
- package/build/listItem/ListItemContext.js.map +1 -0
- package/build/listItem/ListItemContext.mjs +6 -0
- package/build/listItem/ListItemContext.mjs.map +1 -0
- package/build/listItem/Navigation/ListItemNavigation.js +44 -0
- package/build/listItem/Navigation/ListItemNavigation.js.map +1 -0
- package/build/listItem/Navigation/ListItemNavigation.mjs +42 -0
- package/build/listItem/Navigation/ListItemNavigation.mjs.map +1 -0
- package/build/listItem/Prompt/ListItemPrompt.js +59 -0
- package/build/listItem/Prompt/ListItemPrompt.js.map +1 -0
- package/build/listItem/Prompt/ListItemPrompt.mjs +54 -0
- package/build/listItem/Prompt/ListItemPrompt.mjs.map +1 -0
- package/build/listItem/Radio/ListItemRadio.js +30 -0
- package/build/listItem/Radio/ListItemRadio.js.map +1 -0
- package/build/listItem/Radio/ListItemRadio.mjs +28 -0
- package/build/listItem/Radio/ListItemRadio.mjs.map +1 -0
- package/build/listItem/Switch/ListItemSwitch.js +30 -0
- package/build/listItem/Switch/ListItemSwitch.js.map +1 -0
- package/build/listItem/Switch/ListItemSwitch.mjs +28 -0
- package/build/listItem/Switch/ListItemSwitch.mjs.map +1 -0
- package/build/listItem/useListItemControl.js +22 -0
- package/build/listItem/useListItemControl.js.map +1 -0
- package/build/listItem/useListItemControl.mjs +20 -0
- package/build/listItem/useListItemControl.mjs.map +1 -0
- package/build/listItem/useListItemMedia.js +21 -0
- package/build/listItem/useListItemMedia.js.map +1 -0
- package/build/listItem/useListItemMedia.mjs +19 -0
- package/build/listItem/useListItemMedia.mjs.map +1 -0
- package/build/main.css +794 -14
- package/build/styles/header/Header.css +21 -14
- package/build/styles/listItem/ListItem.css +773 -0
- package/build/styles/listItem/ListItem.grid.css +370 -0
- package/build/styles/listItem/Prompt/ListItemPrompt.css +157 -0
- package/build/styles/main.css +794 -14
- package/build/title/Title.js +10 -4
- package/build/title/Title.js.map +1 -1
- package/build/title/Title.mjs +6 -4
- package/build/title/Title.mjs.map +1 -1
- package/build/types/header/Header.d.ts +27 -11
- package/build/types/header/Header.d.ts.map +1 -1
- package/build/types/header/index.d.ts +1 -0
- package/build/types/header/index.d.ts.map +1 -1
- package/build/types/index.d.ts +3 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/listItem/AdditionalInfo/ListItemAdditionalInfo.d.ts +15 -0
- package/build/types/listItem/AdditionalInfo/ListItemAdditionalInfo.d.ts.map +1 -0
- package/build/types/listItem/AdditionalInfo/index.d.ts +3 -0
- package/build/types/listItem/AdditionalInfo/index.d.ts.map +1 -0
- package/build/types/listItem/AvatarLayout/ListItemAvatarLayout.d.ts +18 -0
- package/build/types/listItem/AvatarLayout/ListItemAvatarLayout.d.ts.map +1 -0
- package/build/types/listItem/AvatarLayout/index.d.ts +3 -0
- package/build/types/listItem/AvatarLayout/index.d.ts.map +1 -0
- package/build/types/listItem/AvatarView/ListItemAvatarView.d.ts +16 -0
- package/build/types/listItem/AvatarView/ListItemAvatarView.d.ts.map +1 -0
- package/build/types/listItem/AvatarView/index.d.ts +3 -0
- package/build/types/listItem/AvatarView/index.d.ts.map +1 -0
- package/build/types/listItem/Button/ListItemButton.d.ts +20 -0
- package/build/types/listItem/Button/ListItemButton.d.ts.map +1 -0
- package/build/types/listItem/Button/index.d.ts +3 -0
- package/build/types/listItem/Button/index.d.ts.map +1 -0
- package/build/types/listItem/Checkbox/ListItemCheckbox.d.ts +14 -0
- package/build/types/listItem/Checkbox/ListItemCheckbox.d.ts.map +1 -0
- package/build/types/listItem/Checkbox/index.d.ts +3 -0
- package/build/types/listItem/Checkbox/index.d.ts.map +1 -0
- package/build/types/listItem/IconButton/ListItemIconButton.d.ts +18 -0
- package/build/types/listItem/IconButton/ListItemIconButton.d.ts.map +1 -0
- package/build/types/listItem/IconButton/index.d.ts +3 -0
- package/build/types/listItem/IconButton/index.d.ts.map +1 -0
- package/build/types/listItem/Image/ListItemImage.d.ts +25 -0
- package/build/types/listItem/Image/ListItemImage.d.ts.map +1 -0
- package/build/types/listItem/Image/index.d.ts +3 -0
- package/build/types/listItem/Image/index.d.ts.map +1 -0
- package/build/types/listItem/ListItem.d.ts +111 -0
- package/build/types/listItem/ListItem.d.ts.map +1 -0
- package/build/types/listItem/ListItemContext.d.ts +21 -0
- package/build/types/listItem/ListItemContext.d.ts.map +1 -0
- package/build/types/listItem/Navigation/ListItemNavigation.d.ts +15 -0
- package/build/types/listItem/Navigation/ListItemNavigation.d.ts.map +1 -0
- package/build/types/listItem/Navigation/index.d.ts +3 -0
- package/build/types/listItem/Navigation/index.d.ts.map +1 -0
- package/build/types/listItem/Prompt/ListItemPrompt.d.ts +16 -0
- package/build/types/listItem/Prompt/ListItemPrompt.d.ts.map +1 -0
- package/build/types/listItem/Prompt/index.d.ts +3 -0
- package/build/types/listItem/Prompt/index.d.ts.map +1 -0
- package/build/types/listItem/Radio/ListItemRadio.d.ts +14 -0
- package/build/types/listItem/Radio/ListItemRadio.d.ts.map +1 -0
- package/build/types/listItem/Radio/index.d.ts +3 -0
- package/build/types/listItem/Radio/index.d.ts.map +1 -0
- package/build/types/listItem/Switch/ListItemSwitch.d.ts +14 -0
- package/build/types/listItem/Switch/ListItemSwitch.d.ts.map +1 -0
- package/build/types/listItem/Switch/index.d.ts +3 -0
- package/build/types/listItem/Switch/index.d.ts.map +1 -0
- package/build/types/listItem/_stories/helpers.d.ts +27 -0
- package/build/types/listItem/_stories/helpers.d.ts.map +1 -0
- package/build/types/listItem/_stories/subcomponents.d.ts +18 -0
- package/build/types/listItem/_stories/subcomponents.d.ts.map +1 -0
- package/build/types/listItem/index.d.ts +14 -0
- package/build/types/listItem/index.d.ts.map +1 -0
- package/build/types/listItem/test-utils.d.ts +7 -0
- package/build/types/listItem/test-utils.d.ts.map +1 -0
- package/build/types/listItem/useListItemControl.d.ts +5 -0
- package/build/types/listItem/useListItemControl.d.ts.map +1 -0
- package/build/types/listItem/useListItemMedia.d.ts +6 -0
- package/build/types/listItem/useListItemMedia.d.ts.map +1 -0
- package/build/types/title/Title.d.ts +4 -5
- package/build/types/title/Title.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/button/Button.spec.tsx +25 -1
- package/src/button/Button.story.tsx +1 -0
- package/src/header/Header.accessibility.docs.mdx +85 -0
- package/src/header/Header.css +21 -14
- package/src/header/Header.less +17 -10
- package/src/header/Header.spec.tsx +68 -50
- package/src/header/Header.story.tsx +190 -36
- package/src/header/Header.tsx +96 -65
- package/src/header/index.ts +1 -0
- package/src/i18n/cs.json +2 -0
- package/src/i18n/es.json +2 -0
- package/src/i18n/th.json +2 -0
- package/src/iconButton/iconButton.spec.tsx +31 -0
- package/src/index.ts +16 -0
- package/src/legacylistItem/LegacyListItem.story.tsx +1 -1
- package/src/legacylistItem/LegacyListItem.tests.story.tsx +2 -1
- package/src/list/List.story.tsx +13 -3
- package/src/listItem/AdditionalInfo/ListItemAdditionalInfo.spec.tsx +56 -0
- package/src/listItem/AdditionalInfo/ListItemAdditionalInfo.story.tsx +198 -0
- package/src/listItem/AdditionalInfo/ListItemAdditionalInfo.tsx +36 -0
- package/src/listItem/AdditionalInfo/index.ts +2 -0
- package/src/listItem/AvatarLayout/ListItemAvatarLayout.spec.tsx +59 -0
- package/src/listItem/AvatarLayout/ListItemAvatarLayout.story.tsx +124 -0
- package/src/listItem/AvatarLayout/ListItemAvatarLayout.tsx +27 -0
- package/src/listItem/AvatarLayout/index.ts +2 -0
- package/src/listItem/AvatarView/ListItemAvatarView.spec.tsx +75 -0
- package/src/listItem/AvatarView/ListItemAvatarView.story.tsx +339 -0
- package/src/listItem/AvatarView/ListItemAvatarView.tsx +27 -0
- package/src/listItem/AvatarView/index.ts +2 -0
- package/src/listItem/Button/ListItemButton.spec.tsx +90 -0
- package/src/listItem/Button/ListItemButton.story.tsx +473 -0
- package/src/listItem/Button/ListItemButton.tsx +56 -0
- package/src/listItem/Button/index.ts +2 -0
- package/src/listItem/Checkbox/ListItemCheckbox.spec.tsx +82 -0
- package/src/listItem/Checkbox/ListItemCheckbox.story.tsx +128 -0
- package/src/listItem/Checkbox/ListItemCheckbox.tsx +33 -0
- package/src/listItem/Checkbox/index.ts +2 -0
- package/src/listItem/IconButton/ListItemIconButton.spec.tsx +131 -0
- package/src/listItem/IconButton/ListItemIconButton.story.tsx +284 -0
- package/src/listItem/IconButton/ListItemIconButton.tsx +73 -0
- package/src/listItem/IconButton/index.ts +2 -0
- package/src/listItem/Image/ListItemImage.spec.tsx +30 -0
- package/src/listItem/Image/ListItemImage.story.tsx +80 -0
- package/src/listItem/Image/ListItemImage.tsx +46 -0
- package/src/listItem/Image/index.ts +2 -0
- package/src/listItem/ListItem.css +773 -0
- package/src/listItem/ListItem.grid.css +370 -0
- package/src/listItem/ListItem.grid.less +622 -0
- package/src/listItem/ListItem.less +291 -0
- package/src/listItem/ListItem.spec.tsx +1511 -0
- package/src/listItem/ListItem.tsx +440 -0
- package/src/listItem/ListItemContext.tsx +26 -0
- package/src/listItem/Navigation/ListItemNavigation.spec.tsx +67 -0
- package/src/listItem/Navigation/ListItemNavigation.story.tsx +114 -0
- package/src/listItem/Navigation/ListItemNavigation.tsx +39 -0
- package/src/listItem/Navigation/index.ts +2 -0
- package/src/listItem/Prompt/ListItemPrompt.css +157 -0
- package/src/listItem/Prompt/ListItemPrompt.less +134 -0
- package/src/listItem/Prompt/ListItemPrompt.spec.tsx +36 -0
- package/src/listItem/Prompt/ListItemPrompt.story.tsx +204 -0
- package/src/listItem/Prompt/ListItemPrompt.tsx +32 -0
- package/src/listItem/Prompt/index.ts +2 -0
- package/src/listItem/Radio/ListItemRadio.spec.tsx +66 -0
- package/src/listItem/Radio/ListItemRadio.story.tsx +111 -0
- package/src/listItem/Radio/ListItemRadio.tsx +33 -0
- package/src/listItem/Radio/index.ts +2 -0
- package/src/listItem/Switch/ListItemSwitch.spec.tsx +47 -0
- package/src/listItem/Switch/ListItemSwitch.story.tsx +79 -0
- package/src/listItem/Switch/ListItemSwitch.tsx +33 -0
- package/src/listItem/Switch/index.ts +2 -0
- package/src/listItem/_stories/ListItem.focus.test.story.tsx +265 -0
- package/src/listItem/_stories/ListItem.layout.test.story.tsx +374 -0
- package/src/listItem/_stories/ListItem.scenarios.story.tsx +228 -0
- package/src/listItem/_stories/ListItem.story.tsx +774 -0
- package/src/listItem/_stories/ListItem.variants.test.story.tsx +274 -0
- package/src/listItem/_stories/helpers.tsx +53 -0
- package/src/listItem/_stories/subcomponents.tsx +141 -0
- package/src/listItem/index.ts +14 -0
- package/src/listItem/test-utils.tsx +33 -0
- package/src/listItem/useListItemControl.tsx +18 -0
- package/src/listItem/useListItemMedia.tsx +16 -0
- package/src/main.css +794 -14
- package/src/main.less +1 -0
- package/src/primitives/PrimitiveAnchor/test/PrimitiveAnchor.spec.tsx +15 -4
- package/src/title/Title.tsx +25 -12
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import List from '../../list';
|
|
4
|
+
import { ListItem } from '../ListItem';
|
|
5
|
+
import {
|
|
6
|
+
SB_LIST_ITEM_ADDITIONAL_INFO as INFO,
|
|
7
|
+
SB_LIST_ITEM_MEDIA as MEDIA,
|
|
8
|
+
} from '../_stories/subcomponents';
|
|
9
|
+
import type { ListItemCheckboxProps } from './ListItemCheckbox';
|
|
10
|
+
import { fn } from 'storybook/test';
|
|
11
|
+
|
|
12
|
+
const meta: Meta<ListItemCheckboxProps> = {
|
|
13
|
+
component: ListItem.Checkbox,
|
|
14
|
+
title: 'Content/ListItem/ListItem.Checkbox',
|
|
15
|
+
parameters: {
|
|
16
|
+
docs: {
|
|
17
|
+
toc: true,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
args: {
|
|
21
|
+
checked: false,
|
|
22
|
+
indeterminate: false,
|
|
23
|
+
value: 'checkbox',
|
|
24
|
+
name: 'checkbox',
|
|
25
|
+
onChange: fn(),
|
|
26
|
+
onFocus: fn(),
|
|
27
|
+
onBlur: fn(),
|
|
28
|
+
},
|
|
29
|
+
argTypes: {
|
|
30
|
+
checked: {
|
|
31
|
+
control: 'boolean',
|
|
32
|
+
},
|
|
33
|
+
indeterminate: {
|
|
34
|
+
control: 'boolean',
|
|
35
|
+
},
|
|
36
|
+
onChange: {
|
|
37
|
+
table: {
|
|
38
|
+
type: { summary: '(event: ChangeEvent<HTMLInputElement>) => void' },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
onFocus: {
|
|
42
|
+
table: {
|
|
43
|
+
type: { summary: '(event: FocusEvent<HTMLInputElement>) => void' },
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
onBlur: {
|
|
47
|
+
table: {
|
|
48
|
+
type: { summary: '(event: FocusEvent<HTMLInputElement>) => void' },
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
} satisfies Meta<ListItemCheckboxProps>;
|
|
53
|
+
|
|
54
|
+
export default meta;
|
|
55
|
+
|
|
56
|
+
type Story = StoryObj<ListItemCheckboxProps>;
|
|
57
|
+
|
|
58
|
+
export const Playground: Story = {
|
|
59
|
+
tags: ['!autodocs'],
|
|
60
|
+
render: (args: ListItemCheckboxProps) => {
|
|
61
|
+
return (
|
|
62
|
+
<List>
|
|
63
|
+
<ListItem
|
|
64
|
+
title="List item with checkbox"
|
|
65
|
+
subtitle="Select this option"
|
|
66
|
+
media={MEDIA.avatarSingle}
|
|
67
|
+
control={<ListItem.Checkbox {...args} />}
|
|
68
|
+
additionalInfo={INFO.nonInteractive}
|
|
69
|
+
/>
|
|
70
|
+
</List>
|
|
71
|
+
);
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Checkbox controls demonstrate different states including checked, unchecked, and indeterminate.
|
|
77
|
+
*/
|
|
78
|
+
export const States: Story = {
|
|
79
|
+
parameters: {
|
|
80
|
+
controls: { disable: true },
|
|
81
|
+
},
|
|
82
|
+
render: function Render() {
|
|
83
|
+
const [isChecked0, setisChecked0] = useState(false);
|
|
84
|
+
const [isChecked1, setisChecked1] = useState(true);
|
|
85
|
+
const [isChecked2, setisChecked2] = useState(false);
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<List>
|
|
89
|
+
<ListItem
|
|
90
|
+
control={
|
|
91
|
+
<ListItem.Checkbox
|
|
92
|
+
checked={isChecked0}
|
|
93
|
+
onChange={() => setisChecked0((current) => !current)}
|
|
94
|
+
/>
|
|
95
|
+
}
|
|
96
|
+
title="Unchecked option"
|
|
97
|
+
subtitle="This option is not selected"
|
|
98
|
+
media={MEDIA.avatarSingle}
|
|
99
|
+
/>
|
|
100
|
+
|
|
101
|
+
<ListItem
|
|
102
|
+
control={
|
|
103
|
+
<ListItem.Checkbox
|
|
104
|
+
checked={isChecked1}
|
|
105
|
+
onChange={() => setisChecked1((current) => !current)}
|
|
106
|
+
/>
|
|
107
|
+
}
|
|
108
|
+
title="Checked option"
|
|
109
|
+
subtitle="This option is selected"
|
|
110
|
+
media={MEDIA.avatarSingle}
|
|
111
|
+
/>
|
|
112
|
+
|
|
113
|
+
<ListItem
|
|
114
|
+
control={
|
|
115
|
+
<ListItem.Checkbox
|
|
116
|
+
checked={isChecked2}
|
|
117
|
+
indeterminate
|
|
118
|
+
onChange={() => setisChecked2((current) => !current)}
|
|
119
|
+
/>
|
|
120
|
+
}
|
|
121
|
+
title="Indeterminate option"
|
|
122
|
+
subtitle="This option is partially selected"
|
|
123
|
+
media={MEDIA.avatarSingle}
|
|
124
|
+
/>
|
|
125
|
+
</List>
|
|
126
|
+
);
|
|
127
|
+
},
|
|
128
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import CheckboxButton, { type CheckboxButtonProps } from '../../checkboxButton/CheckboxButton';
|
|
3
|
+
import { useListItemControl } from '../useListItemControl';
|
|
4
|
+
import { ListItemContext } from '../ListItemContext';
|
|
5
|
+
|
|
6
|
+
export type ListItemCheckboxProps = Pick<
|
|
7
|
+
CheckboxButtonProps,
|
|
8
|
+
'checked' | 'indeterminate' | 'onChange' | 'onBlur' | 'onFocus' | 'value' | 'name'
|
|
9
|
+
>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* This component allows for rendering a checkbox control within a fully interactive ListItem. <br />It's a thin wrapper around the
|
|
13
|
+
* [CheckboxButton component](https://storybook.wise.design/?path=/docs/actions-checkboxbutton--docs),
|
|
14
|
+
* but offers only a subset of its features in line with the ListItem's constraints. <br />
|
|
15
|
+
*
|
|
16
|
+
* Please refer to the [Design documentation](https://wise.design/components/list-item---checkbox) for details.
|
|
17
|
+
*/
|
|
18
|
+
export const Checkbox = function (props: ListItemCheckboxProps) {
|
|
19
|
+
const { baseItemProps } = useListItemControl('checkbox', { ...props });
|
|
20
|
+
const { ids, describedByIds } = useContext(ListItemContext);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<CheckboxButton
|
|
24
|
+
{...props}
|
|
25
|
+
className="wds-list-item-control"
|
|
26
|
+
disabled={baseItemProps.disabled}
|
|
27
|
+
id={ids.control}
|
|
28
|
+
aria-describedby={describedByIds}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
Checkbox.displayName = 'ListItem.Checkbox';
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Edit } from '@transferwise/icons';
|
|
2
|
+
import { mockMatchMedia, render, screen, userEvent } from '../../test-utils';
|
|
3
|
+
import { ListItem, type ListItemProps } from '../ListItem';
|
|
4
|
+
|
|
5
|
+
mockMatchMedia();
|
|
6
|
+
|
|
7
|
+
describe('ListItem.IconButton', () => {
|
|
8
|
+
const renderWith = (overrides: Partial<ListItemProps> = {}) =>
|
|
9
|
+
render(<ListItem title="Test title" {...overrides} />);
|
|
10
|
+
|
|
11
|
+
describe('as button', () => {
|
|
12
|
+
it('renders as button by default', () => {
|
|
13
|
+
renderWith({
|
|
14
|
+
control: (
|
|
15
|
+
<ListItem.IconButton>
|
|
16
|
+
<Edit />
|
|
17
|
+
</ListItem.IconButton>
|
|
18
|
+
),
|
|
19
|
+
});
|
|
20
|
+
expect(screen.getByRole('button')).toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('renders as button if onClick is provided', () => {
|
|
24
|
+
renderWith({
|
|
25
|
+
control: (
|
|
26
|
+
<ListItem.IconButton onClick={() => {}}>
|
|
27
|
+
<Edit />
|
|
28
|
+
</ListItem.IconButton>
|
|
29
|
+
),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(screen.getByRole('button')).toBeInTheDocument();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('handles onClick events', async () => {
|
|
36
|
+
const handleClick = jest.fn();
|
|
37
|
+
renderWith({
|
|
38
|
+
control: (
|
|
39
|
+
<ListItem.IconButton onClick={handleClick}>
|
|
40
|
+
<Edit />
|
|
41
|
+
</ListItem.IconButton>
|
|
42
|
+
),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
await userEvent.click(screen.getByRole('button'));
|
|
46
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('is disabled when ListItem is disabled', async () => {
|
|
50
|
+
const handleClick = jest.fn();
|
|
51
|
+
renderWith({
|
|
52
|
+
disabled: true,
|
|
53
|
+
control: (
|
|
54
|
+
<ListItem.IconButton onClick={handleClick}>
|
|
55
|
+
<Edit />
|
|
56
|
+
</ListItem.IconButton>
|
|
57
|
+
),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const button = screen.getByRole('button');
|
|
61
|
+
expect(button).toBeDisabled();
|
|
62
|
+
await userEvent.click(button);
|
|
63
|
+
expect(handleClick).not.toHaveBeenCalled();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('as link', () => {
|
|
68
|
+
it('renders as link when href is provided', () => {
|
|
69
|
+
renderWith({
|
|
70
|
+
control: (
|
|
71
|
+
<ListItem.IconButton href="/test">
|
|
72
|
+
<Edit />
|
|
73
|
+
</ListItem.IconButton>
|
|
74
|
+
),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(screen.getByRole('link')).toHaveAttribute('href', '/test');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('supports target attribute for links', () => {
|
|
81
|
+
renderWith({
|
|
82
|
+
control: (
|
|
83
|
+
<ListItem.IconButton href="/test" target="_blank">
|
|
84
|
+
<Edit />
|
|
85
|
+
</ListItem.IconButton>
|
|
86
|
+
),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const link = screen.getByRole('link');
|
|
90
|
+
expect(link).toHaveAttribute('target', '_blank');
|
|
91
|
+
expect(link).toHaveAttribute('rel', 'noopener noreferrer');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('handles onClick events', async () => {
|
|
95
|
+
const handleClick = jest.fn();
|
|
96
|
+
renderWith({
|
|
97
|
+
control: (
|
|
98
|
+
<ListItem.IconButton href="#test" onClick={handleClick}>
|
|
99
|
+
<Edit />
|
|
100
|
+
</ListItem.IconButton>
|
|
101
|
+
),
|
|
102
|
+
});
|
|
103
|
+
await userEvent.click(screen.getByRole('link'));
|
|
104
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('is disabled when ListItem is disabled', async () => {
|
|
108
|
+
renderWith({
|
|
109
|
+
disabled: true,
|
|
110
|
+
control: (
|
|
111
|
+
<ListItem.IconButton href="/test">
|
|
112
|
+
<Edit />
|
|
113
|
+
</ListItem.IconButton>
|
|
114
|
+
),
|
|
115
|
+
});
|
|
116
|
+
expect(screen.getByRole('link')).toHaveAttribute('aria-disabled', 'true');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('renders children content', () => {
|
|
121
|
+
renderWith({
|
|
122
|
+
control: (
|
|
123
|
+
<ListItem.IconButton>
|
|
124
|
+
<Edit />
|
|
125
|
+
</ListItem.IconButton>
|
|
126
|
+
),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
expect(screen.getByTestId('edit-icon')).toBeInTheDocument();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
import ListItem from '..';
|
|
3
|
+
import { Edit } from '@transferwise/icons';
|
|
4
|
+
import { fn } from 'storybook/test';
|
|
5
|
+
import List from '../../list';
|
|
6
|
+
import {
|
|
7
|
+
SB_LIST_ITEM_ADDITIONAL_INFO as INFO,
|
|
8
|
+
SB_LIST_ITEM_MEDIA as MEDIA,
|
|
9
|
+
SB_LIST_ITEM_PROMPTS as PROMPTS,
|
|
10
|
+
} from '../_stories/subcomponents';
|
|
11
|
+
import { disableControls } from '../_stories/helpers';
|
|
12
|
+
import type { ListItemIconButtonProps } from './ListItemIconButton';
|
|
13
|
+
|
|
14
|
+
const hideControls = disableControls([
|
|
15
|
+
'onClick',
|
|
16
|
+
'children',
|
|
17
|
+
'className',
|
|
18
|
+
'addonStart',
|
|
19
|
+
'addonEnd',
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Use IconButton to provide compact action controls within a ListItem context using iconography.
|
|
24
|
+
*
|
|
25
|
+
* Refer to the [design documentation](https://wise.design/components/list-item) for more details.
|
|
26
|
+
*/
|
|
27
|
+
const meta: Meta<ListItemIconButtonProps> = {
|
|
28
|
+
component: ListItem.IconButton,
|
|
29
|
+
title: 'Content/ListItem/ListItem.IconButton',
|
|
30
|
+
parameters: {
|
|
31
|
+
docs: {
|
|
32
|
+
toc: true,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
args: {
|
|
36
|
+
partiallyInteractive: false,
|
|
37
|
+
priority: undefined,
|
|
38
|
+
type: undefined,
|
|
39
|
+
'aria-label': 'Edit details',
|
|
40
|
+
children: <Edit />,
|
|
41
|
+
href: undefined,
|
|
42
|
+
target: undefined,
|
|
43
|
+
onClick: fn(),
|
|
44
|
+
},
|
|
45
|
+
argTypes: {
|
|
46
|
+
children: {
|
|
47
|
+
description: 'Once of the [Icon components](https://transferwise.github.io/icons/).',
|
|
48
|
+
table: {
|
|
49
|
+
type: { summary: 'ReactNode' },
|
|
50
|
+
},
|
|
51
|
+
control: false,
|
|
52
|
+
},
|
|
53
|
+
priority: {
|
|
54
|
+
control: 'radio',
|
|
55
|
+
options: ['unset (undefined)', 'primary', 'secondary', 'tertiary', 'minimal'],
|
|
56
|
+
mapping: {
|
|
57
|
+
'unset (undefined)': undefined,
|
|
58
|
+
},
|
|
59
|
+
description: 'IconButton priority/style variant',
|
|
60
|
+
},
|
|
61
|
+
type: {
|
|
62
|
+
control: 'radio',
|
|
63
|
+
options: ['unset (undefined)', 'default', 'negative'],
|
|
64
|
+
mapping: {
|
|
65
|
+
'unset (undefined)': undefined,
|
|
66
|
+
},
|
|
67
|
+
description:
|
|
68
|
+
'Sets a visual hierarchy amongst the buttons displayed on the screen. <br /> **NB:** `negative` sentiment only affects the `primary` and `secondary` priorities.',
|
|
69
|
+
},
|
|
70
|
+
partiallyInteractive: {
|
|
71
|
+
control: 'boolean',
|
|
72
|
+
description: 'Whether the icon button allows partial interactivity in the ListItem',
|
|
73
|
+
},
|
|
74
|
+
href: {
|
|
75
|
+
control: 'text',
|
|
76
|
+
description: 'URL for icon button rendered as link',
|
|
77
|
+
},
|
|
78
|
+
target: {
|
|
79
|
+
control: 'select',
|
|
80
|
+
options: [undefined, '_blank', '_self', '_parent', '_top'],
|
|
81
|
+
description:
|
|
82
|
+
'The `target` attribute for HTML anchor. If set to `_blank`, `rel="noopener noreferrer"` is automatically added to the rendered node.',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
} satisfies Meta<ListItemIconButtonProps>;
|
|
86
|
+
|
|
87
|
+
export default meta;
|
|
88
|
+
|
|
89
|
+
type Story = StoryObj<ListItemIconButtonProps>;
|
|
90
|
+
|
|
91
|
+
export const Playground: Story = {
|
|
92
|
+
tags: ['!autodocs'],
|
|
93
|
+
render: (args: ListItemIconButtonProps) => {
|
|
94
|
+
return (
|
|
95
|
+
<List>
|
|
96
|
+
<ListItem
|
|
97
|
+
title="List item title"
|
|
98
|
+
subtitle="Subtitle goes here"
|
|
99
|
+
media={MEDIA.avatarSingle}
|
|
100
|
+
control={<ListItem.IconButton {...args} />}
|
|
101
|
+
additionalInfo={INFO.nonInteractive}
|
|
102
|
+
/>
|
|
103
|
+
</List>
|
|
104
|
+
);
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* It's strongly encouraged to provide `aria-label` prop for the `IconButton` control to ensure it
|
|
110
|
+
* follows accessibility best practices.
|
|
111
|
+
* If not set, the component will provide accessible name via the `title`, `subtitle`, `valueTitle`
|
|
112
|
+
* and `valueSubtitle` props and accessible description via `additionalInfo` and `prompt`, which
|
|
113
|
+
* may not be descriptive enough for the screen readers users.
|
|
114
|
+
*/
|
|
115
|
+
export const AccessibleName: Story = {
|
|
116
|
+
args: {
|
|
117
|
+
children: <Edit />,
|
|
118
|
+
href: 'https://wise.com',
|
|
119
|
+
target: '_blank',
|
|
120
|
+
onClick: undefined,
|
|
121
|
+
},
|
|
122
|
+
argTypes: hideControls(),
|
|
123
|
+
render: (args: ListItemIconButtonProps) => {
|
|
124
|
+
return (
|
|
125
|
+
<List>
|
|
126
|
+
<ListItem
|
|
127
|
+
control={<ListItem.IconButton {...args} />}
|
|
128
|
+
title="List item with icon button link"
|
|
129
|
+
subtitle="IconButton rendered as anchor element"
|
|
130
|
+
media={MEDIA.avatarSingle}
|
|
131
|
+
/>
|
|
132
|
+
</List>
|
|
133
|
+
);
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* By default, ListItem is fully interactive, which means its whole surface is clickable
|
|
139
|
+
* and triggers the control. To remain WCAG-compliant there can be no nested interactive
|
|
140
|
+
* elements inside the item.<br />
|
|
141
|
+
*
|
|
142
|
+
* To work around this limitation, this control also allows for a partially interactive mode,
|
|
143
|
+
* which can be enabled via `partiallyInteractive` prop. Once set, only the control will
|
|
144
|
+
* remain interactive, which allows for interactive element to be attached to the
|
|
145
|
+
* `ListItem.AdditionalInfo`. This allows for more complex layouts while still providing a
|
|
146
|
+
* clear, accessible action point for users. <br />
|
|
147
|
+
*
|
|
148
|
+
* If you still require a fully interactive ListItem, you can alternatively use `ListItem.Prompt`
|
|
149
|
+
* which allows for a single instance of a link or an inline button.
|
|
150
|
+
*
|
|
151
|
+
* Refer to the [design documentation](https://wise.design/components/list-item#interaction) for details.
|
|
152
|
+
*/
|
|
153
|
+
export const Interactivity: Story = {
|
|
154
|
+
args: {
|
|
155
|
+
children: <Edit />,
|
|
156
|
+
},
|
|
157
|
+
argTypes: hideControls(['partiallyInteractive']),
|
|
158
|
+
render: (args: ListItemIconButtonProps) => {
|
|
159
|
+
return (
|
|
160
|
+
<List>
|
|
161
|
+
<ListItem
|
|
162
|
+
control={<ListItem.IconButton {...args} />}
|
|
163
|
+
title="Fully interactive ListItem"
|
|
164
|
+
subtitle="IconButton in fully interactive context"
|
|
165
|
+
media={MEDIA.avatarSingle}
|
|
166
|
+
additionalInfo={INFO.nonInteractive}
|
|
167
|
+
prompt={PROMPTS.interactive}
|
|
168
|
+
/>
|
|
169
|
+
|
|
170
|
+
<ListItem
|
|
171
|
+
control={<ListItem.IconButton {...args} partiallyInteractive />}
|
|
172
|
+
title="Partially interactive ListItem"
|
|
173
|
+
subtitle="IconButton in partially interactive context"
|
|
174
|
+
media={MEDIA.avatarSingle}
|
|
175
|
+
additionalInfo={INFO.interactive}
|
|
176
|
+
prompt={PROMPTS.interactive}
|
|
177
|
+
/>
|
|
178
|
+
</List>
|
|
179
|
+
);
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* There are two different types of icon button – default and negative – designed to emphasise the nature of the action. <br />
|
|
185
|
+
* **NB:** Sentiment only applies to `primary` and `secondary` priorities. <br />
|
|
186
|
+
* [Design documentation](https://wise.design/components/icon-button#types)
|
|
187
|
+
*/
|
|
188
|
+
export const Sentiment: Story = {
|
|
189
|
+
argTypes: hideControls(['sentiment', 'priority']),
|
|
190
|
+
render: (args) => {
|
|
191
|
+
return (
|
|
192
|
+
<List>
|
|
193
|
+
<ListItem
|
|
194
|
+
control={<ListItem.IconButton {...args} priority="primary" type="default" />}
|
|
195
|
+
title="Default Sentiment - Primary"
|
|
196
|
+
subtitle="Default sentiment with primary priority"
|
|
197
|
+
media={MEDIA.avatarSingle}
|
|
198
|
+
/>
|
|
199
|
+
<ListItem
|
|
200
|
+
control={<ListItem.IconButton {...args} priority="secondary" type="default" />}
|
|
201
|
+
title="Default Sentiment - Secondary"
|
|
202
|
+
subtitle="Default sentiment with secondary priority"
|
|
203
|
+
media={MEDIA.avatarSingle}
|
|
204
|
+
/>
|
|
205
|
+
<ListItem
|
|
206
|
+
control={<ListItem.IconButton {...args} priority="primary" type="negative" />}
|
|
207
|
+
title="Negative Sentiment - Primary"
|
|
208
|
+
subtitle="Negative sentiment with primary priority"
|
|
209
|
+
media={MEDIA.avatarSingle}
|
|
210
|
+
/>
|
|
211
|
+
<ListItem
|
|
212
|
+
control={<ListItem.IconButton {...args} priority="secondary" type="negative" />}
|
|
213
|
+
title="Negative Sentiment - Secondary"
|
|
214
|
+
subtitle="Negative sentiment with secondary priority"
|
|
215
|
+
media={MEDIA.avatarSingle}
|
|
216
|
+
/>
|
|
217
|
+
</List>
|
|
218
|
+
);
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Priorities set a visual hierarchy amongst the buttons displayed on the
|
|
224
|
+
* screen to help more important buttons to take precedence over others. <br />
|
|
225
|
+
* [Design documentation](https://wise.design/components/icon-button#priorities)
|
|
226
|
+
*/
|
|
227
|
+
export const Priority: Story = {
|
|
228
|
+
argTypes: hideControls(['sentiment', 'priority']),
|
|
229
|
+
render: (args) => {
|
|
230
|
+
return (
|
|
231
|
+
<List>
|
|
232
|
+
<ListItem
|
|
233
|
+
title="Primary Priority"
|
|
234
|
+
subtitle="Primary priority (default)"
|
|
235
|
+
media={MEDIA.avatarSingle}
|
|
236
|
+
control={<ListItem.IconButton {...args} priority="primary" />}
|
|
237
|
+
/>
|
|
238
|
+
<ListItem
|
|
239
|
+
title="Secondary Priority"
|
|
240
|
+
subtitle="Secondary priority"
|
|
241
|
+
media={MEDIA.avatarSingle}
|
|
242
|
+
control={<ListItem.IconButton {...args} priority="secondary" />}
|
|
243
|
+
/>
|
|
244
|
+
<ListItem
|
|
245
|
+
title="Secondary Neutral Priority"
|
|
246
|
+
subtitle="Secondary neutral priority"
|
|
247
|
+
media={MEDIA.avatarSingle}
|
|
248
|
+
control={<ListItem.IconButton {...args} priority="tertiary" />}
|
|
249
|
+
/>
|
|
250
|
+
<ListItem
|
|
251
|
+
title="Tertiary Priority"
|
|
252
|
+
subtitle="Tertiary priority"
|
|
253
|
+
media={MEDIA.avatarSingle}
|
|
254
|
+
control={<ListItem.IconButton {...args} priority="minimal" />}
|
|
255
|
+
/>
|
|
256
|
+
</List>
|
|
257
|
+
);
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* If `href` prop is set, the component will be automatically rendered as an HTML anchor element.
|
|
263
|
+
*/
|
|
264
|
+
export const AsAnchor: Story = {
|
|
265
|
+
args: {
|
|
266
|
+
children: <Edit />,
|
|
267
|
+
href: 'https://wise.com',
|
|
268
|
+
target: '_blank',
|
|
269
|
+
onClick: undefined,
|
|
270
|
+
},
|
|
271
|
+
argTypes: hideControls(),
|
|
272
|
+
render: (args: ListItemIconButtonProps) => {
|
|
273
|
+
return (
|
|
274
|
+
<List>
|
|
275
|
+
<ListItem
|
|
276
|
+
control={<ListItem.IconButton {...args} />}
|
|
277
|
+
title="List item with icon button link"
|
|
278
|
+
subtitle="IconButton rendered as anchor element"
|
|
279
|
+
media={MEDIA.avatarSingle}
|
|
280
|
+
/>
|
|
281
|
+
</List>
|
|
282
|
+
);
|
|
283
|
+
},
|
|
284
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
import { useContext, type ReactNode } from 'react';
|
|
3
|
+
import IconButtonComp, { type IconButtonProps } from '../../iconButton';
|
|
4
|
+
import { useListItemControl } from '../useListItemControl';
|
|
5
|
+
import { ListItemContext } from '../ListItemContext';
|
|
6
|
+
|
|
7
|
+
export type ListItemIconButtonProps = Pick<
|
|
8
|
+
IconButtonProps,
|
|
9
|
+
'priority' | 'type' | 'onClick' | 'href' | 'target' | 'aria-label'
|
|
10
|
+
> & {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
partiallyInteractive?: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* This component allows for rendering a IconButton control. It's a thin wrapper around the
|
|
17
|
+
* [IconButton component](https://storybook.wise.design/?path=/docs/actions-iconbutton--docs), but offers only
|
|
18
|
+
* a subset of its features in line with the ListItem's constraints. <br />
|
|
19
|
+
* <br />
|
|
20
|
+
* Please refer to the [Design documentation](https://wise.design/components/list-item---icon-button) for details.
|
|
21
|
+
*/
|
|
22
|
+
export const IconButton = function ({
|
|
23
|
+
priority = 'minimal',
|
|
24
|
+
'aria-label': ariaLabel,
|
|
25
|
+
...props
|
|
26
|
+
}: ListItemIconButtonProps) {
|
|
27
|
+
const { partiallyInteractive, ...restProps } = props;
|
|
28
|
+
|
|
29
|
+
const { ids, props: itemProps } = useContext(ListItemContext);
|
|
30
|
+
const { baseItemProps } = useListItemControl('icon-button', {
|
|
31
|
+
partiallyInteractive,
|
|
32
|
+
...restProps,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const getAriaProps = () => {
|
|
36
|
+
const labelIds = [
|
|
37
|
+
itemProps.inverted ? ids.subtitle : ids.title,
|
|
38
|
+
itemProps.inverted ? ids.title : ids.subtitle,
|
|
39
|
+
itemProps.inverted ? ids.valueSubtitle : ids.valueTitle,
|
|
40
|
+
itemProps.inverted ? ids.valueTitle : ids.valueSubtitle,
|
|
41
|
+
].join(' ');
|
|
42
|
+
const descriptorIds = [ids.additionalInfo, ids.prompt].join(' ');
|
|
43
|
+
|
|
44
|
+
if (ariaLabel) {
|
|
45
|
+
return {
|
|
46
|
+
'aria-label': ariaLabel,
|
|
47
|
+
'aria-describedby': labelIds.concat(descriptorIds),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
'aria-labelledby': labelIds,
|
|
53
|
+
'aria-describedby': descriptorIds,
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<IconButtonComp
|
|
59
|
+
{...restProps}
|
|
60
|
+
{...getAriaProps()}
|
|
61
|
+
className={clsx(
|
|
62
|
+
'wds-list-item-control',
|
|
63
|
+
!partiallyInteractive && props.href && 'wds-list-item-control_pseudo-element',
|
|
64
|
+
)}
|
|
65
|
+
id={ids.control}
|
|
66
|
+
size={40}
|
|
67
|
+
priority={priority}
|
|
68
|
+
disabled={baseItemProps.disabled}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
IconButton.displayName = 'ListItem.IconButton';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { render, screen } from '../../test-utils';
|
|
2
|
+
import { ListItem, type ListItemProps } from '../ListItem';
|
|
3
|
+
|
|
4
|
+
const renderWithMedia = (media: ListItemProps['media']) =>
|
|
5
|
+
render(<ListItem title="Test Title" media={media} />);
|
|
6
|
+
|
|
7
|
+
describe('ListItem.Image', () => {
|
|
8
|
+
it('renders image with presentation role when no alt text', () => {
|
|
9
|
+
renderWithMedia(<ListItem.Image src="test-image.jpg" />);
|
|
10
|
+
expect(screen.getByRole('presentation')).toHaveAttribute('src', 'test-image.jpg');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('renders image with img role when alt text provided', () => {
|
|
14
|
+
renderWithMedia(<ListItem.Image src="test-image.jpg" alt="Test image description" />);
|
|
15
|
+
|
|
16
|
+
const image = screen.getByRole('img');
|
|
17
|
+
expect(image).toHaveAttribute('alt', 'Test image description');
|
|
18
|
+
expect(image).toHaveAttribute('src', 'test-image.jpg');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('renders image with loading prop', () => {
|
|
22
|
+
renderWithMedia(<ListItem.Image src="test-image.jpg" loading="lazy" />);
|
|
23
|
+
expect(screen.getByRole('presentation')).toHaveAttribute('loading', 'lazy');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('renders image with className prop', () => {
|
|
27
|
+
renderWithMedia(<ListItem.Image src="test-image.jpg" className="custom-image-class" />);
|
|
28
|
+
expect(screen.getByRole('presentation')).toHaveClass('custom-image-class');
|
|
29
|
+
});
|
|
30
|
+
});
|