@centreon/ui 23.10.96 → 23.10.97
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 +2 -2
- package/src/Dashboard/Dashboard.styles.ts +7 -1
- package/src/Dashboard/Item.tsx +6 -6
- package/src/InputField/Search/PersistentTooltip.tsx +5 -6
- package/src/InputField/Select/Autocomplete/Connected/Multi/MultiConnectedAutocompleteField.cypress.spec.tsx +3 -35
- package/src/InputField/Select/Autocomplete/Connected/index.test.tsx +2 -6
- package/src/InputField/Select/Autocomplete/Multi/index.tsx +21 -18
- package/src/RichTextEditor/RichTextEditor.tsx +4 -1
- package/src/RichTextEditor/plugins/ToolbarPlugin/index.tsx +6 -4
- package/src/Wizard/index.tsx +5 -2
- package/src/Wizard/models.ts +1 -0
- package/src/api/buildListingEndpoint/getSearchQueryParameterValue.ts +33 -22
- package/src/api/buildListingEndpoint/index.test.ts +2 -2
- package/src/api/buildListingEndpoint/models.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@centreon/ui",
|
|
3
|
-
"version": "23.10.
|
|
3
|
+
"version": "23.10.97",
|
|
4
4
|
"description": "Centreon UI Components",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"eslint": "eslint ./src --ext .js,.jsx,.ts,.tsx --max-warnings 0",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"@lexical/rich-text": "^0.11.3",
|
|
108
108
|
"@lexical/selection": "0.11.3",
|
|
109
109
|
"@lexical/utils": "0.11.3",
|
|
110
|
-
"@react-spring/web": "9.
|
|
110
|
+
"@react-spring/web": "^9.7.3",
|
|
111
111
|
"@visx/curve": "^2.1.0",
|
|
112
112
|
"@visx/group": "^3.3.0",
|
|
113
113
|
"@visx/pattern": "^3.0.0",
|
|
@@ -92,7 +92,13 @@ export const useDashboardItemStyles = makeStyles<{ hasHeader: boolean }>()(
|
|
|
92
92
|
'&[data-canMove="true"]': {
|
|
93
93
|
cursor: 'move'
|
|
94
94
|
},
|
|
95
|
-
padding: theme.spacing(0.5, 2)
|
|
95
|
+
padding: theme.spacing(0.5, 2),
|
|
96
|
+
position: 'relative'
|
|
97
|
+
},
|
|
98
|
+
widgetHeaderDraggable: {
|
|
99
|
+
height: '100%',
|
|
100
|
+
position: 'absolute',
|
|
101
|
+
width: '100%'
|
|
96
102
|
},
|
|
97
103
|
widgetPadding: {
|
|
98
104
|
overflowX: 'auto',
|
package/src/Dashboard/Item.tsx
CHANGED
|
@@ -72,12 +72,12 @@ const Item = forwardRef(
|
|
|
72
72
|
data-padding={!disablePadding}
|
|
73
73
|
>
|
|
74
74
|
{header && (
|
|
75
|
-
<div
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
<div className={classes.widgetHeader} data-canMove={canMove}>
|
|
76
|
+
<div
|
|
77
|
+
{...listeners}
|
|
78
|
+
className={classes.widgetHeaderDraggable}
|
|
79
|
+
data-testid={`${id}_move_panel`}
|
|
80
|
+
/>
|
|
81
81
|
{header}
|
|
82
82
|
</div>
|
|
83
83
|
)}
|
|
@@ -10,16 +10,15 @@ import IconClose from '@mui/icons-material/HighlightOff';
|
|
|
10
10
|
const useStyles = makeStyles()((theme) => ({
|
|
11
11
|
buttonClose: {
|
|
12
12
|
position: 'absolute',
|
|
13
|
-
right: theme.spacing(0.5),
|
|
14
|
-
top:
|
|
13
|
+
right: theme.spacing(-0.5),
|
|
14
|
+
top: theme.spacing(-1)
|
|
15
15
|
},
|
|
16
16
|
tooltip: {
|
|
17
17
|
backgroundColor: theme.palette.common.white,
|
|
18
18
|
boxShadow: theme.shadows[3],
|
|
19
19
|
color: theme.palette.text.primary,
|
|
20
20
|
fontSize: theme.typography.pxToRem(12),
|
|
21
|
-
maxWidth: 500
|
|
22
|
-
padding: theme.spacing(1, 2, 1, 1)
|
|
21
|
+
maxWidth: 500
|
|
23
22
|
}
|
|
24
23
|
}));
|
|
25
24
|
|
|
@@ -60,7 +59,7 @@ const PersistentTooltip = ({
|
|
|
60
59
|
};
|
|
61
60
|
|
|
62
61
|
const title = (
|
|
63
|
-
|
|
62
|
+
<div style={{ position: 'relative' }}>
|
|
64
63
|
<IconButton
|
|
65
64
|
className={classes.buttonClose}
|
|
66
65
|
size="small"
|
|
@@ -69,7 +68,7 @@ const PersistentTooltip = ({
|
|
|
69
68
|
<IconClose fontSize="small" />
|
|
70
69
|
</IconButton>
|
|
71
70
|
{children}
|
|
72
|
-
|
|
71
|
+
</div>
|
|
73
72
|
);
|
|
74
73
|
|
|
75
74
|
return (
|
|
@@ -82,34 +82,6 @@ describe('Multi connected autocomplete', () => {
|
|
|
82
82
|
cy.makeSnapshot('hides the list when the user clicks on the input');
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
-
it('displays exactly one option on the list when the user types that option', () => {
|
|
86
|
-
cy.get('[data-testid="Multi Connected Autocomplete"]').as('input');
|
|
87
|
-
|
|
88
|
-
cy.get('@input').click();
|
|
89
|
-
|
|
90
|
-
cy.waitForRequest('@getListOptions');
|
|
91
|
-
|
|
92
|
-
cy.findByRole('presentation').as('listOptions');
|
|
93
|
-
|
|
94
|
-
cy.get('@listOptions').should('be.visible');
|
|
95
|
-
|
|
96
|
-
cy.get('@input').type(optionOne);
|
|
97
|
-
|
|
98
|
-
cy.waitForRequest('@getSearchedOption');
|
|
99
|
-
|
|
100
|
-
cy.fixture('inputField/searchedOption').then((optionData) => {
|
|
101
|
-
cy.get('@listOptions')
|
|
102
|
-
.find('li')
|
|
103
|
-
.should('have.length', optionData.result.length);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
cy.get('@listOptions').within(() => {
|
|
107
|
-
cy.contains(optionOne).should('be.visible');
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
cy.makeSnapshot();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
85
|
it('displays all options on the list when the user searches for and selects an option', () => {
|
|
114
86
|
cy.get('[data-testid="Multi Connected Autocomplete"]').as('input');
|
|
115
87
|
|
|
@@ -125,13 +97,11 @@ describe('Multi connected autocomplete', () => {
|
|
|
125
97
|
|
|
126
98
|
cy.waitForRequest('@getSearchedOption');
|
|
127
99
|
|
|
128
|
-
cy.fixture('inputField/searchedOption').then((
|
|
129
|
-
cy.get('@listOptions')
|
|
130
|
-
.find('li')
|
|
131
|
-
.should('have.length', optionData.result.length);
|
|
100
|
+
cy.fixture('inputField/searchedOption').then(() => {
|
|
101
|
+
cy.get('@listOptions').find('li').should('have.length', 5);
|
|
132
102
|
});
|
|
133
103
|
|
|
134
|
-
cy.get('[type="checkbox"]').check();
|
|
104
|
+
cy.get('[type="checkbox"]').eq(0).check();
|
|
135
105
|
cy.get('@input')
|
|
136
106
|
.parent()
|
|
137
107
|
.within(() => {
|
|
@@ -153,7 +123,5 @@ describe('Multi connected autocomplete', () => {
|
|
|
153
123
|
});
|
|
154
124
|
});
|
|
155
125
|
});
|
|
156
|
-
|
|
157
|
-
cy.makeSnapshot();
|
|
158
126
|
});
|
|
159
127
|
});
|
|
@@ -95,9 +95,7 @@ describe(SingleConnectedAutocompleteField, () => {
|
|
|
95
95
|
|
|
96
96
|
await waitFor(() => {
|
|
97
97
|
expect(getFetchCall(2)).toEqual(
|
|
98
|
-
`${baseEndpoint}?page=1&search
|
|
99
|
-
'{"$and":[{"host.name":{"$lk":"%My Option 2%"}}]}'
|
|
100
|
-
)}`
|
|
98
|
+
`${baseEndpoint}?page=1&search=%7B%22%24and%22%3A%5B%7B%22%24or%22%3A%5B%7B%22host.name%22%3A%7B%22%24lk%22%3A%22%25My%20Option%202%25%22%7D%7D%5D%7D%5D%7D`
|
|
101
99
|
);
|
|
102
100
|
});
|
|
103
101
|
});
|
|
@@ -118,9 +116,7 @@ describe(SingleConnectedAutocompleteField, () => {
|
|
|
118
116
|
|
|
119
117
|
await waitFor(() => {
|
|
120
118
|
expect(getFetchCall(0)).toEqual(
|
|
121
|
-
`${baseEndpoint}?page=1&search
|
|
122
|
-
'{"$and":[{"parent_name":{"$eq":"Centreon-Server"}}]}'
|
|
123
|
-
)}`
|
|
119
|
+
`${baseEndpoint}?page=1&search=%7B%22%24and%22%3A%5B%7B%22%24or%22%3A%5B%7B%22parent_name%22%3A%7B%22%24eq%22%3A%22Centreon-Server%22%7D%7D%5D%7D%5D%7D`
|
|
124
120
|
);
|
|
125
121
|
});
|
|
126
122
|
});
|
|
@@ -53,24 +53,27 @@ const MultiAutocompleteField = ({
|
|
|
53
53
|
const { classes } = useStyles();
|
|
54
54
|
|
|
55
55
|
const renderTags = (renderedValue, getTagProps): Array<JSX.Element> =>
|
|
56
|
-
renderedValue.map((option, index) =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
56
|
+
renderedValue.map((option, index) => {
|
|
57
|
+
return (
|
|
58
|
+
<Tooltip
|
|
59
|
+
key={option.id}
|
|
60
|
+
placement="top"
|
|
61
|
+
title={getOptionTooltipLabel?.(option)}
|
|
62
|
+
>
|
|
63
|
+
<Chip
|
|
64
|
+
classes={{
|
|
65
|
+
deleteIcon: classes.deleteIcon,
|
|
66
|
+
root: classes.tag
|
|
67
|
+
}}
|
|
68
|
+
label={getTagLabel(option)}
|
|
69
|
+
size="medium"
|
|
70
|
+
{...getTagProps({ index })}
|
|
71
|
+
{...chipProps}
|
|
72
|
+
onDelete={(event) => chipProps?.onDelete?.(event, option)}
|
|
73
|
+
/>
|
|
74
|
+
</Tooltip>
|
|
75
|
+
);
|
|
76
|
+
});
|
|
74
77
|
|
|
75
78
|
const getLimitTagsText = (more): JSX.Element => <Option>{`+${more}`}</Option>;
|
|
76
79
|
|
|
@@ -38,6 +38,7 @@ export interface RichTextEditorProps {
|
|
|
38
38
|
openLinkInNewTab?: boolean;
|
|
39
39
|
placeholder?: string;
|
|
40
40
|
resetEditorToInitialStateCondition?: () => boolean;
|
|
41
|
+
toolbarClassName?: string;
|
|
41
42
|
toolbarPositions?: 'start' | 'end';
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -136,7 +137,8 @@ const RichTextEditor = ({
|
|
|
136
137
|
disabled = false,
|
|
137
138
|
openLinkInNewTab = true,
|
|
138
139
|
initialize,
|
|
139
|
-
displayBlockButtons = true
|
|
140
|
+
displayBlockButtons = true,
|
|
141
|
+
toolbarClassName
|
|
140
142
|
}: RichTextEditorProps): JSX.Element => {
|
|
141
143
|
const { classes } = useStyles({ toolbarPositions });
|
|
142
144
|
|
|
@@ -175,6 +177,7 @@ const RichTextEditor = ({
|
|
|
175
177
|
<div className={classes.container}>
|
|
176
178
|
<div className={classes.toolbar}>
|
|
177
179
|
<ToolbarPlugin
|
|
180
|
+
className={toolbarClassName}
|
|
178
181
|
disabled={disabled}
|
|
179
182
|
displayBlockButtons={displayBlockButtons}
|
|
180
183
|
displayMacrosButton={displayMacrosButton}
|
|
@@ -9,6 +9,7 @@ import BlockButtons from './BlockButtons';
|
|
|
9
9
|
import AlignPicker from './AlignPicker';
|
|
10
10
|
|
|
11
11
|
interface Props {
|
|
12
|
+
className?: string;
|
|
12
13
|
disabled: boolean;
|
|
13
14
|
displayBlockButtons: boolean;
|
|
14
15
|
displayMacrosButton?: boolean;
|
|
@@ -36,12 +37,13 @@ const ToolbarPlugin = ({
|
|
|
36
37
|
editable,
|
|
37
38
|
displayMacrosButton,
|
|
38
39
|
disabled,
|
|
39
|
-
displayBlockButtons
|
|
40
|
+
displayBlockButtons,
|
|
41
|
+
className
|
|
40
42
|
}: Props): JSX.Element | null => {
|
|
41
|
-
const { classes } = useStyles();
|
|
43
|
+
const { cx, classes } = useStyles();
|
|
42
44
|
|
|
43
45
|
return editable ? (
|
|
44
|
-
<div className={classes.container}>
|
|
46
|
+
<div className={cx(classes.container, className)}>
|
|
45
47
|
<UndoRedoButtons disabled={disabled} />
|
|
46
48
|
{displayBlockButtons && (
|
|
47
49
|
<>
|
|
@@ -51,13 +53,13 @@ const ToolbarPlugin = ({
|
|
|
51
53
|
)}
|
|
52
54
|
<Divider flexItem orientation="vertical" />
|
|
53
55
|
<FormatButtons disabled={disabled} />
|
|
56
|
+
<AlignPicker disabled={disabled} />
|
|
54
57
|
{displayMacrosButton && (
|
|
55
58
|
<>
|
|
56
59
|
<Divider flexItem orientation="vertical" />
|
|
57
60
|
<MacrosButton disabled={disabled} />
|
|
58
61
|
</>
|
|
59
62
|
)}
|
|
60
|
-
<AlignPicker disabled={disabled} />
|
|
61
63
|
</div>
|
|
62
64
|
) : null;
|
|
63
65
|
};
|
package/src/Wizard/index.tsx
CHANGED
|
@@ -29,6 +29,7 @@ const actionsBarLabelsDefaultValues = {
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
const Wizard = ({
|
|
32
|
+
classNameDialogContent,
|
|
32
33
|
steps,
|
|
33
34
|
onSubmit = undefined,
|
|
34
35
|
initialValues = {},
|
|
@@ -41,7 +42,7 @@ const Wizard = ({
|
|
|
41
42
|
displayConfirmDialog,
|
|
42
43
|
...rest
|
|
43
44
|
}: WizardProps): JSX.Element => {
|
|
44
|
-
const { classes } = useStyles();
|
|
45
|
+
const { classes, cx } = useStyles();
|
|
45
46
|
const [currentStep, setCurrentStep] = useState(0);
|
|
46
47
|
const [sendingRequest, setSendingRequest] = useState(false);
|
|
47
48
|
const [openConfirm, setOpenConfirm] = useState(false);
|
|
@@ -131,7 +132,9 @@ const Wizard = ({
|
|
|
131
132
|
validationSchema={validationSchema}
|
|
132
133
|
onSubmit={submit}
|
|
133
134
|
>
|
|
134
|
-
<DialogContent
|
|
135
|
+
<DialogContent
|
|
136
|
+
className={cx(classes.dialogContent, classNameDialogContent)}
|
|
137
|
+
>
|
|
135
138
|
<WizardContent
|
|
136
139
|
actionsBarLabels={actionsBarLabels}
|
|
137
140
|
currentStep={currentStep}
|
package/src/Wizard/models.ts
CHANGED
|
@@ -45,6 +45,7 @@ type ValueOf<T> = T[keyof T];
|
|
|
45
45
|
export interface WizardProps {
|
|
46
46
|
[key: string]: ValueOf<DialogProps>;
|
|
47
47
|
actionsBarLabels?: ActionsBarLabels;
|
|
48
|
+
classNameDialogContent?: string;
|
|
48
49
|
confirmDialogLabels?: ConfirmDialogLabels;
|
|
49
50
|
displayConfirmDialog?: boolean;
|
|
50
51
|
fullHeight?: boolean;
|
|
@@ -5,9 +5,10 @@ import {
|
|
|
5
5
|
prop,
|
|
6
6
|
head,
|
|
7
7
|
toPairs,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
flatten,
|
|
9
|
+
pluck,
|
|
10
|
+
uniq,
|
|
11
|
+
equals
|
|
11
12
|
} from 'ramda';
|
|
12
13
|
|
|
13
14
|
import {
|
|
@@ -83,28 +84,38 @@ const getConditionsSearchQueryParameterValue = (
|
|
|
83
84
|
return undefined;
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
87
|
+
const fields = uniq(pluck('field', conditions));
|
|
88
|
+
|
|
89
|
+
const toIndividualOperatorValues = (
|
|
90
|
+
listField: string
|
|
91
|
+
): { $or: Array<Record<string, unknown>> } => {
|
|
92
|
+
const filteredItems = conditions.filter(({ field }) =>
|
|
93
|
+
equals(listField, field)
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
$or: flatten(
|
|
98
|
+
filteredItems.map(({ value, values }) => {
|
|
99
|
+
if (!isNil(value)) {
|
|
100
|
+
return [
|
|
101
|
+
{
|
|
102
|
+
[listField]: value
|
|
103
|
+
}
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return toPairs(values || {}).map(([operator, operatorValue]) => ({
|
|
108
|
+
[listField]: {
|
|
109
|
+
[operator]: operatorValue
|
|
110
|
+
}
|
|
111
|
+
}));
|
|
112
|
+
})
|
|
113
|
+
)
|
|
114
|
+
};
|
|
104
115
|
};
|
|
105
116
|
|
|
106
117
|
return {
|
|
107
|
-
$and:
|
|
118
|
+
$and: fields.map(toIndividualOperatorValues)
|
|
108
119
|
};
|
|
109
120
|
};
|
|
110
121
|
|
|
@@ -115,7 +115,7 @@ describe(buildListingEndpoint, () => {
|
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
expect(decodeURIComponent(endpointWithValues)).toContain(
|
|
118
|
-
'&search={"$and":[{"date":{"$gt":"2020-12-01T07:00:00"}},{"date":{"$lt":"2020-12-01T11:00:00"}}]}'
|
|
118
|
+
'&search={"$and":[{"$or":[{"date":{"$gt":"2020-12-01T07:00:00"}},{"date":{"$lt":"2020-12-01T11:00:00"}}]}]}'
|
|
119
119
|
);
|
|
120
120
|
|
|
121
121
|
const endpointWithValue = buildListingEndpoint({
|
|
@@ -134,7 +134,7 @@ describe(buildListingEndpoint, () => {
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
expect(decodeURIComponent(endpointWithValue)).toContain(
|
|
137
|
-
'&search={"$and":[{"is_activated":true}]}'
|
|
137
|
+
'&search={"$and":[{"$or":[{"is_activated":true}]}]}'
|
|
138
138
|
);
|
|
139
139
|
});
|
|
140
140
|
});
|