@centreon/ui 24.10.8 → 24.10.10
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/package.json +1 -1
- package/src/FileDropZone/index.tsx +21 -23
- package/src/Form/CollapsibleGroup.tsx +3 -2
- package/src/Form/Form.cypress.spec.tsx +39 -0
- package/src/Form/Inputs/Autocomplete.tsx +27 -4
- package/src/Form/Inputs/ConnectedAutocomplete.tsx +20 -10
- package/src/Form/Inputs/File.tsx +69 -0
- package/src/Form/Inputs/Grid.tsx +30 -2
- package/src/Form/Inputs/Radio.tsx +12 -4
- package/src/Form/Inputs/Switch.tsx +10 -2
- package/src/Form/Inputs/Text.tsx +13 -4
- package/src/Form/Inputs/index.tsx +5 -2
- package/src/Form/Inputs/models.ts +18 -2
- package/src/Form/storiesData.tsx +15 -3
- package/src/Form/translatedLabels.ts +1 -0
- package/src/components/DataTable/DataTable.cypress.spec.tsx +2 -1
- package/src/components/DataTable/DataTable.stories.tsx +17 -0
- package/src/components/DataTable/EmptyState/DataTableEmptyState.styles.ts +0 -1
- package/src/components/Modal/Modal.styles.ts +1 -1
package/package.json
CHANGED
|
@@ -30,23 +30,15 @@ interface StylesProps {
|
|
|
30
30
|
const useStyles = makeStyles<StylesProps>()(
|
|
31
31
|
(theme, { hasCustomDropZoneContent, isDraggingOver }) => ({
|
|
32
32
|
dropzone: {
|
|
33
|
-
'&:hover':
|
|
34
|
-
|
|
35
|
-
:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}`,
|
|
40
|
-
boxShadow: theme.shadows[3],
|
|
41
|
-
cursor: 'pointer'
|
|
42
|
-
},
|
|
43
|
-
border: `${theme.spacing(0.3)} dashed ${
|
|
44
|
-
hasCustomDropZoneContent && !isDraggingOver
|
|
45
|
-
? 'transparent'
|
|
46
|
-
: theme.palette.primary.main
|
|
47
|
-
}`,
|
|
33
|
+
'&:hover': {
|
|
34
|
+
backgroundColor: alpha(theme.palette.primary.main, 0.1),
|
|
35
|
+
boxShadow: theme.shadows[3],
|
|
36
|
+
cursor: 'pointer'
|
|
37
|
+
},
|
|
38
|
+
border: `${theme.spacing(0.3)} dashed ${theme.palette.primary.main}`,
|
|
48
39
|
boxShadow: isDraggingOver ? theme.shadows[3] : theme.shadows[0],
|
|
49
|
-
|
|
40
|
+
borderRadius: `${theme.shape.borderRadius}px`,
|
|
41
|
+
padding: theme.spacing(0.5, 1),
|
|
50
42
|
width: hasCustomDropZoneContent ? '100%' : theme.spacing(50)
|
|
51
43
|
},
|
|
52
44
|
dropzoneInfo: {
|
|
@@ -66,7 +58,7 @@ const useStyles = makeStyles<StylesProps>()(
|
|
|
66
58
|
export type CustomDropZoneContentProps = Pick<
|
|
67
59
|
UseDropzoneState,
|
|
68
60
|
'openFileExplorer'
|
|
69
|
-
|
|
61
|
+
> & { files: FileList | null; label?: string };
|
|
70
62
|
|
|
71
63
|
interface Props {
|
|
72
64
|
CustomDropZoneContent?: ({
|
|
@@ -79,6 +71,7 @@ interface Props {
|
|
|
79
71
|
maxFileSize?: number;
|
|
80
72
|
multiple?: boolean;
|
|
81
73
|
resetFilesStatusAndUploadData: () => void;
|
|
74
|
+
label?: string;
|
|
82
75
|
}
|
|
83
76
|
|
|
84
77
|
const getExtensions = cond([
|
|
@@ -115,7 +108,8 @@ const Dropzone = ({
|
|
|
115
108
|
accept,
|
|
116
109
|
CustomDropZoneContent,
|
|
117
110
|
maxFileSize,
|
|
118
|
-
className
|
|
111
|
+
className,
|
|
112
|
+
label
|
|
119
113
|
}: Props): JSX.Element => {
|
|
120
114
|
const hasCustomDropZoneContent = !isNil(CustomDropZoneContent);
|
|
121
115
|
const {
|
|
@@ -145,25 +139,29 @@ const Dropzone = ({
|
|
|
145
139
|
<div>
|
|
146
140
|
<Box
|
|
147
141
|
className={cx(classes.dropzone, className)}
|
|
148
|
-
onClick={
|
|
142
|
+
onClick={openFileExplorer}
|
|
149
143
|
onDragLeave={dragOver(false)}
|
|
150
144
|
onDragOver={dragOver(true)}
|
|
151
145
|
onDrop={dropFiles}
|
|
152
146
|
>
|
|
153
147
|
<div className={classes.dropzoneInfo}>
|
|
154
148
|
{hasCustomDropZoneContent ? (
|
|
155
|
-
<CustomDropZoneContent
|
|
149
|
+
<CustomDropZoneContent
|
|
150
|
+
openFileExplorer={openFileExplorer}
|
|
151
|
+
files={files}
|
|
152
|
+
label={label}
|
|
153
|
+
/>
|
|
156
154
|
) : (
|
|
157
|
-
|
|
155
|
+
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center' }}>
|
|
158
156
|
<PostAddIcon color="primary" fontSize="large" />
|
|
159
157
|
<Typography>
|
|
160
158
|
{t(labelDropOr)} {t(labelSelectAFile)}
|
|
161
159
|
</Typography>
|
|
162
|
-
|
|
160
|
+
</Box>
|
|
163
161
|
)}
|
|
164
162
|
<input
|
|
165
163
|
accept={accept}
|
|
166
|
-
aria-label={t(labelSelectAFile)
|
|
164
|
+
aria-label={t(labelSelectAFile)}
|
|
167
165
|
className={classes.input}
|
|
168
166
|
multiple={multiple}
|
|
169
167
|
ref={fileInputRef}
|
|
@@ -43,7 +43,8 @@ const useStyles = makeStyles()((theme) => ({
|
|
|
43
43
|
},
|
|
44
44
|
tooltip: {
|
|
45
45
|
maxWidth: theme.spacing(60)
|
|
46
|
-
}
|
|
46
|
+
},
|
|
47
|
+
title: {}
|
|
47
48
|
}));
|
|
48
49
|
|
|
49
50
|
const CollapsibleGroup = ({
|
|
@@ -97,7 +98,7 @@ const CollapsibleGroup = ({
|
|
|
97
98
|
<div className={classes.groupTitleIcon}>
|
|
98
99
|
<Typography
|
|
99
100
|
className="groupText"
|
|
100
|
-
variant="
|
|
101
|
+
variant="h6"
|
|
101
102
|
{...group?.titleAttributes}
|
|
102
103
|
>
|
|
103
104
|
{t(group?.name as string)}
|
|
@@ -131,3 +131,42 @@ describe('Form list', () => {
|
|
|
131
131
|
cy.makeSnapshot();
|
|
132
132
|
});
|
|
133
133
|
});
|
|
134
|
+
|
|
135
|
+
const initializeFile = (): void => {
|
|
136
|
+
cy.mount({
|
|
137
|
+
Component: (
|
|
138
|
+
<Form
|
|
139
|
+
initialValues={{
|
|
140
|
+
list: []
|
|
141
|
+
}}
|
|
142
|
+
inputs={[
|
|
143
|
+
{
|
|
144
|
+
fieldName: 'file',
|
|
145
|
+
group: '',
|
|
146
|
+
label: 'json',
|
|
147
|
+
type: InputType.File,
|
|
148
|
+
file: {
|
|
149
|
+
accept: '.json'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
]}
|
|
153
|
+
submit={cy.stub()}
|
|
154
|
+
validationSchema={object()}
|
|
155
|
+
/>
|
|
156
|
+
)
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
describe('File', () => {
|
|
161
|
+
it('uploads a file when a file is selected', () => {
|
|
162
|
+
initializeFile();
|
|
163
|
+
|
|
164
|
+
cy.contains('Drop or select a file').should('be.visible');
|
|
165
|
+
cy.findByLabelText('select a file').selectFile('package.json', {
|
|
166
|
+
force: true
|
|
167
|
+
});
|
|
168
|
+
cy.contains('package.json').should('be.visible');
|
|
169
|
+
|
|
170
|
+
cy.makeSnapshot();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useMemo, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import { FormikValues, useFormikContext } from 'formik';
|
|
4
|
-
import {
|
|
4
|
+
import { equals, isNil, map, not, path, prop, type } from 'ramda';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
7
|
import { FormHelperText, Stack } from '@mui/material';
|
|
@@ -53,7 +53,15 @@ const Autocomplete = ({
|
|
|
53
53
|
|
|
54
54
|
const [inputText, setInputText] = useState('');
|
|
55
55
|
|
|
56
|
-
const {
|
|
56
|
+
const {
|
|
57
|
+
values,
|
|
58
|
+
setFieldValue,
|
|
59
|
+
setFieldTouched,
|
|
60
|
+
errors,
|
|
61
|
+
touched,
|
|
62
|
+
setValues,
|
|
63
|
+
setTouched
|
|
64
|
+
} = useFormikContext<FormikValues>();
|
|
57
65
|
|
|
58
66
|
const isMultiple = equals(inputType, InputType.MultiAutocomplete);
|
|
59
67
|
|
|
@@ -67,11 +75,20 @@ const Autocomplete = ({
|
|
|
67
75
|
setInputText('');
|
|
68
76
|
|
|
69
77
|
if (change) {
|
|
70
|
-
|
|
78
|
+
setFieldTouched(fieldName, true, false);
|
|
79
|
+
change({
|
|
80
|
+
setFieldValue,
|
|
81
|
+
value: normalizedNewValues,
|
|
82
|
+
setFieldTouched,
|
|
83
|
+
setValues,
|
|
84
|
+
values,
|
|
85
|
+
setTouched
|
|
86
|
+
});
|
|
71
87
|
|
|
72
88
|
return;
|
|
73
89
|
}
|
|
74
90
|
|
|
91
|
+
setFieldTouched(fieldName, true, false);
|
|
75
92
|
setFieldValue(fieldName, normalizedNewValues);
|
|
76
93
|
};
|
|
77
94
|
|
|
@@ -83,6 +100,10 @@ const Autocomplete = ({
|
|
|
83
100
|
);
|
|
84
101
|
|
|
85
102
|
const getError = useCallback((): Array<string> | undefined => {
|
|
103
|
+
if (!path([...fieldName.split('.')], touched)) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
86
107
|
const error = path([...fieldName.split('.')], errors) as
|
|
87
108
|
| Array<string>
|
|
88
109
|
| string
|
|
@@ -111,7 +132,7 @@ const Autocomplete = ({
|
|
|
111
132
|
const filteredError = formattedError?.filter(Boolean);
|
|
112
133
|
|
|
113
134
|
return (filteredError as Array<string>) || undefined;
|
|
114
|
-
}, [errors, fieldName, isMultiple, selectedValues]);
|
|
135
|
+
}, [errors, fieldName, isMultiple, selectedValues, touched]);
|
|
115
136
|
|
|
116
137
|
const textChange = useCallback(
|
|
117
138
|
(event): void => setInputText(event.target.value),
|
|
@@ -167,6 +188,7 @@ const Autocomplete = ({
|
|
|
167
188
|
value={getValues() ?? null}
|
|
168
189
|
onChange={changeValues}
|
|
169
190
|
onTextChange={textChange}
|
|
191
|
+
style={{ width: autocomplete?.fullWidth ?? true ? 'auto' : '180px' }}
|
|
170
192
|
/>
|
|
171
193
|
{inputErrors && (
|
|
172
194
|
<Stack>
|
|
@@ -180,6 +202,7 @@ const Autocomplete = ({
|
|
|
180
202
|
</div>
|
|
181
203
|
),
|
|
182
204
|
memoProps: [
|
|
205
|
+
values,
|
|
183
206
|
getValues(),
|
|
184
207
|
inputErrors,
|
|
185
208
|
additionalLabel,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useMemo } from 'react';
|
|
2
2
|
|
|
3
3
|
import { FormikValues, useFormikContext } from 'formik';
|
|
4
|
-
import {
|
|
4
|
+
import { equals, isEmpty, path, propEq, reject, split } from 'ramda';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
7
|
import {
|
|
@@ -30,8 +30,15 @@ const ConnectedAutocomplete = ({
|
|
|
30
30
|
}: InputPropsWithoutGroup): JSX.Element => {
|
|
31
31
|
const { t } = useTranslation();
|
|
32
32
|
|
|
33
|
-
const {
|
|
34
|
-
|
|
33
|
+
const {
|
|
34
|
+
values,
|
|
35
|
+
touched,
|
|
36
|
+
errors,
|
|
37
|
+
setFieldValue,
|
|
38
|
+
setFieldTouched,
|
|
39
|
+
setValues,
|
|
40
|
+
setTouched
|
|
41
|
+
} = useFormikContext<FormikValues>();
|
|
35
42
|
|
|
36
43
|
const filterKey = connectedAutocomplete?.filterKey || defaultFilterKey;
|
|
37
44
|
|
|
@@ -58,18 +65,20 @@ const ConnectedAutocomplete = ({
|
|
|
58
65
|
const changeAutocomplete = useCallback(
|
|
59
66
|
(_, value): void => {
|
|
60
67
|
if (change) {
|
|
61
|
-
change({
|
|
68
|
+
change({
|
|
69
|
+
setFieldValue,
|
|
70
|
+
value,
|
|
71
|
+
setFieldTouched,
|
|
72
|
+
setValues,
|
|
73
|
+
values,
|
|
74
|
+
setTouched
|
|
75
|
+
});
|
|
62
76
|
|
|
63
77
|
return;
|
|
64
78
|
}
|
|
65
79
|
|
|
80
|
+
setFieldTouched(fieldName, true, false);
|
|
66
81
|
setFieldValue(fieldName, value);
|
|
67
|
-
|
|
68
|
-
if (path(fieldNamePath, touched)) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
setFieldTouched(fieldName, true);
|
|
73
82
|
},
|
|
74
83
|
[fieldName, touched, additionalMemoProps]
|
|
75
84
|
);
|
|
@@ -105,6 +114,7 @@ const ConnectedAutocomplete = ({
|
|
|
105
114
|
const deleteItem = (_, option): void => {
|
|
106
115
|
const newValue = reject(propEq(option.id, 'id'), value);
|
|
107
116
|
|
|
117
|
+
setFieldTouched(fieldName, true, false);
|
|
108
118
|
setFieldValue(fieldName, newValue);
|
|
109
119
|
};
|
|
110
120
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined';
|
|
2
|
+
import { Box, Typography } from '@mui/material';
|
|
3
|
+
import { FormikValues, useFormikContext } from 'formik';
|
|
4
|
+
import { path, split } from 'ramda';
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import FileDropZone, { transformFileListToArray } from '../../FileDropZone';
|
|
8
|
+
import { InputPropsWithoutGroup } from './models';
|
|
9
|
+
|
|
10
|
+
const File = ({
|
|
11
|
+
fieldName,
|
|
12
|
+
file,
|
|
13
|
+
change,
|
|
14
|
+
dataTestId,
|
|
15
|
+
label
|
|
16
|
+
}: InputPropsWithoutGroup): JSX.Element => {
|
|
17
|
+
const { t } = useTranslation();
|
|
18
|
+
|
|
19
|
+
const { values, setFieldValue, setFieldTouched } =
|
|
20
|
+
useFormikContext<FormikValues>();
|
|
21
|
+
|
|
22
|
+
const fieldNamePath = split('.', fieldName);
|
|
23
|
+
|
|
24
|
+
const files = useMemo(
|
|
25
|
+
() => path(fieldNamePath, values),
|
|
26
|
+
[values]
|
|
27
|
+
) as FileList;
|
|
28
|
+
|
|
29
|
+
const filesArray = transformFileListToArray(files);
|
|
30
|
+
|
|
31
|
+
const changeFiles = (newFiles: FileList | null): void => {
|
|
32
|
+
if (change) {
|
|
33
|
+
change({ setFieldValue, setFieldTouched, value: newFiles });
|
|
34
|
+
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setFieldValue(fieldName, newFiles);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Box data-testid={dataTestId} aria-label={t(label)}>
|
|
43
|
+
<Typography variant="h6">{t(label)}</Typography>
|
|
44
|
+
<Box sx={{ display: 'flex', gap: 1, flexDirection: 'column' }}>
|
|
45
|
+
<FileDropZone
|
|
46
|
+
{...file}
|
|
47
|
+
accept={file?.accept || '*'}
|
|
48
|
+
files={files || null}
|
|
49
|
+
changeFiles={changeFiles}
|
|
50
|
+
resetFilesStatusAndUploadData={() => undefined}
|
|
51
|
+
label={label}
|
|
52
|
+
/>
|
|
53
|
+
<Box sx={{ display: 'flex', gap: 1, flexDirection: 'column' }}>
|
|
54
|
+
{filesArray.map((file) => (
|
|
55
|
+
<Box
|
|
56
|
+
key={file.name}
|
|
57
|
+
sx={{ display: 'flex', gap: 1, flexDirection: 'row' }}
|
|
58
|
+
>
|
|
59
|
+
<DescriptionOutlinedIcon color="success" fontSize="small" />
|
|
60
|
+
<Typography>{file.name}</Typography>
|
|
61
|
+
</Box>
|
|
62
|
+
))}
|
|
63
|
+
</Box>
|
|
64
|
+
</Box>
|
|
65
|
+
</Box>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default File;
|
package/src/Form/Inputs/Grid.tsx
CHANGED
|
@@ -2,6 +2,8 @@ import { makeStyles } from 'tss-react/mui';
|
|
|
2
2
|
|
|
3
3
|
import { InputPropsWithoutGroup } from './models';
|
|
4
4
|
|
|
5
|
+
import { Box, Typography } from '@mui/material';
|
|
6
|
+
import { FormikValues, useFormikContext } from 'formik';
|
|
5
7
|
import { getInput } from '.';
|
|
6
8
|
|
|
7
9
|
interface StylesProps {
|
|
@@ -22,13 +24,22 @@ const useStyles = makeStyles<StylesProps>()(
|
|
|
22
24
|
})
|
|
23
25
|
);
|
|
24
26
|
|
|
25
|
-
const Grid = ({
|
|
27
|
+
const Grid = ({
|
|
28
|
+
grid,
|
|
29
|
+
hideInput
|
|
30
|
+
}: InputPropsWithoutGroup): JSX.Element | null => {
|
|
26
31
|
const { classes, cx } = useStyles({
|
|
27
32
|
alignItems: grid?.alignItems,
|
|
28
33
|
columns: grid?.columns.length,
|
|
29
34
|
gridTemplateColumns: grid?.gridTemplateColumns
|
|
30
35
|
});
|
|
31
36
|
|
|
37
|
+
const { values } = useFormikContext<FormikValues>();
|
|
38
|
+
|
|
39
|
+
if (hideInput?.(values) ?? false) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
32
43
|
const className = grid?.className || '';
|
|
33
44
|
|
|
34
45
|
return (
|
|
@@ -36,7 +47,24 @@ const Grid = ({ grid }: InputPropsWithoutGroup): JSX.Element => {
|
|
|
36
47
|
{grid?.columns.map((field) => {
|
|
37
48
|
const Input = getInput(field.type);
|
|
38
49
|
|
|
39
|
-
|
|
50
|
+
if (field.hideInput?.(values) ?? false) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Box sx={{ width: '100%' }} key={field.fieldName}>
|
|
56
|
+
{field.additionalLabel && (
|
|
57
|
+
<Typography
|
|
58
|
+
sx={{ marginBottom: 0.5, color: 'primary.main' }}
|
|
59
|
+
className={cx(field?.additionalLabelClassName)}
|
|
60
|
+
variant="h6"
|
|
61
|
+
>
|
|
62
|
+
{field.additionalLabel}
|
|
63
|
+
</Typography>
|
|
64
|
+
)}
|
|
65
|
+
<Input {...field} />
|
|
66
|
+
</Box>
|
|
67
|
+
);
|
|
40
68
|
})}
|
|
41
69
|
</div>
|
|
42
70
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FormikValues, useFormikContext } from 'formik';
|
|
2
|
-
import {
|
|
2
|
+
import { equals, includes, path, split, type } from 'ramda';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
|
|
5
5
|
import {
|
|
@@ -26,12 +26,20 @@ const Radio = ({
|
|
|
26
26
|
}: InputPropsWithoutGroup): JSX.Element => {
|
|
27
27
|
const { t } = useTranslation();
|
|
28
28
|
|
|
29
|
-
const { values, setFieldValue } =
|
|
29
|
+
const { values, setFieldValue, setFieldTouched, setValues, setTouched } =
|
|
30
|
+
useFormikContext<FormikValues>();
|
|
30
31
|
|
|
31
32
|
const changeRadio = (_, value): void => {
|
|
32
33
|
if (includes(value, ['true', 'false'])) {
|
|
33
34
|
if (change) {
|
|
34
|
-
change({
|
|
35
|
+
change({
|
|
36
|
+
setFieldValue,
|
|
37
|
+
value: equals(value, 'true'),
|
|
38
|
+
values,
|
|
39
|
+
setFieldTouched,
|
|
40
|
+
setValues,
|
|
41
|
+
setTouched
|
|
42
|
+
});
|
|
35
43
|
|
|
36
44
|
return;
|
|
37
45
|
}
|
|
@@ -42,7 +50,7 @@ const Radio = ({
|
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
if (change) {
|
|
45
|
-
change({ setFieldValue, value });
|
|
53
|
+
change({ setFieldValue, value, values, setFieldTouched, setValues });
|
|
46
54
|
|
|
47
55
|
return;
|
|
48
56
|
}
|
|
@@ -22,11 +22,19 @@ const Switch = ({
|
|
|
22
22
|
}: InputPropsWithoutGroup): JSX.Element => {
|
|
23
23
|
const { t } = useTranslation();
|
|
24
24
|
|
|
25
|
-
const { values, setFieldValue } =
|
|
25
|
+
const { values, setFieldValue, setFieldTouched, setValues, setTouched } =
|
|
26
|
+
useFormikContext<FormikValues>();
|
|
26
27
|
|
|
27
28
|
const changeSwitchValue = (event: ChangeEvent<HTMLInputElement>): void => {
|
|
28
29
|
if (change) {
|
|
29
|
-
change({
|
|
30
|
+
change({
|
|
31
|
+
setFieldValue,
|
|
32
|
+
value: event.target.checked,
|
|
33
|
+
values,
|
|
34
|
+
setFieldTouched,
|
|
35
|
+
setValues,
|
|
36
|
+
setTouched
|
|
37
|
+
});
|
|
30
38
|
|
|
31
39
|
return;
|
|
32
40
|
}
|
package/src/Form/Inputs/Text.tsx
CHANGED
|
@@ -2,11 +2,11 @@ import { ChangeEvent, useCallback, useState } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { FormikValues, useFormikContext } from 'formik';
|
|
4
4
|
import {
|
|
5
|
-
path,
|
|
6
5
|
equals,
|
|
7
6
|
gt,
|
|
8
7
|
isEmpty,
|
|
9
8
|
not,
|
|
9
|
+
path,
|
|
10
10
|
split,
|
|
11
11
|
type as variableType
|
|
12
12
|
} from 'ramda';
|
|
@@ -42,7 +42,9 @@ const Text = ({
|
|
|
42
42
|
touched,
|
|
43
43
|
errors,
|
|
44
44
|
handleBlur,
|
|
45
|
-
setFieldTouched
|
|
45
|
+
setFieldTouched,
|
|
46
|
+
setValues,
|
|
47
|
+
setTouched
|
|
46
48
|
} = useFormikContext<FormikValues>();
|
|
47
49
|
|
|
48
50
|
const fieldNamePath = split('.', fieldName);
|
|
@@ -50,7 +52,14 @@ const Text = ({
|
|
|
50
52
|
const changeText = (event: ChangeEvent<HTMLInputElement>): void => {
|
|
51
53
|
const { value } = event.target;
|
|
52
54
|
if (change) {
|
|
53
|
-
change({
|
|
55
|
+
change({
|
|
56
|
+
setFieldValue,
|
|
57
|
+
value,
|
|
58
|
+
setFieldTouched,
|
|
59
|
+
setValues,
|
|
60
|
+
values,
|
|
61
|
+
setTouched
|
|
62
|
+
});
|
|
54
63
|
|
|
55
64
|
return;
|
|
56
65
|
}
|
|
@@ -113,7 +122,7 @@ const Text = ({
|
|
|
113
122
|
return useMemoComponent({
|
|
114
123
|
Component: (
|
|
115
124
|
<TextField
|
|
116
|
-
fullWidth
|
|
125
|
+
fullWidth={text?.fullWidth ?? true}
|
|
117
126
|
EndAdornment={EndAdornment}
|
|
118
127
|
ariaLabel={t(label) || ''}
|
|
119
128
|
autoFocus={autoFocus}
|
|
@@ -36,6 +36,7 @@ import CheckboxGroup from './CheckboxGroup';
|
|
|
36
36
|
import ConnectedAutocomplete from './ConnectedAutocomplete';
|
|
37
37
|
import Custom from './Custom';
|
|
38
38
|
import FieldsTable from './FieldsTable/FieldsTable';
|
|
39
|
+
import File from './File';
|
|
39
40
|
import Grid from './Grid';
|
|
40
41
|
import List from './List/List';
|
|
41
42
|
import LoadingSkeleton from './LoadingSkeleton';
|
|
@@ -78,6 +79,7 @@ export const getInput = cond<
|
|
|
78
79
|
always(CheckboxGroup)
|
|
79
80
|
],
|
|
80
81
|
[equals(InputType.List) as (b: InputType) => boolean, always(List)],
|
|
82
|
+
[equals(InputType.File) as (b: InputType) => boolean, always(File)],
|
|
81
83
|
[T, always(TextInput)]
|
|
82
84
|
]);
|
|
83
85
|
|
|
@@ -113,7 +115,8 @@ const useStyles = makeStyles<StylesProps>()((theme, { groupDirection }) => ({
|
|
|
113
115
|
display: 'flex',
|
|
114
116
|
flexDirection: 'column',
|
|
115
117
|
marginTop: theme.spacing(1),
|
|
116
|
-
rowGap: theme.spacing(2)
|
|
118
|
+
rowGap: theme.spacing(2),
|
|
119
|
+
marginBottom: theme.spacing(1)
|
|
117
120
|
}
|
|
118
121
|
}));
|
|
119
122
|
|
|
@@ -165,7 +168,7 @@ const Inputs = ({
|
|
|
165
168
|
);
|
|
166
169
|
|
|
167
170
|
return pluck('name', usedGroups);
|
|
168
|
-
}, []);
|
|
171
|
+
}, [inputsByGroup, groups]);
|
|
169
172
|
|
|
170
173
|
const sortedInputsByGroup = useMemo(
|
|
171
174
|
() =>
|
|
@@ -19,7 +19,8 @@ export enum InputType {
|
|
|
19
19
|
Custom = 10,
|
|
20
20
|
Checkbox = 11,
|
|
21
21
|
CheckboxGroup = 12,
|
|
22
|
-
List = 13
|
|
22
|
+
List = 13,
|
|
23
|
+
File = 14
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
interface FieldsTableGetRequiredProps {
|
|
@@ -37,8 +38,16 @@ export interface InputProps {
|
|
|
37
38
|
autocomplete?: {
|
|
38
39
|
creatable?: boolean;
|
|
39
40
|
options: Array<SelectEntry>;
|
|
41
|
+
fullWidth?: boolean;
|
|
40
42
|
};
|
|
41
|
-
change?: ({
|
|
43
|
+
change?: ({
|
|
44
|
+
setFieldValue,
|
|
45
|
+
value,
|
|
46
|
+
setFieldTouched,
|
|
47
|
+
setValues,
|
|
48
|
+
values,
|
|
49
|
+
setTouched
|
|
50
|
+
}) => void;
|
|
42
51
|
checkbox?: {
|
|
43
52
|
direction?: 'horizontal' | 'vertical';
|
|
44
53
|
labelPlacement?: LabelPlacement;
|
|
@@ -51,6 +60,12 @@ export interface InputProps {
|
|
|
51
60
|
filterKey?: string;
|
|
52
61
|
getRenderedOptionText?: (option) => string | JSX.Element;
|
|
53
62
|
};
|
|
63
|
+
file?: {
|
|
64
|
+
multiple?: boolean;
|
|
65
|
+
accept?: string;
|
|
66
|
+
maxFileSize?: number;
|
|
67
|
+
CustomDropZoneContent: ({ files }) => JSX.Element;
|
|
68
|
+
};
|
|
54
69
|
custom?: {
|
|
55
70
|
Component: React.ComponentType<InputPropsWithoutGroup>;
|
|
56
71
|
};
|
|
@@ -103,6 +118,7 @@ export interface InputProps {
|
|
|
103
118
|
placeholder?: string;
|
|
104
119
|
type?: string;
|
|
105
120
|
min?: number;
|
|
121
|
+
fullWidth?: boolean;
|
|
106
122
|
};
|
|
107
123
|
type: InputType;
|
|
108
124
|
}
|
package/src/Form/storiesData.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import { Typography } from '@mui/material';
|
|
|
8
8
|
import { SelectEntry } from '../InputField/Select';
|
|
9
9
|
import { Listing } from '../api/models';
|
|
10
10
|
|
|
11
|
-
import { array, boolean, number, object, string } from 'yup';
|
|
11
|
+
import { array, boolean, mixed, number, object, string } from 'yup';
|
|
12
12
|
import {
|
|
13
13
|
Group,
|
|
14
14
|
InputProps,
|
|
@@ -78,7 +78,8 @@ export const basicFormValidationSchema = object().shape({
|
|
|
78
78
|
})
|
|
79
79
|
),
|
|
80
80
|
scopes: array().of(string().min(3, '3 characters min').required('Required')),
|
|
81
|
-
sports: array().of(selectEntryValidationSchema.required('Required'))
|
|
81
|
+
sports: array().of(selectEntryValidationSchema.required('Required')),
|
|
82
|
+
file: mixed()
|
|
82
83
|
});
|
|
83
84
|
|
|
84
85
|
const roleEntries: Array<SelectEntry> = [
|
|
@@ -134,7 +135,8 @@ export const basicFormInitialValues = {
|
|
|
134
135
|
}
|
|
135
136
|
],
|
|
136
137
|
scopes: [],
|
|
137
|
-
sports: []
|
|
138
|
+
sports: [],
|
|
139
|
+
file: null
|
|
138
140
|
};
|
|
139
141
|
|
|
140
142
|
export const classOptions = [...Array(10).keys()].map((idx) => ({
|
|
@@ -444,6 +446,16 @@ export const basicFormInputs: Array<InputProps> = [
|
|
|
444
446
|
multilineRows: 4
|
|
445
447
|
},
|
|
446
448
|
type: InputType.Text
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
fieldName: 'file',
|
|
452
|
+
group: 'First group',
|
|
453
|
+
label: 'File',
|
|
454
|
+
type: InputType.File,
|
|
455
|
+
file: {
|
|
456
|
+
accept: 'image/*',
|
|
457
|
+
multiple: true
|
|
458
|
+
}
|
|
447
459
|
}
|
|
448
460
|
];
|
|
449
461
|
|
|
@@ -48,6 +48,23 @@ export const AsEmptyState: Story = {
|
|
|
48
48
|
}
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
export const AsEmptyStateWithDescription: Story = {
|
|
52
|
+
args: {
|
|
53
|
+
children: (
|
|
54
|
+
<DataTable.EmptyState
|
|
55
|
+
labels={{
|
|
56
|
+
actions: {
|
|
57
|
+
create: 'Create item'
|
|
58
|
+
},
|
|
59
|
+
title: 'No items found',
|
|
60
|
+
description: 'Description'
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
),
|
|
64
|
+
isEmpty: true
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
51
68
|
export const withFixedHeightContainer: Story = {
|
|
52
69
|
args: { ...Default.args },
|
|
53
70
|
render: (args) => (
|