@centreon/ui 25.1.5 → 25.1.7
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/Dashboard/Dashboard.styles.ts +76 -21
- package/src/Dashboard/Grid.tsx +2 -2
- package/src/Dashboard/Item.tsx +11 -9
- package/src/Dashboard/Layout.tsx +15 -32
- package/src/Dashboard/utils.ts +1 -1
- package/src/Dialog/Confirm/index.tsx +4 -2
- package/src/Dialog/Duplicate/index.tsx +6 -4
- package/src/Dialog/index.tsx +4 -1
- package/src/Form/FormButtons.tsx +1 -1
- package/src/Form/Inputs/Text.tsx +8 -4
- package/src/Graph/Chart/Chart.tsx +1 -1
- package/src/Graph/SingleBar/SingleBar.tsx +15 -8
- package/src/InputField/Number/Number.cypress.spec.tsx +6 -2
- package/src/InputField/Number/Number.tsx +10 -5
- package/src/InputField/Select/Autocomplete/Draggable/index.tsx +13 -3
- package/src/InputField/Select/Autocomplete/autoComplete.styles.ts +92 -0
- package/src/InputField/Select/Autocomplete/index.tsx +61 -129
- package/src/InputField/Text/index.tsx +46 -40
- package/src/Listing/Header/Cell/ListingHeaderCell.tsx +3 -1
- package/src/Listing/Listing.cypress.spec.tsx +2 -2
- package/src/Listing/Row/Row.tsx +1 -1
- package/src/ThemeProvider/palettes.ts +2 -2
- package/src/TopCounterElements/ResourceCounters.tsx +1 -3
- package/src/api/useMutationQuery/useOptimisticMutation.ts +8 -5
- package/src/components/Button/Icon/IconButton.tsx +32 -27
- package/src/components/CrudPage/Actions/Search.tsx +6 -2
- package/src/components/CrudPage/CrudPageRoot.tsx +3 -1
- package/src/components/DataTable/Item/DataTableItem.tsx +1 -1
- package/src/components/Form/Dashboard/GlobalRefreshFieldOption.tsx +7 -3
- package/src/components/Header/PageHeader/PageHeader.styles.ts +18 -20
- package/src/components/Layout/PageLayout/PageLayout.styles.ts +3 -3
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { equals, isEmpty, isNil, pick } from 'ramda';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import { makeStyles } from 'tss-react/mui';
|
|
4
3
|
|
|
5
4
|
import {
|
|
6
5
|
Autocomplete,
|
|
7
6
|
AutocompleteProps,
|
|
8
7
|
CircularProgress,
|
|
9
8
|
InputAdornment,
|
|
9
|
+
InputProps,
|
|
10
10
|
useTheme
|
|
11
11
|
} from '@mui/material';
|
|
12
|
-
import {
|
|
12
|
+
import { AutocompleteSlotsAndSlotProps } from '@mui/material/Autocomplete';
|
|
13
|
+
import { TextFieldSlotsAndSlotProps } from '@mui/material/TextField';
|
|
13
14
|
import { UseAutocompleteProps } from '@mui/material/useAutocomplete';
|
|
14
15
|
|
|
15
|
-
import { ThemeMode } from '@centreon/ui-context';
|
|
16
|
-
|
|
17
16
|
import { ForwardedRef, HTMLAttributes, ReactElement, forwardRef } from 'react';
|
|
18
17
|
import { SelectEntry } from '..';
|
|
19
18
|
import { getNormalizedId } from '../../../utils';
|
|
20
19
|
import TextField from '../../Text';
|
|
21
20
|
import { searchLabel } from '../../translatedLabels';
|
|
22
21
|
import Option from '../Option';
|
|
22
|
+
import { useAutoCompleteStyles } from './autoComplete.styles';
|
|
23
23
|
|
|
24
24
|
export type Props = {
|
|
25
25
|
autoFocus?: boolean;
|
|
@@ -39,96 +39,21 @@ export type Props = {
|
|
|
39
39
|
placeholder?: string | undefined;
|
|
40
40
|
required?: boolean;
|
|
41
41
|
forceInputRenderValue?: boolean;
|
|
42
|
+
textFieldSlotsAndSlotProps?: TextFieldSlotsAndSlotProps<InputProps>;
|
|
43
|
+
autocompleteSlotsAndSlotProps?: AutocompleteSlotsAndSlotProps<
|
|
44
|
+
SelectEntry,
|
|
45
|
+
Multiple,
|
|
46
|
+
DisableClearable,
|
|
47
|
+
FreeSolo
|
|
48
|
+
>;
|
|
42
49
|
} & Omit<
|
|
43
50
|
AutocompleteProps<SelectEntry, Multiple, DisableClearable, FreeSolo>,
|
|
44
51
|
'renderInput'
|
|
45
52
|
> &
|
|
46
53
|
UseAutocompleteProps<SelectEntry, Multiple, DisableClearable, FreeSolo>;
|
|
47
54
|
|
|
48
|
-
type StyledProps = Partial<Pick<Props, 'hideInput'>>;
|
|
49
|
-
|
|
50
|
-
const textfieldHeight = (hideInput?: boolean): number | undefined =>
|
|
51
|
-
hideInput ? 0 : undefined;
|
|
52
|
-
|
|
53
|
-
const useStyles = makeStyles<StyledProps>()((theme, { hideInput }) => ({
|
|
54
|
-
hiddenText: {
|
|
55
|
-
transform: 'scale(0)'
|
|
56
|
-
},
|
|
57
|
-
input: {
|
|
58
|
-
'&:after': {
|
|
59
|
-
borderBottom: 0
|
|
60
|
-
},
|
|
61
|
-
'&:before': {
|
|
62
|
-
borderBottom: 0,
|
|
63
|
-
content: 'unset'
|
|
64
|
-
},
|
|
65
|
-
'&:hover:before': {
|
|
66
|
-
borderBottom: 0
|
|
67
|
-
},
|
|
68
|
-
height: textfieldHeight(hideInput)
|
|
69
|
-
},
|
|
70
|
-
inputLabel: {
|
|
71
|
-
'&&': {
|
|
72
|
-
fontSize: theme.typography.body1.fontSize,
|
|
73
|
-
maxWidth: '72%',
|
|
74
|
-
overflow: 'hidden',
|
|
75
|
-
textOverflow: 'ellipsis',
|
|
76
|
-
transform: 'translate(12px, 14px) scale(1)',
|
|
77
|
-
whiteSpace: 'nowrap'
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
inputLabelShrink: {
|
|
81
|
-
'&&': {
|
|
82
|
-
maxWidth: '90%'
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
inputWithLabel: {
|
|
86
|
-
'&[class*="MuiFilledInput-root"]': {
|
|
87
|
-
paddingTop: theme.spacing(2)
|
|
88
|
-
},
|
|
89
|
-
paddingTop: theme.spacing(1)
|
|
90
|
-
},
|
|
91
|
-
inputWithoutLabel: {
|
|
92
|
-
'&[class*="MuiFilledInput-root"][class*="MuiFilledInput-marginDense"]': {
|
|
93
|
-
paddingBottom: hideInput ? 0 : theme.spacing(0.75),
|
|
94
|
-
paddingRight: hideInput ? 0 : theme.spacing(1),
|
|
95
|
-
paddingTop: hideInput ? 0 : theme.spacing(0.75)
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
loadingIndicator: {
|
|
99
|
-
textAlign: 'center'
|
|
100
|
-
},
|
|
101
|
-
options: {
|
|
102
|
-
alignItems: 'center',
|
|
103
|
-
display: 'grid',
|
|
104
|
-
gridAutoFlow: 'column',
|
|
105
|
-
gridGap: theme.spacing(1)
|
|
106
|
-
},
|
|
107
|
-
popper: {
|
|
108
|
-
[`& .${autocompleteClasses.listbox}`]: {
|
|
109
|
-
[`& .${autocompleteClasses.option}`]: {
|
|
110
|
-
[`&:hover, &[aria-selected="true"], &.${autocompleteClasses.focused},
|
|
111
|
-
&.${autocompleteClasses.focused}[aria-selected="true"]`]: {
|
|
112
|
-
background: equals(theme.palette.mode, ThemeMode.dark)
|
|
113
|
-
? theme.palette.primary.dark
|
|
114
|
-
: theme.palette.primary.light,
|
|
115
|
-
color: equals(theme.palette.mode, ThemeMode.dark)
|
|
116
|
-
? theme.palette.common.white
|
|
117
|
-
: theme.palette.primary.main
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
padding: 0
|
|
121
|
-
},
|
|
122
|
-
zIndex: theme.zIndex.tooltip + 1
|
|
123
|
-
},
|
|
124
|
-
textfield: {
|
|
125
|
-
height: textfieldHeight(hideInput),
|
|
126
|
-
visibility: hideInput ? 'hidden' : 'visible'
|
|
127
|
-
}
|
|
128
|
-
}));
|
|
129
|
-
|
|
130
55
|
const LoadingIndicator = (): JSX.Element => {
|
|
131
|
-
const { classes } =
|
|
56
|
+
const { classes } = useAutoCompleteStyles({});
|
|
132
57
|
|
|
133
58
|
return (
|
|
134
59
|
<div className={classes.loadingIndicator}>
|
|
@@ -163,11 +88,13 @@ const AutocompleteField = forwardRef(
|
|
|
163
88
|
autoSizeCustomPadding,
|
|
164
89
|
getOptionItemLabel = (option) => option?.name,
|
|
165
90
|
forceInputRenderValue = false,
|
|
91
|
+
textFieldSlotsAndSlotProps,
|
|
92
|
+
autocompleteSlotsAndSlotProps,
|
|
166
93
|
...autocompleteProps
|
|
167
94
|
}: Props,
|
|
168
95
|
ref?: ForwardedRef<HTMLDivElement>
|
|
169
96
|
): JSX.Element => {
|
|
170
|
-
const { classes, cx } =
|
|
97
|
+
const { classes, cx } = useAutoCompleteStyles({ hideInput });
|
|
171
98
|
const { t } = useTranslation();
|
|
172
99
|
const theme = useTheme();
|
|
173
100
|
|
|
@@ -184,33 +111,6 @@ const AutocompleteField = forwardRef(
|
|
|
184
111
|
return (
|
|
185
112
|
<TextField
|
|
186
113
|
{...params}
|
|
187
|
-
InputLabelProps={{
|
|
188
|
-
classes: {
|
|
189
|
-
marginDense: classes.inputLabel,
|
|
190
|
-
shrink: classes.inputLabelShrink
|
|
191
|
-
}
|
|
192
|
-
}}
|
|
193
|
-
InputProps={{
|
|
194
|
-
...params.InputProps,
|
|
195
|
-
endAdornment: (
|
|
196
|
-
<>
|
|
197
|
-
{endAdornment && (
|
|
198
|
-
<InputAdornment position="end">{endAdornment}</InputAdornment>
|
|
199
|
-
)}
|
|
200
|
-
{params.InputProps.endAdornment}
|
|
201
|
-
</>
|
|
202
|
-
),
|
|
203
|
-
style: {
|
|
204
|
-
background: 'transparent',
|
|
205
|
-
minWidth: 0,
|
|
206
|
-
padding: theme.spacing(
|
|
207
|
-
0.75,
|
|
208
|
-
isEmpty(placeholder) ? 0 : 5,
|
|
209
|
-
0.75,
|
|
210
|
-
0.75
|
|
211
|
-
)
|
|
212
|
-
}
|
|
213
|
-
}}
|
|
214
114
|
autoFocus={autoFocus}
|
|
215
115
|
autoSize={autoSize}
|
|
216
116
|
autoSizeCustomPadding={7 + (autoSizeCustomPadding || 0)}
|
|
@@ -220,20 +120,6 @@ const AutocompleteField = forwardRef(
|
|
|
220
120
|
}}
|
|
221
121
|
error={error}
|
|
222
122
|
externalValueForAutoSize={autocompleteProps?.value?.name}
|
|
223
|
-
inputProps={{
|
|
224
|
-
...params.inputProps,
|
|
225
|
-
'aria-label': label,
|
|
226
|
-
'data-testid': dataTestId || label,
|
|
227
|
-
id: getNormalizedId(label || ''),
|
|
228
|
-
...(forceInputRenderValue
|
|
229
|
-
? {
|
|
230
|
-
value: getOptionItemLabel(
|
|
231
|
-
autocompleteProps?.value || undefined
|
|
232
|
-
)
|
|
233
|
-
}
|
|
234
|
-
: {}),
|
|
235
|
-
...autocompleteProps?.inputProps
|
|
236
|
-
}}
|
|
237
123
|
label={label}
|
|
238
124
|
placeholder={isNil(placeholder) ? t(searchLabel) : placeholder}
|
|
239
125
|
required={required}
|
|
@@ -245,6 +131,51 @@ const AutocompleteField = forwardRef(
|
|
|
245
131
|
undefined
|
|
246
132
|
}
|
|
247
133
|
onChange={onTextChange}
|
|
134
|
+
slotProps={{
|
|
135
|
+
input: {
|
|
136
|
+
...params.InputProps,
|
|
137
|
+
endAdornment: (
|
|
138
|
+
<>
|
|
139
|
+
{endAdornment && (
|
|
140
|
+
<InputAdornment position="end">
|
|
141
|
+
{endAdornment}
|
|
142
|
+
</InputAdornment>
|
|
143
|
+
)}
|
|
144
|
+
{params.InputProps.endAdornment}
|
|
145
|
+
</>
|
|
146
|
+
),
|
|
147
|
+
style: {
|
|
148
|
+
background: 'transparent',
|
|
149
|
+
minWidth: 0,
|
|
150
|
+
padding: theme.spacing(
|
|
151
|
+
0.75,
|
|
152
|
+
isEmpty(placeholder) ? 0 : 5,
|
|
153
|
+
0.75,
|
|
154
|
+
0.75
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
inputLabel: {
|
|
159
|
+
classes: {
|
|
160
|
+
marginDense: classes.inputLabel,
|
|
161
|
+
shrink: classes.inputLabelShrink
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
htmlInput: {
|
|
165
|
+
...params.inputProps,
|
|
166
|
+
'aria-label': label,
|
|
167
|
+
'data-testid': dataTestId || label,
|
|
168
|
+
id: getNormalizedId(label || ''),
|
|
169
|
+
...(forceInputRenderValue
|
|
170
|
+
? {
|
|
171
|
+
value: getOptionItemLabel(
|
|
172
|
+
autocompleteProps?.value || undefined
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
: {}),
|
|
176
|
+
...textFieldSlotsAndSlotProps?.slotProps?.htmlInput
|
|
177
|
+
}
|
|
178
|
+
}}
|
|
248
179
|
/>
|
|
249
180
|
);
|
|
250
181
|
};
|
|
@@ -286,6 +217,7 @@ const AutocompleteField = forwardRef(
|
|
|
286
217
|
);
|
|
287
218
|
}}
|
|
288
219
|
size="small"
|
|
220
|
+
slotProps={autocompleteSlotsAndSlotProps?.slotProps}
|
|
289
221
|
{...autocompleteProps}
|
|
290
222
|
/>
|
|
291
223
|
);
|
|
@@ -6,8 +6,10 @@ import { makeStyles } from 'tss-react/mui';
|
|
|
6
6
|
import {
|
|
7
7
|
Box,
|
|
8
8
|
InputAdornment,
|
|
9
|
+
InputProps,
|
|
9
10
|
TextField as MuiTextField,
|
|
10
11
|
TextFieldProps,
|
|
12
|
+
TextFieldSlotsAndSlotProps,
|
|
11
13
|
Theme,
|
|
12
14
|
Tooltip,
|
|
13
15
|
Typography
|
|
@@ -95,6 +97,7 @@ export type TextProps = {
|
|
|
95
97
|
size?: SizeVariant;
|
|
96
98
|
transparent?: boolean;
|
|
97
99
|
value?: string;
|
|
100
|
+
textFieldSlotsAndSlotProps?: TextFieldSlotsAndSlotProps<InputProps>;
|
|
98
101
|
} & Omit<TextFieldProps, 'variant' | 'size' | 'error'>;
|
|
99
102
|
|
|
100
103
|
const TextField = forwardRef(
|
|
@@ -119,6 +122,7 @@ const TextField = forwardRef(
|
|
|
119
122
|
required = false,
|
|
120
123
|
containerClassName,
|
|
121
124
|
type,
|
|
125
|
+
textFieldSlotsAndSlotProps,
|
|
122
126
|
...rest
|
|
123
127
|
}: TextProps,
|
|
124
128
|
ref: React.ForwardedRef<HTMLDivElement>
|
|
@@ -140,7 +144,6 @@ const TextField = forwardRef(
|
|
|
140
144
|
if (debounced) {
|
|
141
145
|
return {};
|
|
142
146
|
}
|
|
143
|
-
|
|
144
147
|
if (defaultValue) {
|
|
145
148
|
return { defaultValue };
|
|
146
149
|
}
|
|
@@ -159,50 +162,11 @@ const TextField = forwardRef(
|
|
|
159
162
|
error={!isNil(error)}
|
|
160
163
|
helperText={displayErrorInTooltip ? undefined : error}
|
|
161
164
|
id={getNormalizedId(dataTestId || '')}
|
|
162
|
-
inputProps={{
|
|
163
|
-
'aria-label': ariaLabel,
|
|
164
|
-
'data-testid': dataTestId,
|
|
165
|
-
...rest.inputProps
|
|
166
|
-
}}
|
|
167
165
|
label={label}
|
|
168
166
|
ref={ref}
|
|
169
167
|
size={size || 'small'}
|
|
170
168
|
onChange={changeInputValue}
|
|
171
169
|
{...getValueProps()}
|
|
172
|
-
{...rest}
|
|
173
|
-
InputLabelProps={{
|
|
174
|
-
classes: {
|
|
175
|
-
root: cx(equals(size, 'compact') && classes.compactLabel),
|
|
176
|
-
shrink: cx(
|
|
177
|
-
equals(size, 'compact') && classes.compactLabelShrink
|
|
178
|
-
)
|
|
179
|
-
}
|
|
180
|
-
}}
|
|
181
|
-
InputProps={{
|
|
182
|
-
className: cx(
|
|
183
|
-
classes.inputBase,
|
|
184
|
-
{
|
|
185
|
-
[classes.transparent]: transparent,
|
|
186
|
-
[classes.autoSizeCompact]: autoSize && equals(size, 'compact')
|
|
187
|
-
},
|
|
188
|
-
className
|
|
189
|
-
),
|
|
190
|
-
endAdornment: (
|
|
191
|
-
<OptionalLabelInputAdornment label={label} position="end">
|
|
192
|
-
{EndAdornment ? (
|
|
193
|
-
<EndAdornment />
|
|
194
|
-
) : (
|
|
195
|
-
rest.InputProps?.endAdornment
|
|
196
|
-
)}
|
|
197
|
-
</OptionalLabelInputAdornment>
|
|
198
|
-
),
|
|
199
|
-
startAdornment: StartAdornment && (
|
|
200
|
-
<OptionalLabelInputAdornment label={label} position="start">
|
|
201
|
-
<StartAdornment />
|
|
202
|
-
</OptionalLabelInputAdornment>
|
|
203
|
-
),
|
|
204
|
-
...rest.InputProps
|
|
205
|
-
}}
|
|
206
170
|
className={classes.textField}
|
|
207
171
|
required={required}
|
|
208
172
|
sx={{
|
|
@@ -210,6 +174,48 @@ const TextField = forwardRef(
|
|
|
210
174
|
...rest?.sx
|
|
211
175
|
}}
|
|
212
176
|
type={type}
|
|
177
|
+
slotProps={{
|
|
178
|
+
input: {
|
|
179
|
+
className: cx(
|
|
180
|
+
classes.inputBase,
|
|
181
|
+
{
|
|
182
|
+
[classes.transparent]: transparent,
|
|
183
|
+
[classes.autoSizeCompact]:
|
|
184
|
+
autoSize && equals(size, 'compact')
|
|
185
|
+
},
|
|
186
|
+
className
|
|
187
|
+
),
|
|
188
|
+
endAdornment: (
|
|
189
|
+
<OptionalLabelInputAdornment label={label} position="end">
|
|
190
|
+
{EndAdornment ? (
|
|
191
|
+
<EndAdornment />
|
|
192
|
+
) : (
|
|
193
|
+
textFieldSlotsAndSlotProps?.slotProps?.input?.endAdornment
|
|
194
|
+
)}
|
|
195
|
+
</OptionalLabelInputAdornment>
|
|
196
|
+
),
|
|
197
|
+
startAdornment: StartAdornment && (
|
|
198
|
+
<OptionalLabelInputAdornment label={label} position="start">
|
|
199
|
+
<StartAdornment />
|
|
200
|
+
</OptionalLabelInputAdornment>
|
|
201
|
+
),
|
|
202
|
+
...textFieldSlotsAndSlotProps?.slotProps?.input
|
|
203
|
+
},
|
|
204
|
+
inputLabel: {
|
|
205
|
+
classes: {
|
|
206
|
+
root: cx(equals(size, 'compact') && classes.compactLabel),
|
|
207
|
+
shrink: cx(
|
|
208
|
+
equals(size, 'compact') && classes.compactLabelShrink
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
htmlInput: {
|
|
213
|
+
'aria-label': ariaLabel,
|
|
214
|
+
'data-testid': dataTestId,
|
|
215
|
+
...textFieldSlotsAndSlotProps?.slotProps?.htmlInput
|
|
216
|
+
}
|
|
217
|
+
}}
|
|
218
|
+
{...rest}
|
|
213
219
|
/>
|
|
214
220
|
</Tooltip>
|
|
215
221
|
{autoSize && (
|
|
@@ -64,7 +64,9 @@ const ListingHeaderCell = ({
|
|
|
64
64
|
|
|
65
65
|
const headerContent = (
|
|
66
66
|
<Tooltip placement="top" title={getTooltipLabel(column.shortLabel)}>
|
|
67
|
-
<
|
|
67
|
+
<span>
|
|
68
|
+
<HeaderLabel>{columnLabel}</HeaderLabel>
|
|
69
|
+
</span>
|
|
68
70
|
</Tooltip>
|
|
69
71
|
);
|
|
70
72
|
|
|
@@ -227,8 +227,8 @@ describe('Listing', () => {
|
|
|
227
227
|
|
|
228
228
|
cy.contains('Sub Item 0').realHover();
|
|
229
229
|
|
|
230
|
-
cy.get('[data-
|
|
231
|
-
cy.get('[data-
|
|
230
|
+
cy.get('[data-is-hovered="true"]').should('have.length', 1);
|
|
231
|
+
cy.get('[data-is-hovered="true"]').contains('Sub Item 0').should('exist');
|
|
232
232
|
|
|
233
233
|
cy.findByLabelText('Collapse 0').click();
|
|
234
234
|
cy.findByLabelText('Collapse 2').click();
|
package/src/Listing/Row/Row.tsx
CHANGED
|
@@ -215,7 +215,7 @@ const IntersectionRow = ({ isHovered, ...rest }: Props): JSX.Element => {
|
|
|
215
215
|
return (
|
|
216
216
|
<div
|
|
217
217
|
className={classes.intersectionRow}
|
|
218
|
-
data-
|
|
218
|
+
data-is-hovered={isHovered}
|
|
219
219
|
ref={rowRef}
|
|
220
220
|
>
|
|
221
221
|
<Row {...rest} isHovered={isHovered} isInViewport={isInViewport} />
|
|
@@ -163,7 +163,7 @@ export const lightPalette: PaletteOptions = {
|
|
|
163
163
|
active: '#666666',
|
|
164
164
|
disabled: '#999999',
|
|
165
165
|
disabledBackground: 'rgba(0, 0, 0, 0.12)',
|
|
166
|
-
focus: '
|
|
166
|
+
focus: '#dadada',
|
|
167
167
|
focusOpacity: 0.12,
|
|
168
168
|
hover: 'rgba(0, 0, 0, 0.06)',
|
|
169
169
|
hoverOpacity: 0.06,
|
|
@@ -303,7 +303,7 @@ export const darkPalette: PaletteOptions = {
|
|
|
303
303
|
active: '#B5B5B5',
|
|
304
304
|
disabled: '#999999',
|
|
305
305
|
disabledBackground: '#555555',
|
|
306
|
-
focus: '
|
|
306
|
+
focus: '#6d6d6d',
|
|
307
307
|
focusOpacity: 0.3,
|
|
308
308
|
hover: 'rgba(255, 255, 255, 0.16)',
|
|
309
309
|
hoverOpacity: 0.16,
|
|
@@ -53,9 +53,7 @@ export default ({ counters }: CounterProps): JSX.Element => {
|
|
|
53
53
|
{counters.map(
|
|
54
54
|
({ to, ariaLabel, onClick, count, severityCode }, index) => (
|
|
55
55
|
<Fragment key={to.toString().replace(/\W/g, '')}>
|
|
56
|
-
{index === 2 &&
|
|
57
|
-
<li aria-hidden="true" className={classes.splitter} />
|
|
58
|
-
)}
|
|
56
|
+
{index === 2 && <li className={classes.splitter} />}
|
|
59
57
|
<li className={classes.item}>
|
|
60
58
|
<Link
|
|
61
59
|
aria-label={ariaLabel}
|
|
@@ -60,9 +60,12 @@ export const useOptimisticMutation = <T, TMeta>({
|
|
|
60
60
|
}: GetOptimisticMutationListingProps<T, TMeta>): object => {
|
|
61
61
|
const listingQueryKey = getListingQueryKey();
|
|
62
62
|
|
|
63
|
+
const updatedPayload = payload && 'id' in payload ? payload :{...payload, id: optimisticListing?.total}
|
|
64
|
+
|
|
65
|
+
|
|
63
66
|
const hasOnlyOnePage =
|
|
64
67
|
(optimisticListing?.total || 0) <= (optimisticListing?.limit || 0);
|
|
65
|
-
const isFormDataPayload = equals(type(
|
|
68
|
+
const isFormDataPayload = equals(type(updatedPayload), 'FormData');
|
|
66
69
|
|
|
67
70
|
const items = last(
|
|
68
71
|
queryClient.getQueriesData({
|
|
@@ -71,7 +74,7 @@ export const useOptimisticMutation = <T, TMeta>({
|
|
|
71
74
|
)?.[1];
|
|
72
75
|
|
|
73
76
|
if (equals(Method.POST, method) && !isFormDataPayload && hasOnlyOnePage) {
|
|
74
|
-
const newItems = append(
|
|
77
|
+
const newItems = append(updatedPayload, items.result);
|
|
75
78
|
|
|
76
79
|
return { ...items, result: newItems };
|
|
77
80
|
}
|
|
@@ -96,12 +99,12 @@ export const useOptimisticMutation = <T, TMeta>({
|
|
|
96
99
|
);
|
|
97
100
|
const item = items.result.find(({ id }) => equals(id, _meta.id));
|
|
98
101
|
const updatedItem = equals(Method.PUT, method)
|
|
99
|
-
?
|
|
102
|
+
? updatedPayload
|
|
100
103
|
: {
|
|
101
104
|
...item,
|
|
102
105
|
...(isFormDataPayload
|
|
103
|
-
? Object.fromEntries(
|
|
104
|
-
:
|
|
106
|
+
? Object.fromEntries(updatedPayload.entries())
|
|
107
|
+
: updatedPayload)
|
|
105
108
|
};
|
|
106
109
|
const newItems = update(itemIndex, updatedItem, items.result);
|
|
107
110
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReactElement, ReactNode } from 'react';
|
|
1
|
+
import { ReactElement, ReactNode, forwardRef } from 'react';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
IconButton as MuiIconButton,
|
|
@@ -32,31 +32,36 @@ type IconButtonProps = {
|
|
|
32
32
|
/**
|
|
33
33
|
* @todo re-factor as `iconVariant: 'icon-only'` Button variant, and remove IconButton component (reason: code duplication)
|
|
34
34
|
*/
|
|
35
|
-
const IconButton = (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
data-size={size}
|
|
49
|
-
data-variant={variant}
|
|
50
|
-
disabled={disabled}
|
|
51
|
-
size={size}
|
|
52
|
-
onClick={(e) => onClick?.(e)}
|
|
53
|
-
{...attr}
|
|
54
|
-
// Mui overrides
|
|
55
|
-
color={muiColorMap[variant]}
|
|
56
|
-
>
|
|
57
|
-
{icon}
|
|
58
|
-
</MuiIconButton>
|
|
59
|
-
);
|
|
60
|
-
};
|
|
35
|
+
const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
|
|
36
|
+
(
|
|
37
|
+
{
|
|
38
|
+
variant = 'primary',
|
|
39
|
+
size = 'medium',
|
|
40
|
+
icon,
|
|
41
|
+
disabled = false,
|
|
42
|
+
onClick,
|
|
43
|
+
...attr
|
|
44
|
+
},
|
|
45
|
+
ref
|
|
46
|
+
): ReactElement => {
|
|
47
|
+
const { classes } = useStyles();
|
|
61
48
|
|
|
49
|
+
return (
|
|
50
|
+
<MuiIconButton
|
|
51
|
+
ref={ref}
|
|
52
|
+
className={classes.iconButton}
|
|
53
|
+
data-size={size}
|
|
54
|
+
data-variant={variant}
|
|
55
|
+
disabled={disabled}
|
|
56
|
+
size={size}
|
|
57
|
+
onClick={(e) => onClick?.(e)}
|
|
58
|
+
{...attr}
|
|
59
|
+
// Mui overrides
|
|
60
|
+
color={muiColorMap[variant]}
|
|
61
|
+
>
|
|
62
|
+
{icon}
|
|
63
|
+
</MuiIconButton>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
);
|
|
62
67
|
export { IconButton };
|
|
@@ -21,8 +21,12 @@ const Search = ({ label, filters }: Props): JSX.Element => {
|
|
|
21
21
|
dataTestId={label}
|
|
22
22
|
placeholder={label}
|
|
23
23
|
onChange={change}
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
textFieldSlotsAndSlotProps={{
|
|
25
|
+
slotProps: {
|
|
26
|
+
input: {
|
|
27
|
+
endAdornment: <Filters label="filters" filters={filters} />
|
|
28
|
+
}
|
|
29
|
+
}
|
|
26
30
|
}}
|
|
27
31
|
/>
|
|
28
32
|
);
|
|
@@ -83,7 +83,9 @@ export const CrudPageRoot = <
|
|
|
83
83
|
<PageLayout>
|
|
84
84
|
<PageLayout.Header>
|
|
85
85
|
<PageHeader>
|
|
86
|
-
<PageHeader.
|
|
86
|
+
<PageHeader.Main>
|
|
87
|
+
<PageHeader.Title title={labels.title} />
|
|
88
|
+
</PageHeader.Main>
|
|
87
89
|
</PageHeader>
|
|
88
90
|
</PageLayout.Header>
|
|
89
91
|
<PageLayout.Body>
|
|
@@ -52,7 +52,7 @@ const DataTableItem = forwardRef(
|
|
|
52
52
|
<img
|
|
53
53
|
alt={`thumbnail-${title}-${description}`}
|
|
54
54
|
className={classes.thumbnail}
|
|
55
|
-
data-
|
|
55
|
+
data-testid={`thumbnail-${title}-${description}`}
|
|
56
56
|
loading="lazy"
|
|
57
57
|
src={thumbnail}
|
|
58
58
|
/>
|
|
@@ -42,9 +42,13 @@ const GlobalRefreshFieldOption = (): JSX.Element => {
|
|
|
42
42
|
<TextField
|
|
43
43
|
autoSize
|
|
44
44
|
dataTestId={labelInterval}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
textFieldSlotsAndSlotProps={{
|
|
46
|
+
slotProps: {
|
|
47
|
+
htmlInput: {
|
|
48
|
+
'aria-label': t(labelInterval) as string,
|
|
49
|
+
min: 1
|
|
50
|
+
}
|
|
51
|
+
}
|
|
48
52
|
}}
|
|
49
53
|
size="compact"
|
|
50
54
|
type="number"
|