@linagora/twake-mui 9.9.0 → 9.10.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/CHANGELOG.md +17 -0
- package/dist/components/ContactPopover/ContactPopover.stories.js +7 -11
- package/dist/components/ContactPopover/ContactPopoverCalendarAction.js +1 -1
- package/dist/components/ContactPopover/ContactPopoverChatAction.js +1 -1
- package/dist/components/ContactPopover/ContactPopoverEmailAction.js +1 -1
- package/dist/components/ContactPopover/ContactPopoverVideoAction.js +1 -1
- package/dist/components/ContactPopover/index.js +1 -1
- package/dist/components/Dialog/Dialog.stories.js +3 -1
- package/dist/components/ListItem/ListItem.stories.d.ts +9 -0
- package/dist/components/ListItem/ListItem.stories.js +135 -0
- package/dist/components/ListItem/helpers.d.ts +24 -0
- package/dist/components/ListItem/helpers.js +30 -0
- package/dist/components/ListItem/index.d.ts +7 -0
- package/dist/components/ListItem/index.js +19 -0
- package/dist/components/ListItemButton/index.d.ts +6 -0
- package/dist/components/ListItemButton/index.js +23 -0
- package/dist/components/ListItemText/ListItemText.stories.d.ts +7 -0
- package/dist/components/ListItemText/ListItemText.stories.js +58 -0
- package/dist/components/ListItemText/index.d.ts +7 -0
- package/dist/components/ListItemText/index.js +23 -0
- package/dist/components/ListSubheader/ListSubheader.stories.d.ts +7 -0
- package/dist/components/ListSubheader/ListSubheader.stories.js +59 -0
- package/dist/components/ListSubheader/index.d.ts +8 -0
- package/dist/components/ListSubheader/index.js +6 -0
- package/dist/components/Nav/index.d.ts +1 -1
- package/dist/components/NavDesktopDropdown/index.js +2 -7
- package/dist/components/NavIcon/index.js +2 -0
- package/dist/components/NavItem/index.d.ts +1 -1
- package/dist/components/NavItem/index.js +1 -0
- package/dist/components/NavLink/index.js +2 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +4 -0
- package/dist/lib/overrides.js +109 -0
- package/dist/stories/List.stories.d.ts +12 -0
- package/dist/stories/List.stories.js +141 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
# @linagora/twake-mui [9.10.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@9.9.1...@linagora/twake-mui@9.10.0) (2026-09-28)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **twake-mui:** Apply review on list components ([b5971e8](https://github.com/linagora/twake-ui/commit/b5971e8cdc66d12e24479cc1dee5d657c86495a7))
|
|
7
|
+
* **twake-mui:** Migrate ListItem from cozy-ui ([f3f4d0a](https://github.com/linagora/twake-ui/commit/f3f4d0ab092b8cd0c5344831193607da23d353a0))
|
|
8
|
+
* **twake-mui:** Migrate ListItemText from cozy-ui ([ee840a6](https://github.com/linagora/twake-ui/commit/ee840a671b7e0f823a28d1d7a33c325b0bb0f9c9))
|
|
9
|
+
* **twake-mui:** Migrate ListSubheader from cozy-ui ([af32dbd](https://github.com/linagora/twake-ui/commit/af32dbdde9365b86237ca7754a91c562e28913e0))
|
|
10
|
+
|
|
11
|
+
## @linagora/twake-mui [9.9.1](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@9.9.0...@linagora/twake-mui@9.9.1) (2026-09-24)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* **twake-mui:** Fixed contactPopover click propagation & opening link in new pages ([b11270b](https://github.com/linagora/twake-ui/commit/b11270b060bcc749805f59059214c8a011b7b20d))
|
|
17
|
+
|
|
1
18
|
# @linagora/twake-mui [9.9.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@9.8.0...@linagora/twake-mui@9.9.0) (2026-09-24)
|
|
2
19
|
|
|
3
20
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Chip, Box, Typography } from '@mui/material';
|
|
2
|
-
import React
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { screen } from 'storybook/test';
|
|
3
4
|
import { ContactPopover } from './index';
|
|
4
5
|
const meta = {
|
|
5
6
|
title: 'Components/ContactPopover',
|
|
@@ -32,16 +33,7 @@ const meta = {
|
|
|
32
33
|
export default meta;
|
|
33
34
|
export const Default = {
|
|
34
35
|
render: () => {
|
|
35
|
-
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
const timer = setTimeout(() => {
|
|
38
|
-
containerRef.current
|
|
39
|
-
?.querySelectorAll('[role="button"]')[0]
|
|
40
|
-
.click();
|
|
41
|
-
}, 100);
|
|
42
|
-
return () => clearTimeout(timer);
|
|
43
|
-
}, []);
|
|
44
|
-
return (React.createElement(Box, { ref: containerRef, sx: { display: 'flex', flexDirection: 'column', gap: 3 } },
|
|
36
|
+
return (React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 3 } },
|
|
45
37
|
React.createElement(Box, null,
|
|
46
38
|
React.createElement(Typography, { variant: "subtitle2", gutterBottom: true }, "Default \u2014 all actions"),
|
|
47
39
|
React.createElement(ContactPopover, { name: "John Doe", email: "john.doe@example.com" },
|
|
@@ -69,5 +61,9 @@ export const Default = {
|
|
|
69
61
|
React.createElement(ContactPopover.ChatAction, { url: "https://chat.example.com/christopher" }),
|
|
70
62
|
React.createElement(ContactPopover.VideoAction, { url: "https://meet.example.com/christopher" }),
|
|
71
63
|
React.createElement(ContactPopover.CalendarAction, { url: "https://calendar.example.com/christopher" }))))));
|
|
64
|
+
},
|
|
65
|
+
play: async ({ canvas, userEvent }) => {
|
|
66
|
+
await userEvent.click(canvas.getByText('John Doe'));
|
|
67
|
+
await screen.findByText('john.doe@example.com');
|
|
72
68
|
}
|
|
73
69
|
};
|
|
@@ -4,6 +4,6 @@ import { IconButton, useTheme } from '@mui/material';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
export const ContactPopoverCalendarAction = ({ url }) => {
|
|
6
6
|
const theme = useTheme();
|
|
7
|
-
return (React.createElement(IconButton, { href: url, size: "small", sx: { border: `1px solid ${theme.palette.divider}` } },
|
|
7
|
+
return (React.createElement(IconButton, { href: url, target: "_blank", rel: "noopener noreferrer", size: "small", sx: { border: `1px solid ${theme.palette.divider}` } },
|
|
8
8
|
React.createElement(Icon, { icon: CalendarToday, size: 20, color: theme.palette.text.icon })));
|
|
9
9
|
};
|
|
@@ -4,6 +4,6 @@ import { IconButton, useTheme } from '@mui/material';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
export const ContactPopoverChatAction = ({ url }) => {
|
|
6
6
|
const theme = useTheme();
|
|
7
|
-
return (React.createElement(IconButton, { href: url, size: "small", sx: { border: `1px solid ${theme.palette.divider}` } },
|
|
7
|
+
return (React.createElement(IconButton, { href: url, target: "_blank", rel: "noopener noreferrer", size: "small", sx: { border: `1px solid ${theme.palette.divider}` } },
|
|
8
8
|
React.createElement(Icon, { icon: Discuss, size: 20, color: theme.palette.text.icon })));
|
|
9
9
|
};
|
|
@@ -4,7 +4,7 @@ import { useI18n } from 'twake-i18n';
|
|
|
4
4
|
export const ContactPopoverEmailAction = ({ url }) => {
|
|
5
5
|
const theme = useTheme();
|
|
6
6
|
const { t } = useI18n();
|
|
7
|
-
return (React.createElement(Button, { href: url, variant: "outlined", size: "small", fullWidth: true, sx: {
|
|
7
|
+
return (React.createElement(Button, { href: url, target: "_blank", rel: "noopener noreferrer", variant: "outlined", size: "small", fullWidth: true, sx: {
|
|
8
8
|
justifyContent: 'center',
|
|
9
9
|
color: theme.palette.text.primary,
|
|
10
10
|
borderColor: theme.palette.divider
|
|
@@ -4,6 +4,6 @@ import { IconButton, useTheme } from '@mui/material';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
export const ContactPopoverVideoAction = ({ url }) => {
|
|
6
6
|
const theme = useTheme();
|
|
7
|
-
return (React.createElement(IconButton, { href: url, size: "small", sx: { border: `1px solid ${theme.palette.divider}` } },
|
|
7
|
+
return (React.createElement(IconButton, { href: url, target: "_blank", rel: "noopener noreferrer", size: "small", sx: { border: `1px solid ${theme.palette.divider}` } },
|
|
8
8
|
React.createElement(Icon, { icon: Camera, size: 20, color: theme.palette.text.icon })));
|
|
9
9
|
};
|
|
@@ -119,7 +119,7 @@ export const ContactPopover = ({ name, email, children, onEmailCopy, onClose, di
|
|
|
119
119
|
onMouseLeave: handleMouseLeave
|
|
120
120
|
}
|
|
121
121
|
} },
|
|
122
|
-
React.createElement(StyledPopoverContent,
|
|
122
|
+
React.createElement(StyledPopoverContent, { onClick: event => event.stopPropagation() },
|
|
123
123
|
React.createElement(CloseButton, { size: "small", onClick: handleClose },
|
|
124
124
|
React.createElement(Icon, { icon: Cross, size: 16 })),
|
|
125
125
|
React.createElement(AttendeeInfo, { name: name, email: email, onEmailCopy: onEmailCopy }),
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { CrossMedium, FileTypeFolder, FileTypeText, Icon } from '@linagora/twake-icons';
|
|
2
|
-
import { Button, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, IconButton, List,
|
|
2
|
+
import { Button, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, IconButton, List, ListItemIcon, Stack } from '@mui/material';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { Dialog } from './index';
|
|
5
|
+
import ListItemButton from '../ListItemButton';
|
|
6
|
+
import ListItemText from '../ListItemText';
|
|
5
7
|
const sizes = ['small', 'medium', 'large', 'full'];
|
|
6
8
|
const meta = {
|
|
7
9
|
title: 'Dialog',
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import ListItem from './index';
|
|
3
|
+
import ListItemButton from '../ListItemButton';
|
|
4
|
+
declare const meta: Meta<typeof ListItem>;
|
|
5
|
+
export default meta;
|
|
6
|
+
type Story = StoryObj<typeof meta>;
|
|
7
|
+
export declare const Default: Story;
|
|
8
|
+
export declare const Button: StoryObj<typeof ListItemButton>;
|
|
9
|
+
export declare const Screenshot: Story;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Comment, Dots, Icon, Pen, People, Right, Trash } from '@linagora/twake-icons';
|
|
2
|
+
import { Box, Checkbox, IconButton, List, ListItemAvatar, ListItemIcon, ListItemSecondaryAction, Radio, Stack } from '@mui/material';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import ListItem from './index';
|
|
5
|
+
import { Avatar } from '../Avatar';
|
|
6
|
+
import ListItemButton from '../ListItemButton';
|
|
7
|
+
import ListItemText from '../ListItemText';
|
|
8
|
+
import ListSubheader from '../ListSubheader';
|
|
9
|
+
const longText = 'Ada Lovelace wrote the first algorithm meant to be carried out by a machine, long before any computer existed to run it.';
|
|
10
|
+
const layoutArgTypes = {
|
|
11
|
+
gutters: {
|
|
12
|
+
control: 'select',
|
|
13
|
+
options: ['default', 'double', 'disabled']
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
control: 'select',
|
|
17
|
+
options: ['small', 'medium', 'large']
|
|
18
|
+
},
|
|
19
|
+
ellipsis: { control: 'boolean' },
|
|
20
|
+
dense: { control: 'boolean' },
|
|
21
|
+
divider: { control: 'boolean' },
|
|
22
|
+
alignItems: {
|
|
23
|
+
control: 'select',
|
|
24
|
+
options: ['center', 'flex-start']
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const meta = {
|
|
28
|
+
title: 'ListItem',
|
|
29
|
+
component: ListItem,
|
|
30
|
+
subcomponents: { ListItemButton },
|
|
31
|
+
tags: ['autodocs'],
|
|
32
|
+
argTypes: layoutArgTypes,
|
|
33
|
+
decorators: [
|
|
34
|
+
(Story) => (React.createElement(Box, { sx: { maxWidth: 480 } },
|
|
35
|
+
React.createElement(Story, null)))
|
|
36
|
+
]
|
|
37
|
+
};
|
|
38
|
+
export default meta;
|
|
39
|
+
const defaultContent = [
|
|
40
|
+
React.createElement(ListItemIcon, { key: "icon" },
|
|
41
|
+
React.createElement(Icon, { icon: Comment })),
|
|
42
|
+
React.createElement(ListItemText, { key: "text", primary: "I'm a primary text" })
|
|
43
|
+
];
|
|
44
|
+
export const Default = {
|
|
45
|
+
args: {
|
|
46
|
+
gutters: 'default',
|
|
47
|
+
size: 'medium',
|
|
48
|
+
ellipsis: true
|
|
49
|
+
},
|
|
50
|
+
render: args => (React.createElement(List, null,
|
|
51
|
+
React.createElement(ListItem, { ...args }, defaultContent)))
|
|
52
|
+
};
|
|
53
|
+
export const Button = {
|
|
54
|
+
argTypes: {
|
|
55
|
+
...layoutArgTypes,
|
|
56
|
+
selected: { control: 'boolean' },
|
|
57
|
+
disabled: { control: 'boolean' }
|
|
58
|
+
},
|
|
59
|
+
args: {
|
|
60
|
+
gutters: 'default',
|
|
61
|
+
size: 'medium',
|
|
62
|
+
ellipsis: true
|
|
63
|
+
},
|
|
64
|
+
render: args => (React.createElement(List, null,
|
|
65
|
+
React.createElement(ListItemButton, { ...args }, defaultContent)))
|
|
66
|
+
};
|
|
67
|
+
const Example = ({ title, button = true, dense, selected, disabled, longText: isLongText, iconSize = 16, multipleLeftIcons, multipleRightIcons, withActions, ...props }) => {
|
|
68
|
+
const content = [
|
|
69
|
+
React.createElement(ListItemIcon, { key: "comment" },
|
|
70
|
+
React.createElement(Icon, { icon: Comment, size: iconSize })),
|
|
71
|
+
multipleLeftIcons && (React.createElement(React.Fragment, { key: "left" },
|
|
72
|
+
React.createElement(ListItemIcon, null,
|
|
73
|
+
React.createElement(Icon, { icon: People, size: iconSize })),
|
|
74
|
+
React.createElement(ListItemIcon, null,
|
|
75
|
+
React.createElement(Radio, null)),
|
|
76
|
+
React.createElement(ListItemIcon, null,
|
|
77
|
+
React.createElement(Checkbox, null)))),
|
|
78
|
+
React.createElement(ListItemText, { key: "text", primary: isLongText ? longText : "I'm a primary text" }),
|
|
79
|
+
multipleRightIcons && (React.createElement(React.Fragment, { key: "right" },
|
|
80
|
+
React.createElement(ListItemIcon, null,
|
|
81
|
+
React.createElement(Icon, { icon: Right, size: iconSize })),
|
|
82
|
+
React.createElement(ListItemIcon, null,
|
|
83
|
+
React.createElement(Radio, null)),
|
|
84
|
+
React.createElement(ListItemIcon, null,
|
|
85
|
+
React.createElement(Checkbox, null)))),
|
|
86
|
+
withActions && (React.createElement(ListItemSecondaryAction, { key: "actions" },
|
|
87
|
+
React.createElement(IconButton, null,
|
|
88
|
+
React.createElement(Icon, { icon: Pen })),
|
|
89
|
+
React.createElement(IconButton, null,
|
|
90
|
+
React.createElement(Icon, { icon: Trash })),
|
|
91
|
+
React.createElement(IconButton, null,
|
|
92
|
+
React.createElement(Icon, { icon: Dots }))))
|
|
93
|
+
];
|
|
94
|
+
return (React.createElement("section", null,
|
|
95
|
+
React.createElement("h3", null, title),
|
|
96
|
+
React.createElement(List, { dense: dense, subheader: React.createElement(ListSubheader, null, "Section 1") }, button ? (React.createElement(ListItemButton, { selected: selected, disabled: disabled, ...props }, content)) : (React.createElement(ListItem, { ...props }, content)))));
|
|
97
|
+
};
|
|
98
|
+
// Visual Regression - Combined view for Argos testing
|
|
99
|
+
export const Screenshot = {
|
|
100
|
+
tags: ['argos'],
|
|
101
|
+
render: () => (React.createElement(Stack, { spacing: 4 },
|
|
102
|
+
React.createElement(Example, { title: "Default" }),
|
|
103
|
+
React.createElement(Example, { title: "Dense", dense: true }),
|
|
104
|
+
React.createElement(Example, { title: "Long text", longText: true }),
|
|
105
|
+
React.createElement(Example, { title: "Long text without ellipsis", longText: true, ellipsis: false }),
|
|
106
|
+
React.createElement(Example, { title: "Disabled gutters", gutters: "disabled" }),
|
|
107
|
+
React.createElement(Example, { title: "Double gutters", gutters: "double" }),
|
|
108
|
+
React.createElement(Example, { title: "Small", size: "small" }),
|
|
109
|
+
React.createElement(Example, { title: "Large", size: "large" }),
|
|
110
|
+
React.createElement(Example, { title: "Dense small", dense: true, size: "small" }),
|
|
111
|
+
React.createElement(Example, { title: "Dense large", dense: true, size: "large" }),
|
|
112
|
+
React.createElement(Example, { title: "Big icons", iconSize: 32 }),
|
|
113
|
+
React.createElement(Example, { title: "Small icons", iconSize: 8 }),
|
|
114
|
+
React.createElement(Example, { title: "Multiple left icons", multipleLeftIcons: true }),
|
|
115
|
+
React.createElement(Example, { title: "Multiple right icons", multipleRightIcons: true }),
|
|
116
|
+
React.createElement(Example, { title: "Multiple right icons, double gutters", multipleRightIcons: true, gutters: "double" }),
|
|
117
|
+
React.createElement(Example, { title: "With actions", withActions: true, longText: true }),
|
|
118
|
+
React.createElement(Example, { title: "With actions, double gutters", withActions: true, longText: true, gutters: "double" }),
|
|
119
|
+
React.createElement("section", null,
|
|
120
|
+
React.createElement("h3", null, "With avatar"),
|
|
121
|
+
React.createElement(List, null,
|
|
122
|
+
React.createElement(ListItemButton, null,
|
|
123
|
+
React.createElement(ListItemAvatar, null,
|
|
124
|
+
React.createElement(Avatar, null, "Ada Lovelace")),
|
|
125
|
+
React.createElement(ListItemText, { primary: "Ada Lovelace", secondary: longText })),
|
|
126
|
+
React.createElement(ListItemButton, { alignItems: "flex-start", ellipsis: false },
|
|
127
|
+
React.createElement(ListItemAvatar, null,
|
|
128
|
+
React.createElement(Avatar, null, "Ada Lovelace")),
|
|
129
|
+
React.createElement(ListItemText, { primary: "Ada Lovelace", secondary: longText })))),
|
|
130
|
+
React.createElement(Example, { title: "Selected", selected: true }),
|
|
131
|
+
React.createElement(Example, { title: "Disabled", disabled: true }),
|
|
132
|
+
React.createElement(Example, { title: "Not a button", button: false }),
|
|
133
|
+
React.createElement(Example, { title: "Not a button, dense small", button: false, dense: true, size: "small" }),
|
|
134
|
+
React.createElement(Example, { title: "Not a button, with actions", button: false, withActions: true })))
|
|
135
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SxProps, Theme } from '@mui/material/styles';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export type ListItemGutters = 'default' | 'double' | 'disabled';
|
|
4
|
+
export type ListItemSize = 'small' | 'medium' | 'large';
|
|
5
|
+
export interface ListItemLayoutProps {
|
|
6
|
+
gutters?: ListItemGutters;
|
|
7
|
+
size?: ListItemSize;
|
|
8
|
+
ellipsis?: boolean;
|
|
9
|
+
}
|
|
10
|
+
interface ListItemRowOptions extends ListItemLayoutProps {
|
|
11
|
+
className?: string;
|
|
12
|
+
disableGutters?: boolean;
|
|
13
|
+
sx?: SxProps<Theme>;
|
|
14
|
+
children?: React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
export interface ListItemRow {
|
|
17
|
+
className: string;
|
|
18
|
+
disableGutters: boolean;
|
|
19
|
+
sx: SxProps<Theme>;
|
|
20
|
+
action: React.ReactElement | null;
|
|
21
|
+
content: React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
export declare function computeListItemRow({ className, gutters, size, ellipsis, disableGutters, sx, children }: ListItemRowOptions): ListItemRow;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { isMuiElement } from '@mui/material/utils';
|
|
2
|
+
import cx from 'classnames';
|
|
3
|
+
import { Children, cloneElement, isValidElement } from 'react';
|
|
4
|
+
import ListItemText from '../ListItemText';
|
|
5
|
+
const ACTION_BUTTON_SIZE = 48;
|
|
6
|
+
export function computeListItemRow({ className, gutters = 'default', size = 'medium', ellipsis = true, disableGutters = false, sx, children }) {
|
|
7
|
+
const items = Children.toArray(children);
|
|
8
|
+
const lastItem = items.at(-1);
|
|
9
|
+
const action = isValidElement(lastItem) &&
|
|
10
|
+
isMuiElement(lastItem, ['ListItemSecondaryAction'])
|
|
11
|
+
? lastItem
|
|
12
|
+
: null;
|
|
13
|
+
const actionPadding = action
|
|
14
|
+
? Children.count(action.props.children) * ACTION_BUTTON_SIZE +
|
|
15
|
+
(gutters === 'double' ? 24 : 8)
|
|
16
|
+
: 0;
|
|
17
|
+
return {
|
|
18
|
+
className: cx(className, size, { doubleGutters: gutters === 'double' }),
|
|
19
|
+
disableGutters: disableGutters || gutters === 'disabled',
|
|
20
|
+
sx: [
|
|
21
|
+
// && outranks the theme's .doubleGutters padding
|
|
22
|
+
actionPadding > 0 && { '&&': { paddingRight: `${actionPadding}px` } },
|
|
23
|
+
sx ?? false
|
|
24
|
+
].flat(),
|
|
25
|
+
action,
|
|
26
|
+
content: (action ? items.slice(0, -1) : items).map(item => isValidElement(item) && item.type === ListItemText
|
|
27
|
+
? cloneElement(item, { ellipsis })
|
|
28
|
+
: item)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ListItemProps as MuiListItemProps } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { ListItemLayoutProps } from './helpers';
|
|
4
|
+
export type { ListItemGutters, ListItemSize } from './helpers';
|
|
5
|
+
export type ListItemProps = Omit<MuiListItemProps, 'ref'> & ListItemLayoutProps;
|
|
6
|
+
declare const ListItem: React.ForwardRefExoticComponent<Omit<MuiListItemProps, "ref"> & ListItemLayoutProps & React.RefAttributes<HTMLLIElement>>;
|
|
7
|
+
export default ListItem;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ListItem as MuiListItem } from '@mui/material';
|
|
2
|
+
import React, { forwardRef } from 'react';
|
|
3
|
+
import { computeListItemRow } from './helpers';
|
|
4
|
+
const ListItem = forwardRef(({ className, gutters, size, ellipsis, disableGutters, sx, children, ...props }, ref) => {
|
|
5
|
+
const row = computeListItemRow({
|
|
6
|
+
className,
|
|
7
|
+
gutters,
|
|
8
|
+
size,
|
|
9
|
+
ellipsis,
|
|
10
|
+
disableGutters,
|
|
11
|
+
sx,
|
|
12
|
+
children
|
|
13
|
+
});
|
|
14
|
+
return (React.createElement(MuiListItem, { ref: ref, ...props, className: row.className, disableGutters: row.disableGutters, sx: row.sx },
|
|
15
|
+
row.content,
|
|
16
|
+
row.action));
|
|
17
|
+
});
|
|
18
|
+
ListItem.displayName = 'ListItem';
|
|
19
|
+
export default ListItem;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ListItemButtonProps as MuiListItemButtonProps } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { ListItemLayoutProps } from '../ListItem/helpers';
|
|
4
|
+
export type ListItemButtonProps = Omit<MuiListItemButtonProps, 'ref'> & ListItemLayoutProps;
|
|
5
|
+
declare const ListItemButton: React.ForwardRefExoticComponent<Omit<MuiListItemButtonProps, "ref"> & ListItemLayoutProps & React.RefAttributes<HTMLDivElement>>;
|
|
6
|
+
export default ListItemButton;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ListItem, ListItemButton as MuiListItemButton } from '@mui/material';
|
|
2
|
+
import React, { forwardRef } from 'react';
|
|
3
|
+
import { computeListItemRow } from '../ListItem/helpers';
|
|
4
|
+
const ListItemButton = forwardRef(({ className, gutters, size, ellipsis, disableGutters, sx, children, ...props }, ref) => {
|
|
5
|
+
const row = computeListItemRow({
|
|
6
|
+
className,
|
|
7
|
+
gutters,
|
|
8
|
+
size,
|
|
9
|
+
ellipsis,
|
|
10
|
+
disableGutters,
|
|
11
|
+
sx,
|
|
12
|
+
children
|
|
13
|
+
});
|
|
14
|
+
const button = (React.createElement(MuiListItemButton, { ref: ref, ...props, className: row.className, disableGutters: row.disableGutters, sx: row.sx }, row.content));
|
|
15
|
+
if (row.action === null)
|
|
16
|
+
return button;
|
|
17
|
+
// Keeps the action out of the button, so clicking it does not click the row
|
|
18
|
+
return (React.createElement(ListItem, { disablePadding: true },
|
|
19
|
+
button,
|
|
20
|
+
row.action));
|
|
21
|
+
});
|
|
22
|
+
ListItemButton.displayName = 'ListItemButton';
|
|
23
|
+
export default ListItemButton;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import ListItemText from './index';
|
|
3
|
+
declare const meta: Meta<typeof ListItemText>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof meta>;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const Screenshot: Story;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Box, List, Stack, Typography } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import ListItemText from './index';
|
|
4
|
+
const longText = 'Ada Lovelace wrote the first algorithm meant to be carried out by a machine, long before any computer existed to run it.';
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'ListItemText',
|
|
7
|
+
component: ListItemText,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
argTypes: {
|
|
10
|
+
primary: { control: 'text' },
|
|
11
|
+
secondary: { control: 'text' },
|
|
12
|
+
ellipsis: { control: 'boolean' },
|
|
13
|
+
inset: { control: 'boolean' },
|
|
14
|
+
disableTypography: { control: 'boolean' }
|
|
15
|
+
},
|
|
16
|
+
decorators: [
|
|
17
|
+
(Story) => (React.createElement(Box, { sx: { maxWidth: 480 } },
|
|
18
|
+
React.createElement(Story, null)))
|
|
19
|
+
]
|
|
20
|
+
};
|
|
21
|
+
export default meta;
|
|
22
|
+
export const Default = {
|
|
23
|
+
args: {
|
|
24
|
+
primary: "I'm a primary text",
|
|
25
|
+
secondary: "I'm a secondary text",
|
|
26
|
+
ellipsis: true
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const Row = ({ title, children }) => (React.createElement("section", null,
|
|
30
|
+
React.createElement("h3", null, title),
|
|
31
|
+
children));
|
|
32
|
+
// Visual Regression - Combined view for Argos testing
|
|
33
|
+
export const Screenshot = {
|
|
34
|
+
tags: ['argos'],
|
|
35
|
+
render: () => (React.createElement(Stack, { spacing: 4 },
|
|
36
|
+
React.createElement(Row, { title: "Primary text" },
|
|
37
|
+
React.createElement(ListItemText, { primary: "I'm a list item text" })),
|
|
38
|
+
React.createElement(Row, { title: "Secondary text" },
|
|
39
|
+
React.createElement(ListItemText, { primary: "I'm a primary text", secondary: "I'm a secondary text" })),
|
|
40
|
+
React.createElement(Row, { title: "Custom" },
|
|
41
|
+
React.createElement(ListItemText, null,
|
|
42
|
+
React.createElement(Typography, { variant: "body1" }, "I'm a primary text"),
|
|
43
|
+
React.createElement(Typography, { component: "a", href: "http://cozy.io", variant: "caption", color: "textSecondary" }, longText))),
|
|
44
|
+
React.createElement(Row, { title: "Ellipsis" },
|
|
45
|
+
React.createElement(ListItemText, { primary: longText, secondary: longText })),
|
|
46
|
+
React.createElement(Row, { title: "Without ellipsis" },
|
|
47
|
+
React.createElement(ListItemText, { primary: longText, secondary: longText, ellipsis: false })),
|
|
48
|
+
React.createElement(Row, { title: "Dense" },
|
|
49
|
+
React.createElement(List, { dense: true, disablePadding: true },
|
|
50
|
+
React.createElement(ListItemText, { primary: "I'm a primary text", secondary: "I'm a secondary text" }))),
|
|
51
|
+
React.createElement(Row, { title: "Inset" },
|
|
52
|
+
React.createElement(ListItemText, { inset: true, primary: "I'm a primary text" })),
|
|
53
|
+
React.createElement(Row, { title: "Slot props" },
|
|
54
|
+
React.createElement(ListItemText, { primary: "I'm a primary text", secondary: "I'm a secondary text", slotProps: {
|
|
55
|
+
primary: { variant: 'caption' },
|
|
56
|
+
secondary: { color: 'inherit', variant: 'body1' }
|
|
57
|
+
} }))))
|
|
58
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ListItemTextProps as MuiListItemTextProps } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export interface ListItemTextProps extends Omit<MuiListItemTextProps, 'ref'> {
|
|
4
|
+
ellipsis?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare const ListItemText: React.ForwardRefExoticComponent<ListItemTextProps & React.RefAttributes<HTMLDivElement>>;
|
|
7
|
+
export default ListItemText;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ListItemText as MuiListItemText } from '@mui/material';
|
|
2
|
+
import React, { forwardRef } from 'react';
|
|
3
|
+
const ListItemText = forwardRef(({ ellipsis = true, slotProps, ...props }, ref) => {
|
|
4
|
+
const primarySlot = slotProps?.primary;
|
|
5
|
+
return (React.createElement(MuiListItemText, { ref: ref, ...props,
|
|
6
|
+
// A primary or secondary slot replaces these defaults, ellipsis included
|
|
7
|
+
slotProps: {
|
|
8
|
+
...slotProps,
|
|
9
|
+
primary: typeof primarySlot === 'function'
|
|
10
|
+
? primarySlot
|
|
11
|
+
: {
|
|
12
|
+
// Keeps a caption primary out of the theme's grey caption rule
|
|
13
|
+
color: 'inherit',
|
|
14
|
+
...(primarySlot ?? { noWrap: ellipsis })
|
|
15
|
+
},
|
|
16
|
+
secondary: slotProps?.secondary ?? {
|
|
17
|
+
variant: 'caption',
|
|
18
|
+
noWrap: ellipsis
|
|
19
|
+
}
|
|
20
|
+
} }));
|
|
21
|
+
});
|
|
22
|
+
ListItemText.displayName = 'ListItemText';
|
|
23
|
+
export default ListItemText;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import ListSubheader from './index';
|
|
3
|
+
declare const meta: Meta<typeof ListSubheader>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof meta>;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const Screenshot: Story;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { FileTypeFolder, Icon } from '@linagora/twake-icons';
|
|
2
|
+
import { Box, List, ListItemIcon, Stack } from '@mui/material';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import ListSubheader from './index';
|
|
5
|
+
import ListItemButton from '../ListItemButton';
|
|
6
|
+
import ListItemText from '../ListItemText';
|
|
7
|
+
const meta = {
|
|
8
|
+
title: 'ListSubheader',
|
|
9
|
+
component: ListSubheader,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
argTypes: {
|
|
12
|
+
gutters: {
|
|
13
|
+
control: 'select',
|
|
14
|
+
options: ['default', 'double', 'disabled']
|
|
15
|
+
},
|
|
16
|
+
color: {
|
|
17
|
+
control: 'select',
|
|
18
|
+
options: ['default', 'primary', 'inherit']
|
|
19
|
+
},
|
|
20
|
+
inset: { control: 'boolean' },
|
|
21
|
+
disableSticky: { control: 'boolean' }
|
|
22
|
+
},
|
|
23
|
+
decorators: [
|
|
24
|
+
(Story) => (React.createElement(Box, { sx: { maxWidth: 480 } },
|
|
25
|
+
React.createElement(Story, null)))
|
|
26
|
+
]
|
|
27
|
+
};
|
|
28
|
+
export default meta;
|
|
29
|
+
const Example = props => (React.createElement(List, { subheader: React.createElement(ListSubheader, { ...props }, "This is the subheader") },
|
|
30
|
+
React.createElement(ListItemButton, null,
|
|
31
|
+
React.createElement(ListItemIcon, null,
|
|
32
|
+
React.createElement(Icon, { icon: FileTypeFolder, size: 32 })),
|
|
33
|
+
React.createElement(ListItemText, { primary: "I'm a primary text" }))));
|
|
34
|
+
export const Default = {
|
|
35
|
+
args: {
|
|
36
|
+
gutters: 'default'
|
|
37
|
+
},
|
|
38
|
+
render: args => React.createElement(Example, { ...args })
|
|
39
|
+
};
|
|
40
|
+
const Row = ({ title, children }) => (React.createElement("section", null,
|
|
41
|
+
React.createElement("h3", null, title),
|
|
42
|
+
children));
|
|
43
|
+
// Visual Regression - Combined view for Argos testing
|
|
44
|
+
export const Screenshot = {
|
|
45
|
+
tags: ['argos'],
|
|
46
|
+
render: () => (React.createElement(Stack, { spacing: 4 },
|
|
47
|
+
React.createElement(Row, { title: "Default gutters" },
|
|
48
|
+
React.createElement(Example, null)),
|
|
49
|
+
React.createElement(Row, { title: "Disabled gutters" },
|
|
50
|
+
React.createElement(Example, { gutters: "disabled" })),
|
|
51
|
+
React.createElement(Row, { title: "Double gutters" },
|
|
52
|
+
React.createElement(Example, { gutters: "double" })),
|
|
53
|
+
React.createElement(Row, { title: "Inset" },
|
|
54
|
+
React.createElement(Example, { inset: true })),
|
|
55
|
+
React.createElement(Row, { title: "Not sticky" },
|
|
56
|
+
React.createElement(Example, { disableSticky: true })),
|
|
57
|
+
React.createElement(Row, { title: "Primary color" },
|
|
58
|
+
React.createElement(Example, { color: "primary" }))))
|
|
59
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ListSubheaderProps as MuiListSubheaderProps } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { ListItemGutters } from '../ListItem/helpers';
|
|
4
|
+
export interface ListSubheaderProps extends Omit<MuiListSubheaderProps, 'ref'> {
|
|
5
|
+
gutters?: ListItemGutters;
|
|
6
|
+
}
|
|
7
|
+
declare const ListSubheader: React.ForwardRefExoticComponent<ListSubheaderProps & React.RefAttributes<HTMLLIElement>>;
|
|
8
|
+
export default ListSubheader;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ListSubheader as MuiListSubheader } from '@mui/material';
|
|
2
|
+
import cx from 'classnames';
|
|
3
|
+
import React, { forwardRef } from 'react';
|
|
4
|
+
const ListSubheader = forwardRef(({ className, gutters = 'default', disableGutters, ...props }, ref) => (React.createElement(MuiListSubheader, { ref: ref, ...props, className: cx(className, { doubleGutters: gutters === 'double' }), disableGutters: disableGutters || gutters === 'disabled' })));
|
|
5
|
+
ListSubheader.displayName = 'ListSubheader';
|
|
6
|
+
export default ListSubheader;
|
|
@@ -2,7 +2,7 @@ import { Theme } from '@mui/material/styles';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
declare const NavRoot: import("@emotion/styled").StyledComponent<import("@mui/material").ListOwnProps & Omit<import("@mui/material").ListOwnProps, keyof import("@mui/material").ListOwnProps> & import("@mui/material/OverridableComponent").CommonProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>, "ref"> & {
|
|
4
4
|
ref?: ((instance: HTMLUListElement | null) => void) | React.RefObject<HTMLUListElement> | null | undefined;
|
|
5
|
-
}, "style" | "className" | "children" | "
|
|
5
|
+
}, "style" | "className" | "children" | "dense" | "disablePadding" | "classes" | "sx" | "subheader"> & import("@mui/system").MUIStyledCommonProps<Theme>, {}, {}>;
|
|
6
6
|
export type NavProps = React.ComponentProps<typeof NavRoot>;
|
|
7
7
|
export declare const Nav: (props: NavProps) => React.ReactElement;
|
|
8
8
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ListItem } from '@mui/material';
|
|
2
1
|
import React, { Children, isValidElement, useState } from 'react';
|
|
3
2
|
import { useBreakpoints } from '../../hooks/useBreakpoints';
|
|
4
3
|
import DropdownText from '../DropdownText';
|
|
4
|
+
import ListItem from '../ListItem';
|
|
5
5
|
export const NavDesktopDropdown = ({ label, children, defaultOpen = true, limit = 5 }) => {
|
|
6
6
|
const { isDesktop } = useBreakpoints();
|
|
7
7
|
const [open, setOpen] = useState(defaultOpen);
|
|
@@ -10,12 +10,7 @@ export const NavDesktopDropdown = ({ label, children, defaultOpen = true, limit
|
|
|
10
10
|
if (!isDesktop)
|
|
11
11
|
return null;
|
|
12
12
|
return (React.createElement(React.Fragment, null,
|
|
13
|
-
React.createElement(ListItem, { sx: {
|
|
14
|
-
minHeight: 48,
|
|
15
|
-
py: 1,
|
|
16
|
-
px: 2,
|
|
17
|
-
cursor: isActivated ? 'pointer' : undefined
|
|
18
|
-
} },
|
|
13
|
+
React.createElement(ListItem, { size: "small", sx: { cursor: isActivated ? 'pointer' : undefined } },
|
|
19
14
|
React.createElement(DropdownText, { variant: "caption", color: "textSecondary", spaceBetween: true, onClick: isActivated ? onToggle : undefined, innerIconProps: {
|
|
20
15
|
rotate: open ? 0 : -90,
|
|
21
16
|
display: isActivated ? undefined : 'none'
|
|
@@ -4,6 +4,8 @@ import { styled } from '@mui/material/styles';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
const NavIconRoot = styled(ListItemIcon)(({ theme }) => ({
|
|
6
6
|
minWidth: 0,
|
|
7
|
+
width: 'auto',
|
|
8
|
+
height: 'auto',
|
|
7
9
|
marginRight: 12,
|
|
8
10
|
color: theme.vars.palette.text.primary,
|
|
9
11
|
[theme.breakpoints.down('lg')]: {
|
|
@@ -2,7 +2,7 @@ import { Theme } from '@mui/material/styles';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
export declare const NavItem: import("@emotion/styled").StyledComponent<import("@mui/material").ListItemOwnProps & import("@mui/material/OverridableComponent").CommonProps & Omit<Omit<React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>, "ref"> & {
|
|
4
4
|
ref?: ((instance: HTMLLIElement | null) => void) | React.RefObject<HTMLLIElement> | null | undefined;
|
|
5
|
-
}, "style" | "className" | "children" | "
|
|
5
|
+
}, "style" | "className" | "children" | "alignItems" | "dense" | "disablePadding" | "disableGutters" | "classes" | "sx" | "divider" | "slotProps" | "slots" | "secondaryAction"> & import("@mui/system").MUIStyledCommonProps<Theme> & {
|
|
6
6
|
secondary?: boolean;
|
|
7
7
|
}, {}, {}>;
|
|
8
8
|
export type NavItemProps = React.ComponentProps<typeof NavItem>;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,10 @@ export { Dialog, default as DialogDefault } from './components/Dialog';
|
|
|
13
13
|
export { ContactPopover, default as ContactPopoverDefault } from './components/ContactPopover';
|
|
14
14
|
export { default as DropdownButton } from './components/DropdownButton';
|
|
15
15
|
export { default as DropdownText } from './components/DropdownText';
|
|
16
|
+
export { default as ListItem } from './components/ListItem';
|
|
17
|
+
export { default as ListItemButton } from './components/ListItemButton';
|
|
18
|
+
export { default as ListItemText } from './components/ListItemText';
|
|
19
|
+
export { default as ListSubheader } from './components/ListSubheader';
|
|
16
20
|
export { Tabs, default as TabsDefault } from './components/Tabs';
|
|
17
21
|
export { Switch, default as SwitchDefault } from './components/Switch';
|
|
18
22
|
export { SearchBar, default as SearchBarDefault } from './components/SearchBar';
|
|
@@ -45,6 +49,10 @@ export type { ChipProps } from './components/Chip';
|
|
|
45
49
|
export type { DialogProps, DialogSize } from './components/Dialog';
|
|
46
50
|
export type { DropdownButtonProps } from './components/DropdownButton';
|
|
47
51
|
export type { DropdownTextProps, DropdownTextVariant } from './components/DropdownText';
|
|
52
|
+
export type { ListItemProps, ListItemGutters, ListItemSize } from './components/ListItem';
|
|
53
|
+
export type { ListItemButtonProps } from './components/ListItemButton';
|
|
54
|
+
export type { ListItemTextProps } from './components/ListItemText';
|
|
55
|
+
export type { ListSubheaderProps } from './components/ListSubheader';
|
|
48
56
|
export type { TabsProps } from './components/Tabs';
|
|
49
57
|
export type { SwitchProps } from './components/Switch';
|
|
50
58
|
export type { SearchBarProps } from './components/SearchBar/types';
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,10 @@ export { Dialog, default as DialogDefault } from './components/Dialog';
|
|
|
16
16
|
export { ContactPopover, default as ContactPopoverDefault } from './components/ContactPopover';
|
|
17
17
|
export { default as DropdownButton } from './components/DropdownButton';
|
|
18
18
|
export { default as DropdownText } from './components/DropdownText';
|
|
19
|
+
export { default as ListItem } from './components/ListItem';
|
|
20
|
+
export { default as ListItemButton } from './components/ListItemButton';
|
|
21
|
+
export { default as ListItemText } from './components/ListItemText';
|
|
22
|
+
export { default as ListSubheader } from './components/ListSubheader';
|
|
19
23
|
export { Tabs, default as TabsDefault } from './components/Tabs';
|
|
20
24
|
export { Switch, default as SwitchDefault } from './components/Switch';
|
|
21
25
|
export { SearchBar, default as SearchBarDefault } from './components/SearchBar';
|
package/dist/lib/overrides.js
CHANGED
|
@@ -5,6 +5,7 @@ import { checkboxClasses } from '@mui/material/Checkbox';
|
|
|
5
5
|
import { dialogTitleClasses } from '@mui/material/DialogTitle';
|
|
6
6
|
import { formHelperTextClasses } from '@mui/material/FormHelperText';
|
|
7
7
|
import { formLabelClasses } from '@mui/material/FormLabel';
|
|
8
|
+
import { listItemButtonClasses } from '@mui/material/ListItemButton';
|
|
8
9
|
import { listItemIconClasses } from '@mui/material/ListItemIcon';
|
|
9
10
|
import { listItemTextClasses } from '@mui/material/ListItemText';
|
|
10
11
|
import { outlinedInputClasses } from '@mui/material/OutlinedInput';
|
|
@@ -97,6 +98,38 @@ const radioColors = [
|
|
|
97
98
|
'warning',
|
|
98
99
|
'info'
|
|
99
100
|
];
|
|
101
|
+
const listItemRoot = {
|
|
102
|
+
gap: 16,
|
|
103
|
+
variants: [
|
|
104
|
+
{
|
|
105
|
+
props: ({ dense, disablePadding }) => !dense && !disablePadding,
|
|
106
|
+
style: {
|
|
107
|
+
paddingTop: 12,
|
|
108
|
+
paddingBottom: 12,
|
|
109
|
+
minHeight: 56,
|
|
110
|
+
'&.small': { paddingTop: 8, paddingBottom: 8, minHeight: 48 },
|
|
111
|
+
'&.large': { paddingTop: 16, paddingBottom: 16, minHeight: 64 }
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
props: ({ dense, disablePadding }) => dense && !disablePadding,
|
|
116
|
+
style: {
|
|
117
|
+
paddingTop: 8,
|
|
118
|
+
paddingBottom: 8,
|
|
119
|
+
minHeight: 48,
|
|
120
|
+
'&.small': { paddingTop: 4, paddingBottom: 4, minHeight: 40 },
|
|
121
|
+
'&.large': { paddingTop: 12, paddingBottom: 12, minHeight: 56 }
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
props: { disableGutters: false },
|
|
126
|
+
style: {
|
|
127
|
+
'&.doubleGutters': { paddingLeft: 32, paddingRight: 32 },
|
|
128
|
+
[`& > .${listItemIconClasses.root}:last-child`]: { marginRight: -8 }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
};
|
|
100
133
|
export const overrides = {
|
|
101
134
|
MuiButton: {
|
|
102
135
|
defaultProps: {
|
|
@@ -1015,6 +1048,80 @@ export const overrides = {
|
|
|
1015
1048
|
})
|
|
1016
1049
|
}
|
|
1017
1050
|
},
|
|
1051
|
+
MuiListItem: {
|
|
1052
|
+
styleOverrides: {
|
|
1053
|
+
root: listItemRoot
|
|
1054
|
+
}
|
|
1055
|
+
},
|
|
1056
|
+
MuiListItemButton: {
|
|
1057
|
+
styleOverrides: {
|
|
1058
|
+
root: ({ theme }) => ({
|
|
1059
|
+
...listItemRoot,
|
|
1060
|
+
[`&.${listItemButtonClasses.selected}, &.${listItemButtonClasses.selected}:hover, &.${listItemButtonClasses.focusVisible}, &.${listItemButtonClasses.selected}.${listItemButtonClasses.focusVisible}`]: {
|
|
1061
|
+
backgroundColor: theme.vars.palette.action.selected
|
|
1062
|
+
},
|
|
1063
|
+
[`&.${listItemButtonClasses.disabled}`]: {
|
|
1064
|
+
opacity: 0.5
|
|
1065
|
+
}
|
|
1066
|
+
})
|
|
1067
|
+
}
|
|
1068
|
+
},
|
|
1069
|
+
MuiListItemIcon: {
|
|
1070
|
+
styleOverrides: {
|
|
1071
|
+
root: ({ theme }) => ({
|
|
1072
|
+
minWidth: 'auto',
|
|
1073
|
+
width: 32,
|
|
1074
|
+
height: 32,
|
|
1075
|
+
alignItems: 'center',
|
|
1076
|
+
justifyContent: 'center',
|
|
1077
|
+
color: theme.vars.palette.text.icon
|
|
1078
|
+
})
|
|
1079
|
+
}
|
|
1080
|
+
},
|
|
1081
|
+
MuiListSubheader: {
|
|
1082
|
+
styleOverrides: {
|
|
1083
|
+
root: ({ theme }) => ({
|
|
1084
|
+
...theme.typography.subtitle2,
|
|
1085
|
+
paddingTop: 8,
|
|
1086
|
+
paddingBottom: 8,
|
|
1087
|
+
marginBottom: '0.5rem',
|
|
1088
|
+
backgroundColor: theme.vars.palette.background.contrast,
|
|
1089
|
+
variants: [
|
|
1090
|
+
{
|
|
1091
|
+
props: { disableSticky: false },
|
|
1092
|
+
style: { backgroundColor: theme.vars.palette.background.default }
|
|
1093
|
+
},
|
|
1094
|
+
{
|
|
1095
|
+
props: { disableGutters: false },
|
|
1096
|
+
style: {
|
|
1097
|
+
'&.doubleGutters': { paddingLeft: 32, paddingRight: 32 }
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
]
|
|
1101
|
+
})
|
|
1102
|
+
}
|
|
1103
|
+
},
|
|
1104
|
+
MuiListItemText: {
|
|
1105
|
+
styleOverrides: {
|
|
1106
|
+
root: {
|
|
1107
|
+
marginTop: 1,
|
|
1108
|
+
marginBottom: 1
|
|
1109
|
+
},
|
|
1110
|
+
secondary: {
|
|
1111
|
+
marginTop: 1
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
},
|
|
1115
|
+
MuiListItemSecondaryAction: {
|
|
1116
|
+
styleOverrides: {
|
|
1117
|
+
root: {
|
|
1118
|
+
right: 0,
|
|
1119
|
+
'.doubleGutters > &, .doubleGutters ~ &': {
|
|
1120
|
+
marginRight: 16
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
},
|
|
1018
1125
|
MuiMenu: {
|
|
1019
1126
|
styleOverrides: {
|
|
1020
1127
|
paper: { maxWidth: 320 }
|
|
@@ -1030,6 +1137,8 @@ export const overrides = {
|
|
|
1030
1137
|
wordBreak: 'break-word',
|
|
1031
1138
|
[`& .${listItemIconClasses.root}`]: {
|
|
1032
1139
|
minWidth: 'auto',
|
|
1140
|
+
width: 'auto',
|
|
1141
|
+
height: 'auto',
|
|
1033
1142
|
color: theme.vars.palette.text.icon
|
|
1034
1143
|
},
|
|
1035
1144
|
[`& .${listItemTextClasses.root}`]: {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { ListItemLayoutProps } from '../components/ListItem/helpers';
|
|
4
|
+
interface DemoProps extends ListItemLayoutProps {
|
|
5
|
+
dense?: boolean;
|
|
6
|
+
}
|
|
7
|
+
declare const Demo: React.FC<DemoProps>;
|
|
8
|
+
declare const meta: Meta<typeof Demo>;
|
|
9
|
+
export default meta;
|
|
10
|
+
type Story = StoryObj<typeof meta>;
|
|
11
|
+
export declare const Default: Story;
|
|
12
|
+
export declare const Screenshot: Story;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Comment, Dots, FileTypeFolder, FileTypeText, Help, Icon, LinkOut, MagicTrick, People, PieChart, Right } from '@linagora/twake-icons';
|
|
2
|
+
import { Box, Divider, IconButton, List, ListItemIcon, ListItemSecondaryAction, Radio, Stack, Typography } from '@mui/material';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import ListItemButton from '../components/ListItemButton';
|
|
5
|
+
import ListItemText from '../components/ListItemText';
|
|
6
|
+
import ListSubheader from '../components/ListSubheader';
|
|
7
|
+
const longText = 'Ada Lovelace wrote the first algorithm meant to be carried out by a machine, long before any computer existed to run it.';
|
|
8
|
+
const Demo = ({ dense, ...itemProps }) => (React.createElement(React.Fragment, null,
|
|
9
|
+
React.createElement(List, { dense: dense, subheader: React.createElement(ListSubheader, null, "Section 1") },
|
|
10
|
+
React.createElement(ListItemButton, { ...itemProps },
|
|
11
|
+
React.createElement(ListItemIcon, null,
|
|
12
|
+
React.createElement(Icon, { icon: FileTypeFolder, size: 32 })),
|
|
13
|
+
React.createElement(ListItemText, { primary: "I'm a primary text" }),
|
|
14
|
+
React.createElement(ListItemText, { secondary: "Metadata" }),
|
|
15
|
+
React.createElement(ListItemText, { secondary: "Metadata" })),
|
|
16
|
+
React.createElement(Divider, { component: "li", variant: "inset" }),
|
|
17
|
+
React.createElement(ListItemButton, { ...itemProps },
|
|
18
|
+
React.createElement(ListItemIcon, null,
|
|
19
|
+
React.createElement(Icon, { icon: FileTypeText, size: 32 })),
|
|
20
|
+
React.createElement(ListItemText, { primary: "I'm a primary text", secondary: "I'm a secondary text" })),
|
|
21
|
+
React.createElement(Divider, { component: "li", variant: "inset" }),
|
|
22
|
+
React.createElement(ListItemButton, { ...itemProps },
|
|
23
|
+
React.createElement(ListItemIcon, null,
|
|
24
|
+
React.createElement(Icon, { icon: FileTypeText, size: 32 })),
|
|
25
|
+
React.createElement(ListItemText, { primary: "I'm a primary text", secondary: "I'm a secondary text", slotProps: {
|
|
26
|
+
primary: { variant: 'caption' },
|
|
27
|
+
secondary: { color: 'inherit', variant: dense ? 'body2' : 'body1' }
|
|
28
|
+
} }))),
|
|
29
|
+
React.createElement(List, { dense: dense, subheader: React.createElement(ListSubheader, null, "Section 2") },
|
|
30
|
+
React.createElement(ListItemButton, { ...itemProps },
|
|
31
|
+
React.createElement(ListItemIcon, null,
|
|
32
|
+
React.createElement(Icon, { icon: Comment })),
|
|
33
|
+
React.createElement(ListItemText, { primary: "Support" }),
|
|
34
|
+
React.createElement(ListItemSecondaryAction, null,
|
|
35
|
+
React.createElement(IconButton, null,
|
|
36
|
+
React.createElement(Icon, { icon: Dots })))),
|
|
37
|
+
React.createElement(Divider, { component: "li", variant: "inset" }),
|
|
38
|
+
React.createElement(ListItemButton, { ...itemProps },
|
|
39
|
+
React.createElement(ListItemIcon, null,
|
|
40
|
+
React.createElement(Icon, { icon: MagicTrick })),
|
|
41
|
+
React.createElement(ListItemText, { primary: "Double actions" }),
|
|
42
|
+
React.createElement(ListItemSecondaryAction, null,
|
|
43
|
+
React.createElement(IconButton, null,
|
|
44
|
+
React.createElement(Icon, { icon: Right })),
|
|
45
|
+
React.createElement(IconButton, null,
|
|
46
|
+
React.createElement(Icon, { icon: Dots }))))),
|
|
47
|
+
React.createElement(Divider, null),
|
|
48
|
+
React.createElement(List, { dense: dense },
|
|
49
|
+
React.createElement(ListItemButton, { ...itemProps },
|
|
50
|
+
React.createElement(ListItemIcon, null,
|
|
51
|
+
React.createElement(Icon, { icon: People })),
|
|
52
|
+
React.createElement(ListItemText, { primary: "Profil" }),
|
|
53
|
+
React.createElement(ListItemIcon, null,
|
|
54
|
+
React.createElement(Icon, { icon: Right }))),
|
|
55
|
+
React.createElement(Divider, { component: "li", variant: "inset" }),
|
|
56
|
+
React.createElement(ListItemButton, { ...itemProps },
|
|
57
|
+
React.createElement(ListItemIcon, null,
|
|
58
|
+
React.createElement(Icon, { icon: People })),
|
|
59
|
+
React.createElement(ListItemText, { primary: longText, secondary: longText }),
|
|
60
|
+
React.createElement(ListItemIcon, null,
|
|
61
|
+
React.createElement(Radio, null))),
|
|
62
|
+
React.createElement(Divider, { component: "li", variant: "inset" }),
|
|
63
|
+
React.createElement(ListItemButton, { ...itemProps },
|
|
64
|
+
React.createElement(ListItemIcon, null,
|
|
65
|
+
React.createElement(Icon, { icon: Help })),
|
|
66
|
+
React.createElement(ListItemText, { primary: "Help" }),
|
|
67
|
+
React.createElement(ListItemIcon, null,
|
|
68
|
+
React.createElement(Icon, { icon: LinkOut }))),
|
|
69
|
+
React.createElement(Divider, { component: "li", variant: "inset" }),
|
|
70
|
+
React.createElement(ListItemButton, { ...itemProps },
|
|
71
|
+
React.createElement(ListItemIcon, null,
|
|
72
|
+
React.createElement(Icon, { icon: PieChart })),
|
|
73
|
+
React.createElement(ListItemText, { primary: "Storage" }),
|
|
74
|
+
React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "82% used"),
|
|
75
|
+
React.createElement(ListItemIcon, null,
|
|
76
|
+
React.createElement(Icon, { icon: Right }))))));
|
|
77
|
+
const HighlightedItem = () => (React.createElement(List, null,
|
|
78
|
+
React.createElement(ListItemButton, null,
|
|
79
|
+
React.createElement(ListItemIcon, null,
|
|
80
|
+
React.createElement(Icon, { icon: FileTypeFolder, size: 32 })),
|
|
81
|
+
React.createElement(ListItemText, { primary: "I'm a primary text" })),
|
|
82
|
+
React.createElement(Divider, { variant: "inset" }),
|
|
83
|
+
React.createElement(ListItemButton, { selected: true },
|
|
84
|
+
React.createElement(ListItemIcon, null,
|
|
85
|
+
React.createElement(Icon, { icon: FileTypeText, size: 32 })),
|
|
86
|
+
React.createElement(ListItemText, { primary: "I'm a primary text", secondary: "I'm a secondary text" })),
|
|
87
|
+
React.createElement(Divider, { variant: "inset" }),
|
|
88
|
+
React.createElement(ListItemButton, null,
|
|
89
|
+
React.createElement(ListItemIcon, null,
|
|
90
|
+
React.createElement(Icon, { icon: FileTypeText, size: 32 })),
|
|
91
|
+
React.createElement(ListItemText, { primary: "I'm a primary text" }))));
|
|
92
|
+
const meta = {
|
|
93
|
+
title: 'List',
|
|
94
|
+
component: Demo,
|
|
95
|
+
tags: ['autodocs'],
|
|
96
|
+
argTypes: {
|
|
97
|
+
dense: { control: 'boolean' },
|
|
98
|
+
gutters: {
|
|
99
|
+
control: 'select',
|
|
100
|
+
options: ['default', 'double', 'disabled']
|
|
101
|
+
},
|
|
102
|
+
size: {
|
|
103
|
+
control: 'select',
|
|
104
|
+
options: ['small', 'medium', 'large']
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
decorators: [
|
|
108
|
+
(Story) => (React.createElement(Box, { sx: { maxWidth: 480 } },
|
|
109
|
+
React.createElement(Story, null)))
|
|
110
|
+
]
|
|
111
|
+
};
|
|
112
|
+
export default meta;
|
|
113
|
+
export const Default = {
|
|
114
|
+
args: {
|
|
115
|
+
dense: false,
|
|
116
|
+
gutters: 'default',
|
|
117
|
+
size: 'medium'
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const Row = ({ title, children }) => (React.createElement("section", null,
|
|
121
|
+
React.createElement("h3", null, title),
|
|
122
|
+
children));
|
|
123
|
+
// Visual Regression - Combined view for Argos testing
|
|
124
|
+
export const Screenshot = {
|
|
125
|
+
tags: ['argos'],
|
|
126
|
+
render: () => (React.createElement(Stack, { spacing: 4 },
|
|
127
|
+
React.createElement(Row, { title: "Default" },
|
|
128
|
+
React.createElement(Demo, null)),
|
|
129
|
+
React.createElement(Row, { title: "Dense" },
|
|
130
|
+
React.createElement(Demo, { dense: true })),
|
|
131
|
+
React.createElement(Row, { title: "Disabled gutters" },
|
|
132
|
+
React.createElement(Demo, { gutters: "disabled" })),
|
|
133
|
+
React.createElement(Row, { title: "Double gutters" },
|
|
134
|
+
React.createElement(Demo, { gutters: "double" })),
|
|
135
|
+
React.createElement(Row, { title: "Small" },
|
|
136
|
+
React.createElement(Demo, { size: "small" })),
|
|
137
|
+
React.createElement(Row, { title: "Large" },
|
|
138
|
+
React.createElement(Demo, { size: "large" })),
|
|
139
|
+
React.createElement(Row, { title: "Highlighted item" },
|
|
140
|
+
React.createElement(HighlightedItem, null))))
|
|
141
|
+
};
|