@linagora/twake-mui 18.4.1 → 18.5.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 +14 -0
- package/dist/components/NavText/NavPrimaryText.js +5 -2
- package/dist/lib/makeTypography.js +12 -5
- package/dist/lib/overrides.js +22 -4
- package/dist/lib/types.d.ts +15 -0
- package/dist/stories/Button.stories.js +1 -1
- package/dist/stories/DatePicker.stories.d.ts +7 -0
- package/dist/stories/DatePicker.stories.js +98 -0
- package/dist/stories/FileInput.stories.d.ts +11 -0
- package/dist/stories/FileInput.stories.js +135 -0
- package/dist/stories/Typography.stories.js +1 -0
- package/package.json +19 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# @linagora/twake-mui [18.5.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@18.4.1...@linagora/twake-mui@18.5.0) (2026-10-27)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **twake-mui:** Take the line heights and tracking of the small styles from the design ([bab4fa1](https://github.com/linagora/twake-ui/commit/bab4fa147fd55735c8e4396c364e19bc696099db))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **twake-mui:** Add a body3 typography variant ([b1110b3](https://github.com/linagora/twake-ui/commit/b1110b328f29ad49714b138cceaf887236dad667))
|
|
12
|
+
* **twake-mui:** Add an xsmall Button size ([6ca4db8](https://github.com/linagora/twake-ui/commit/6ca4db8105c37d87bec419b8a4bfe563e4d40098))
|
|
13
|
+
* **twake-mui:** Set the button text to 14px at every size ([6587b37](https://github.com/linagora/twake-ui/commit/6587b377eea92cece727309b3ff76cec8e8dfd3e))
|
|
14
|
+
|
|
1
15
|
## @linagora/twake-mui [18.4.1](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@18.4.0...@linagora/twake-mui@18.4.1) (2026-10-23)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -4,13 +4,16 @@ export const NavPrimaryText = styled('span', {
|
|
|
4
4
|
})(({ theme, disableMobileView }) => ({
|
|
5
5
|
fontSize: theme.typography.pxToRem(14),
|
|
6
6
|
fontWeight: 500,
|
|
7
|
-
|
|
7
|
+
lineHeight: 1.313,
|
|
8
|
+
letterSpacing: '.25px',
|
|
8
9
|
...(!disableMobileView && {
|
|
9
10
|
[theme.breakpoints.down('lg')]: {
|
|
10
11
|
display: 'block',
|
|
11
12
|
textAlign: 'center',
|
|
12
13
|
whiteSpace: 'nowrap',
|
|
13
|
-
fontSize: theme.typography.pxToRem(12)
|
|
14
|
+
fontSize: theme.typography.pxToRem(12),
|
|
15
|
+
// The design line height is for the sidebar, the bottom bar keeps its rows
|
|
16
|
+
lineHeight: 'inherit'
|
|
14
17
|
}
|
|
15
18
|
})
|
|
16
19
|
}));
|
|
@@ -54,7 +54,7 @@ export const makeTypography = () => {
|
|
|
54
54
|
subtitle2: {
|
|
55
55
|
fontSize: 14,
|
|
56
56
|
fontWeight: 600,
|
|
57
|
-
lineHeight: 1.
|
|
57
|
+
lineHeight: 1.313,
|
|
58
58
|
letterSpacing: '0.25px'
|
|
59
59
|
},
|
|
60
60
|
body1: {
|
|
@@ -69,23 +69,30 @@ export const makeTypography = () => {
|
|
|
69
69
|
lineHeight: 1.313,
|
|
70
70
|
letterSpacing: '0.25px'
|
|
71
71
|
},
|
|
72
|
+
body3: {
|
|
73
|
+
fontSize: 14,
|
|
74
|
+
fontWeight: 400,
|
|
75
|
+
lineHeight: 1.313,
|
|
76
|
+
letterSpacing: '0.25px'
|
|
77
|
+
},
|
|
72
78
|
button: {
|
|
73
|
-
fontSize:
|
|
79
|
+
fontSize: 14,
|
|
74
80
|
fontWeight: 500,
|
|
75
81
|
lineHeight: 1.313,
|
|
82
|
+
letterSpacing: '0.1px',
|
|
76
83
|
textTransform: 'inherit'
|
|
77
84
|
},
|
|
78
85
|
caption: {
|
|
79
86
|
fontSize: 12,
|
|
80
87
|
fontWeight: 500,
|
|
81
|
-
lineHeight: 1.
|
|
88
|
+
lineHeight: 1.317,
|
|
82
89
|
letterSpacing: '0.5px'
|
|
83
90
|
},
|
|
84
91
|
overline: {
|
|
85
92
|
fontSize: 11,
|
|
86
93
|
fontWeight: 500,
|
|
87
|
-
lineHeight: '
|
|
88
|
-
letterSpacing: '0.
|
|
94
|
+
lineHeight: '13px',
|
|
95
|
+
letterSpacing: '0.4px',
|
|
89
96
|
textTransform: 'inherit'
|
|
90
97
|
}
|
|
91
98
|
};
|
package/dist/lib/overrides.js
CHANGED
|
@@ -212,14 +212,28 @@ export const overrides = {
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
})),
|
|
215
|
+
{
|
|
216
|
+
props: { size: 'xsmall' },
|
|
217
|
+
style: {
|
|
218
|
+
minWidth: 'auto',
|
|
219
|
+
minHeight: 24,
|
|
220
|
+
padding: '4px 8px',
|
|
221
|
+
fontSize: theme.typography.pxToRem(11),
|
|
222
|
+
lineHeight: '16px',
|
|
223
|
+
letterSpacing: '0.5px'
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
props: { size: 'xsmall', variant: 'outlined' },
|
|
228
|
+
style: { padding: '3px 7px' }
|
|
229
|
+
},
|
|
215
230
|
// Vertical paddings sit 1px under cozy-ui's so the outlined border
|
|
216
231
|
// still fits the fixed heights cozy-ui forces (36, 40, 48).
|
|
217
232
|
{
|
|
218
233
|
props: { size: 'small' },
|
|
219
234
|
style: {
|
|
220
235
|
minHeight: 36,
|
|
221
|
-
padding: '9px 16px'
|
|
222
|
-
fontSize: theme.typography.pxToRem(13)
|
|
236
|
+
padding: '9px 16px'
|
|
223
237
|
}
|
|
224
238
|
},
|
|
225
239
|
{
|
|
@@ -230,8 +244,7 @@ export const overrides = {
|
|
|
230
244
|
props: { size: 'large' },
|
|
231
245
|
style: {
|
|
232
246
|
minHeight: 48,
|
|
233
|
-
padding: '13px 32px'
|
|
234
|
-
fontSize: theme.typography.pxToRem(15)
|
|
247
|
+
padding: '13px 32px'
|
|
235
248
|
}
|
|
236
249
|
},
|
|
237
250
|
{
|
|
@@ -257,6 +270,10 @@ export const overrides = {
|
|
|
257
270
|
props: { loadingPosition: 'end', variant: 'text' },
|
|
258
271
|
style: { right: 8 }
|
|
259
272
|
},
|
|
273
|
+
{
|
|
274
|
+
props: { loadingPosition: 'end', size: 'xsmall' },
|
|
275
|
+
style: { right: 8 }
|
|
276
|
+
},
|
|
260
277
|
{
|
|
261
278
|
props: { loadingPosition: 'end', size: 'small' },
|
|
262
279
|
style: { right: 16 }
|
|
@@ -983,6 +1000,7 @@ export const overrides = {
|
|
|
983
1000
|
}
|
|
984
1001
|
},
|
|
985
1002
|
MuiTypography: {
|
|
1003
|
+
defaultProps: { variantMapping: { body3: 'p' } },
|
|
986
1004
|
styleOverrides: {
|
|
987
1005
|
root: ({ theme }) => ({
|
|
988
1006
|
variants: [
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import paletteJson from '@linagora/twake-css/palette.json';
|
|
2
2
|
import { Shadows, TypographyVariantsOptions } from '@mui/material/styles';
|
|
3
|
+
import type { CSSProperties } from 'react';
|
|
3
4
|
export type PaletteJson = typeof paletteJson;
|
|
4
5
|
export type PaletteColor = PaletteJson['Primary'];
|
|
5
6
|
export type GreyPalette = PaletteJson['Grey'];
|
|
@@ -69,6 +70,12 @@ declare module '@mui/material/styles' {
|
|
|
69
70
|
interface CssThemeVariables {
|
|
70
71
|
enabled: true;
|
|
71
72
|
}
|
|
73
|
+
interface TypographyVariants {
|
|
74
|
+
body3: CSSProperties;
|
|
75
|
+
}
|
|
76
|
+
interface TypographyVariantsOptions {
|
|
77
|
+
body3?: CSSProperties;
|
|
78
|
+
}
|
|
72
79
|
interface TypeBackground {
|
|
73
80
|
contrast: string;
|
|
74
81
|
}
|
|
@@ -82,6 +89,11 @@ declare module '@mui/material/styles' {
|
|
|
82
89
|
border?: Partial<BorderPalette>;
|
|
83
90
|
}
|
|
84
91
|
}
|
|
92
|
+
declare module '@mui/material/Typography' {
|
|
93
|
+
interface TypographyPropsVariantOverrides {
|
|
94
|
+
body3: true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
85
97
|
declare module '@mui/material/IconButton' {
|
|
86
98
|
interface IconButtonPropsSizeOverrides {
|
|
87
99
|
xsmall: true;
|
|
@@ -97,4 +109,7 @@ declare module '@mui/material/Button' {
|
|
|
97
109
|
interface ButtonPropsVariantOverrides {
|
|
98
110
|
ghost: true;
|
|
99
111
|
}
|
|
112
|
+
interface ButtonPropsSizeOverrides {
|
|
113
|
+
xsmall: true;
|
|
114
|
+
}
|
|
100
115
|
}
|
|
@@ -3,7 +3,7 @@ import { Box, Stack } from '@mui/material';
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import Button from '../components/Button';
|
|
5
5
|
const variants = ['contained', 'outlined', 'ghost', 'text'];
|
|
6
|
-
const sizes = ['small', 'medium', 'large'];
|
|
6
|
+
const sizes = ['xsmall', 'small', 'medium', 'large'];
|
|
7
7
|
const colors = [
|
|
8
8
|
'inherit',
|
|
9
9
|
'primary',
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
declare const meta: Meta;
|
|
3
|
+
export default meta;
|
|
4
|
+
type Story = StoryObj<typeof meta>;
|
|
5
|
+
export declare const Default: Story;
|
|
6
|
+
export declare const Modes: Story;
|
|
7
|
+
export declare const MonthPicker: Story;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Paper, Stack } from '@mui/material';
|
|
2
|
+
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV2';
|
|
3
|
+
import { DateCalendar } from '@mui/x-date-pickers/DateCalendar';
|
|
4
|
+
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
|
|
5
|
+
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
|
|
6
|
+
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
|
7
|
+
import { TimePicker } from '@mui/x-date-pickers/TimePicker';
|
|
8
|
+
import { enUS } from 'date-fns/locale';
|
|
9
|
+
import React, { useState } from 'react';
|
|
10
|
+
const description = `
|
|
11
|
+
To pick a date, a time or a month, use [MUI X Date Pickers](https://mui.com/x/react-date-pickers/), installed in the app with \`date-fns\`. Their popups are built on MUI components that twake-mui themes. Their field is a \`PickersTextField\`, which twake-mui does not theme yet: its radius, border colors and gutters still differ from a \`TextField\`.
|
|
12
|
+
|
|
13
|
+
Wrap the app once in a \`LocalizationProvider\`, with the date-fns locale and the picker texts of the user's language. Twake apps use date-fns 2.30.0, so import the adapter from \`@mui/x-date-pickers/AdapterDateFnsV2\`:
|
|
14
|
+
|
|
15
|
+
\`\`\`jsx
|
|
16
|
+
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV2'
|
|
17
|
+
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'
|
|
18
|
+
import { frFR } from '@mui/x-date-pickers/locales'
|
|
19
|
+
import { fr } from 'date-fns/locale'
|
|
20
|
+
|
|
21
|
+
<LocalizationProvider
|
|
22
|
+
dateAdapter={AdapterDateFns}
|
|
23
|
+
adapterLocale={fr}
|
|
24
|
+
localeText={frFR.components.MuiLocalizationProvider.defaultProps.localeText}
|
|
25
|
+
>
|
|
26
|
+
<App />
|
|
27
|
+
</LocalizationProvider>
|
|
28
|
+
\`\`\`
|
|
29
|
+
`;
|
|
30
|
+
const MIN_DATE = new Date(2025, 0, 10);
|
|
31
|
+
const getErrorMessage = (error) => {
|
|
32
|
+
if (error === 'minDate')
|
|
33
|
+
return 'The date must be on or after 01/10/2025';
|
|
34
|
+
if (error === 'invalidDate')
|
|
35
|
+
return 'Invalid date';
|
|
36
|
+
return null;
|
|
37
|
+
};
|
|
38
|
+
const ExampleDatePicker = () => {
|
|
39
|
+
const [value, setValue] = useState(new Date(2025, 0, 8));
|
|
40
|
+
const [error, setError] = useState(null);
|
|
41
|
+
return (React.createElement(DatePicker, { label: "Expiration date", value: value, minDate: MIN_DATE, onChange: setValue, onError: setError, slotProps: {
|
|
42
|
+
textField: { helperText: getErrorMessage(error) },
|
|
43
|
+
actionBar: { actions: ['clear', 'today', 'cancel', 'accept'] }
|
|
44
|
+
} }));
|
|
45
|
+
};
|
|
46
|
+
const ExampleModes = () => (React.createElement(Stack, { spacing: 2 },
|
|
47
|
+
React.createElement(TimePicker, { label: "Time", defaultValue: new Date(2025, 0, 15, 14, 30) }),
|
|
48
|
+
React.createElement(DateTimePicker, { label: "Date and time", defaultValue: new Date(2025, 0, 15, 14, 30) })));
|
|
49
|
+
const ExampleMonthPicker = () => {
|
|
50
|
+
const [value, setValue] = useState(new Date(2025, 7));
|
|
51
|
+
return (React.createElement(Stack, { spacing: 2 },
|
|
52
|
+
React.createElement(Paper, null,
|
|
53
|
+
React.createElement(DateCalendar, { views: ['year', 'month'], openTo: "month", value: value, onChange: setValue })),
|
|
54
|
+
React.createElement(DatePicker, { label: "Month", views: ['year', 'month'], openTo: "month", value: value, onChange: setValue })));
|
|
55
|
+
};
|
|
56
|
+
const meta = {
|
|
57
|
+
title: 'DatePicker',
|
|
58
|
+
tags: ['autodocs'],
|
|
59
|
+
parameters: {
|
|
60
|
+
layout: 'centered',
|
|
61
|
+
docs: { description: { component: description } }
|
|
62
|
+
},
|
|
63
|
+
decorators: [
|
|
64
|
+
(Story) => (React.createElement(LocalizationProvider, { dateAdapter: AdapterDateFns, adapterLocale: enUS },
|
|
65
|
+
React.createElement(Story, null)))
|
|
66
|
+
]
|
|
67
|
+
};
|
|
68
|
+
export default meta;
|
|
69
|
+
export const Default = {
|
|
70
|
+
parameters: {
|
|
71
|
+
docs: {
|
|
72
|
+
description: {
|
|
73
|
+
story: 'A date field with `minDate`: `onError` gives the validation error, shown in `slotProps.textField.helperText`, and `slotProps.actionBar.actions` adds the clear and today buttons. The value is before `minDate`, so the error message shows.'
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
render: () => React.createElement(ExampleDatePicker, null)
|
|
78
|
+
};
|
|
79
|
+
export const Modes = {
|
|
80
|
+
parameters: {
|
|
81
|
+
docs: {
|
|
82
|
+
description: {
|
|
83
|
+
story: 'A time field with `TimePicker`, and a date and time field with `DateTimePicker`.'
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
render: () => React.createElement(ExampleModes, null)
|
|
88
|
+
};
|
|
89
|
+
export const MonthPicker = {
|
|
90
|
+
parameters: {
|
|
91
|
+
docs: {
|
|
92
|
+
description: {
|
|
93
|
+
story: "A month picker, inline with `DateCalendar` or as a field with `DatePicker`, both with `views={['year', 'month']}` and `openTo=\"month\"`."
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
render: () => React.createElement(ExampleMonthPicker, null)
|
|
98
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
interface FileInputArgs {
|
|
3
|
+
disabled: boolean;
|
|
4
|
+
isReadOnly: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare const meta: Meta<FileInputArgs>;
|
|
7
|
+
export default meta;
|
|
8
|
+
type Story = StoryObj<typeof meta>;
|
|
9
|
+
export declare const Default: Story;
|
|
10
|
+
export declare const InMenu: Story;
|
|
11
|
+
export declare const Field: Story;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { CrossSmall, Icon, Upload } from '@linagora/twake-icons';
|
|
2
|
+
import { ListItemIcon, MenuList, Paper, Stack, Typography } from '@mui/material';
|
|
3
|
+
import { MuiFileInput } from 'mui-file-input';
|
|
4
|
+
import React, { useState } from 'react';
|
|
5
|
+
import ActionsMenuItem from '../components/ActionsMenu/ActionsMenuItem';
|
|
6
|
+
import Button from '../components/Button';
|
|
7
|
+
import ListItemText from '../components/ListItemText';
|
|
8
|
+
import AlertProvider from '../providers/AlertProvider';
|
|
9
|
+
import { useAlert } from '../providers/AlertProvider/useAlert';
|
|
10
|
+
const description = `
|
|
11
|
+
To open the file picker from a button or a menu item, render the trigger as a \`label\`, with \`component="label"\`, and put a hidden \`input type="file"\` inside it. The label opens the picker on click, Enter and Space.
|
|
12
|
+
|
|
13
|
+
\`\`\`jsx
|
|
14
|
+
const handleChange = event => {
|
|
15
|
+
const files = Array.from(event.target.files)
|
|
16
|
+
event.target.value = ''
|
|
17
|
+
onUpload(files)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
<Button component="label" variant="primary" startIcon={<Icon icon={Upload} />}>
|
|
21
|
+
Upload
|
|
22
|
+
<input hidden type="file" multiple data-testid="upload-btn" onChange={handleChange} />
|
|
23
|
+
</Button>
|
|
24
|
+
\`\`\`
|
|
25
|
+
|
|
26
|
+
- Resetting \`event.target.value\` lets the user pick the same file again.
|
|
27
|
+
- \`disabled\` on the trigger also blocks the picker.
|
|
28
|
+
- To keep the picker closed, call \`event.preventDefault()\` in the \`input\`'s \`onClick\`.
|
|
29
|
+
- In tests, fire the change on the input: \`fireEvent.change(getByTestId('upload-btn'), { target: { files: [file] } })\`.
|
|
30
|
+
|
|
31
|
+
#### File field with the chosen files
|
|
32
|
+
|
|
33
|
+
For a form field that displays the chosen files, with size limits or a clear button, install [\`mui-file-input\`](https://viclafouch.github.io/mui-file-input/) in the app and use \`MuiFileInput\`, as in the **Field** story below. It is a \`TextField\`, so twake-mui themes it.
|
|
34
|
+
`;
|
|
35
|
+
const getFileNames = (files) => files.length > 0 ? files.map(file => file.name).join(', ') : 'No file chosen';
|
|
36
|
+
const readFiles = (event) => {
|
|
37
|
+
const files = Array.from(event.target.files ?? []);
|
|
38
|
+
event.target.value = '';
|
|
39
|
+
return files;
|
|
40
|
+
};
|
|
41
|
+
const UploadButton = ({ disabled }) => {
|
|
42
|
+
const [files, setFiles] = useState([]);
|
|
43
|
+
const handleChange = (event) => {
|
|
44
|
+
setFiles(readFiles(event));
|
|
45
|
+
};
|
|
46
|
+
return (React.createElement(Stack, { spacing: 2, sx: { alignItems: 'center' } },
|
|
47
|
+
React.createElement(Button, { component: "label", variant: "primary", disabled: disabled, startIcon: React.createElement(Icon, { icon: Upload }) },
|
|
48
|
+
"Upload",
|
|
49
|
+
React.createElement("input", { hidden: true, type: "file", multiple: true, "data-testid": "upload-btn", onChange: handleChange })),
|
|
50
|
+
React.createElement(Typography, { variant: "caption" }, getFileNames(files))));
|
|
51
|
+
};
|
|
52
|
+
const UploadMenuItem = ({ isReadOnly }) => {
|
|
53
|
+
const { showAlert } = useAlert();
|
|
54
|
+
const [files, setFiles] = useState([]);
|
|
55
|
+
const handleClick = (event) => {
|
|
56
|
+
if (!isReadOnly)
|
|
57
|
+
return;
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
showAlert({
|
|
60
|
+
message: 'This is a read-only folder. You cannot perform this action.',
|
|
61
|
+
severity: 'warning',
|
|
62
|
+
duration: 4000
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
const handleChange = (event) => {
|
|
66
|
+
setFiles(readFiles(event));
|
|
67
|
+
};
|
|
68
|
+
return (React.createElement(Stack, { spacing: 2, sx: { alignItems: 'center' } },
|
|
69
|
+
React.createElement(Paper, null,
|
|
70
|
+
React.createElement(MenuList, null,
|
|
71
|
+
React.createElement(ActionsMenuItem, { component: "label" },
|
|
72
|
+
React.createElement(ListItemIcon, null,
|
|
73
|
+
React.createElement(Icon, { icon: Upload })),
|
|
74
|
+
React.createElement(ListItemText, { primary: "Upload files" }),
|
|
75
|
+
React.createElement("input", { hidden: true, type: "file", multiple: true, "data-testid": "upload-btn", onClick: handleClick, onChange: handleChange })))),
|
|
76
|
+
React.createElement(Typography, { variant: "caption" }, getFileNames(files))));
|
|
77
|
+
};
|
|
78
|
+
const MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
79
|
+
const AttachmentsField = () => {
|
|
80
|
+
const [files, setFiles] = useState([]);
|
|
81
|
+
const hasTooLargeFile = files.some(file => file.size > MAX_FILE_SIZE);
|
|
82
|
+
return (React.createElement(MuiFileInput, { multiple: true, label: "Attachments", value: files, onChange: setFiles, error: hasTooLargeFile, helperText: hasTooLargeFile
|
|
83
|
+
? 'Each file must be under 5 MB'
|
|
84
|
+
: 'PDF or images, 5 MB max per file', slotProps: { htmlInput: { accept: 'image/*,.pdf' } }, clearIconButtonProps: {
|
|
85
|
+
title: 'Remove',
|
|
86
|
+
children: React.createElement(Icon, { icon: CrossSmall })
|
|
87
|
+
}, sx: { width: 360 } }));
|
|
88
|
+
};
|
|
89
|
+
const meta = {
|
|
90
|
+
title: 'FileInput',
|
|
91
|
+
tags: ['autodocs'],
|
|
92
|
+
parameters: {
|
|
93
|
+
layout: 'centered',
|
|
94
|
+
docs: { description: { component: description } }
|
|
95
|
+
},
|
|
96
|
+
args: { disabled: false, isReadOnly: false }
|
|
97
|
+
};
|
|
98
|
+
export default meta;
|
|
99
|
+
export const Default = {
|
|
100
|
+
argTypes: { isReadOnly: { table: { disable: true } } },
|
|
101
|
+
parameters: {
|
|
102
|
+
docs: {
|
|
103
|
+
description: {
|
|
104
|
+
story: 'An upload button. With `disabled`, the file picker does not open.'
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
render: args => React.createElement(UploadButton, { disabled: args.disabled })
|
|
109
|
+
};
|
|
110
|
+
export const InMenu = {
|
|
111
|
+
argTypes: { disabled: { table: { disable: true } } },
|
|
112
|
+
parameters: {
|
|
113
|
+
docs: {
|
|
114
|
+
description: {
|
|
115
|
+
story: 'An upload item in a menu. With `isReadOnly`, the click shows an alert and the file picker does not open.'
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
render: args => (React.createElement(AlertProvider, null,
|
|
120
|
+
React.createElement(UploadMenuItem, { isReadOnly: args.isReadOnly })))
|
|
121
|
+
};
|
|
122
|
+
export const Field = {
|
|
123
|
+
argTypes: {
|
|
124
|
+
disabled: { table: { disable: true } },
|
|
125
|
+
isReadOnly: { table: { disable: true } }
|
|
126
|
+
},
|
|
127
|
+
parameters: {
|
|
128
|
+
docs: {
|
|
129
|
+
description: {
|
|
130
|
+
story: 'An attachments field with `mui-file-input`: PDF or images only, a clear button, and an error message when a file is over 5 MB.'
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
render: () => React.createElement(AttachmentsField, null)
|
|
135
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linagora/twake-mui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "18.
|
|
4
|
+
"version": "18.5.0",
|
|
5
5
|
"description": "MUI v9 theme system for Twake applications",
|
|
6
6
|
"author": "Twake",
|
|
7
7
|
"license": "MIT",
|
|
@@ -60,13 +60,13 @@
|
|
|
60
60
|
"twake-i18n": ">=0.7.1"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@mui/utils": "
|
|
63
|
+
"@mui/utils": "9.2.0",
|
|
64
64
|
"@react-spring/web": "10.1.2",
|
|
65
65
|
"@use-gesture/react": "10.3.1",
|
|
66
|
-
"classnames": "
|
|
66
|
+
"classnames": "2.5.1",
|
|
67
67
|
"cozy-minilog": "^3.10.1",
|
|
68
|
-
"pdf-lib": "
|
|
69
|
-
"react-markdown": "
|
|
68
|
+
"pdf-lib": "1.17.1",
|
|
69
|
+
"react-markdown": "10.1.0",
|
|
70
70
|
"react-use-measure": "2.1.7",
|
|
71
71
|
"react-virtuoso": "4.13.0",
|
|
72
72
|
"remark-gfm": "4.0.1",
|
|
@@ -77,34 +77,37 @@
|
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@argos-ci/cli": "6.9.5",
|
|
79
79
|
"@argos-ci/storybook": "6.4.1",
|
|
80
|
-
"@emotion/react": "
|
|
81
|
-
"@emotion/styled": "
|
|
80
|
+
"@emotion/react": "11.14.0",
|
|
81
|
+
"@emotion/styled": "11.14.1",
|
|
82
82
|
"@linagora/twake-css": "^1.0.0",
|
|
83
83
|
"@linagora/twake-icons": "^2.10.0",
|
|
84
|
-
"@mui/icons-material": "
|
|
85
|
-
"@mui/lab": "
|
|
86
|
-
"@mui/material": "
|
|
84
|
+
"@mui/icons-material": "9.2.0",
|
|
85
|
+
"@mui/lab": "9.0.0-beta.6",
|
|
86
|
+
"@mui/material": "9.2.0",
|
|
87
|
+
"@mui/x-date-pickers": "9.16.0",
|
|
87
88
|
"@storybook/addon-docs": "10.3.5",
|
|
88
89
|
"@storybook/addon-vitest": "10.3.5",
|
|
89
90
|
"@storybook/builder-vite": "10.3.5",
|
|
90
91
|
"@storybook/react": "10.3.5",
|
|
91
92
|
"@storybook/react-vite": "10.3.5",
|
|
92
|
-
"@testing-library/react": "
|
|
93
|
-
"@types/classnames": "
|
|
93
|
+
"@testing-library/react": "16.3.3",
|
|
94
|
+
"@types/classnames": "2.3.4",
|
|
94
95
|
"@types/lodash": "4.17.24",
|
|
95
96
|
"@types/react": "19.3.0",
|
|
96
|
-
"@types/react-dom": "
|
|
97
|
+
"@types/react-dom": "19.3.0",
|
|
97
98
|
"@vitest/browser-playwright": "4.1.11",
|
|
98
99
|
"cozy-intent": "^2.31.1",
|
|
99
100
|
"cozy-tsconfig": "^2.0.0",
|
|
101
|
+
"date-fns": "2.30.0",
|
|
100
102
|
"eslint": "10.2.1",
|
|
101
103
|
"eslint-config-cozy-app": "7.0.0",
|
|
102
104
|
"eslint-plugin-storybook": "10.3.5",
|
|
103
|
-
"jsdom": "
|
|
105
|
+
"jsdom": "30.1.2",
|
|
104
106
|
"lodash": "4.18.1",
|
|
107
|
+
"mui-file-input": "9.0.0",
|
|
105
108
|
"playwright": "1.64.1",
|
|
106
|
-
"react": "
|
|
107
|
-
"react-dom": "
|
|
109
|
+
"react": "19.3.0",
|
|
110
|
+
"react-dom": "19.3.0",
|
|
108
111
|
"storybook": "10.3.5",
|
|
109
112
|
"twake-i18n": "^0.7.1",
|
|
110
113
|
"vite": "8.3.1",
|