@centreon/ui 24.4.59 → 24.4.61
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 +50 -42
- package/public/mockServiceWorker.js +1 -1
- package/src/ActionsList/ActionsList.styles.ts +40 -71
- package/src/Button/Icon/index.tsx +1 -1
- package/src/Button/Save/StartIcon.tsx +3 -3
- package/src/Button/Save/index.tsx +9 -5
- package/src/Checkbox/Checkbox.tsx +2 -2
- package/src/Checkbox/CheckboxGroup/index.tsx +2 -2
- package/src/Dashboard/Item.tsx +1 -1
- package/src/Dashboard/Layout.tsx +2 -2
- package/src/Dialog/Confirm/index.tsx +10 -2
- package/src/Dialog/UnsavedChanges/index.tsx +21 -20
- package/src/Dialog/UnsavedChanges/translatedLabels.ts +4 -6
- package/src/Dialog/index.tsx +9 -2
- package/src/FallbackPage/FallbackPage.tsx +3 -3
- package/src/FileDropZone/index.tsx +3 -1
- package/src/Form/Form.cypress.spec.tsx +133 -0
- package/src/Form/Inputs/List/Content.tsx +62 -0
- package/src/Form/Inputs/List/List.styles.ts +29 -0
- package/src/Form/Inputs/List/List.tsx +58 -0
- package/src/Form/Inputs/List/useList.ts +81 -0
- package/src/Form/Inputs/Text.tsx +3 -1
- package/src/Form/Inputs/index.tsx +3 -1
- package/src/Form/Inputs/models.ts +10 -1
- package/src/Graph/BarStack/BarStack.cypress.spec.tsx +154 -0
- package/src/Graph/BarStack/BarStack.stories.tsx +123 -0
- package/src/Graph/BarStack/BarStack.styles.ts +37 -0
- package/src/Graph/BarStack/BarStack.tsx +14 -0
- package/src/Graph/BarStack/ResponsiveBarStack.tsx +222 -0
- package/src/Graph/BarStack/index.ts +1 -0
- package/src/Graph/BarStack/models.ts +20 -0
- package/src/Graph/BarStack/useResponsiveBarStack.ts +137 -0
- package/src/Graph/Gauge/Gauge.cypress.spec.tsx +102 -0
- package/src/Graph/Gauge/Gauge.tsx +1 -1
- package/src/Graph/HeatMap/HeatMap.cypress.spec.tsx +145 -0
- package/src/Graph/HeatMap/HeatMap.stories.tsx +0 -25
- package/src/Graph/HeatMap/ResponsiveHeatMap.tsx +8 -2
- package/src/Graph/Legend/Legend.tsx +21 -0
- package/src/Graph/Legend/index.ts +1 -0
- package/src/Graph/Legend/models.ts +11 -0
- package/src/Graph/LineChart/BasicComponents/Lines/Threshold/Circle.tsx +2 -2
- package/src/Graph/LineChart/BasicComponents/Thresholds.tsx +2 -2
- package/src/Graph/LineChart/BasicComponents/useFilterLines.ts +1 -1
- package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/GuidingLines.tsx +2 -2
- package/src/Graph/LineChart/InteractiveComponents/Annotations/EventAnnotations.tsx +1 -1
- package/src/Graph/LineChart/Legend/Legend.styles.ts +1 -1
- package/src/Graph/LineChart/Legend/LegendHeader.tsx +1 -1
- package/src/Graph/LineChart/Legend/useInteractiveValues.ts +2 -2
- package/src/Graph/LineChart/Legend/useLegend.ts +3 -3
- package/src/Graph/LineChart/helpers/doc.ts +16 -13
- package/src/Graph/LineChart/helpers/index.ts +1 -1
- package/src/Graph/LineChart/index.stories.tsx +4 -2
- package/src/Graph/LineChart/index.tsx +1 -1
- package/src/Graph/PieChart/PieChart.cypress.spec.tsx +169 -0
- package/src/Graph/PieChart/PieChart.stories.tsx +194 -0
- package/src/Graph/PieChart/PieChart.styles.ts +39 -0
- package/src/Graph/PieChart/PieChart.tsx +14 -0
- package/src/Graph/PieChart/ResponsivePie.tsx +254 -0
- package/src/Graph/PieChart/index.ts +1 -0
- package/src/Graph/PieChart/models.ts +20 -0
- package/src/Graph/PieChart/useResponsivePie.ts +85 -0
- package/src/Graph/SingleBar/SingleBar.cypress.spec.tsx +121 -0
- package/src/Graph/SingleBar/Thresholds.tsx +2 -2
- package/src/Graph/Text/Text.cypress.spec.tsx +101 -0
- package/src/Graph/Text/Text.stories.tsx +60 -4
- package/src/Graph/Text/Text.tsx +1 -1
- package/src/Graph/common/testUtils.ts +71 -0
- package/src/Graph/common/timeSeries/index.ts +22 -14
- package/src/Graph/common/utils.ts +19 -0
- package/src/Graph/index.ts +3 -0
- package/src/Graph/translatedLabels.ts +1 -0
- package/src/InputField/Select/Autocomplete/Connected/index.tsx +10 -7
- package/src/InputField/Select/Autocomplete/Draggable/SortableList.tsx +1 -1
- package/src/InputField/Select/Autocomplete/Draggable/SortableListContent.tsx +1 -1
- package/src/InputField/Select/Autocomplete/Draggable/index.tsx +1 -1
- package/src/InputField/Select/Autocomplete/Multi/index.tsx +4 -2
- package/src/InputField/Select/Autocomplete/index.tsx +129 -116
- package/src/InputField/Select/IconPopover/index.tsx +2 -2
- package/src/InputField/Select/index.tsx +15 -2
- package/src/InputField/Text/index.tsx +2 -2
- package/src/Listing/ActionBar/index.tsx +9 -8
- package/src/Listing/Cell/DataCell.styles.ts +3 -0
- package/src/Listing/Cell/DataCell.tsx +23 -5
- package/src/Listing/Header/ListingHeader.tsx +1 -1
- package/src/Listing/Listing.cypress.spec.tsx +218 -33
- package/src/Listing/Listing.styles.ts +4 -7
- package/src/Listing/Row/Row.tsx +7 -3
- package/src/Listing/index.stories.tsx +37 -3
- package/src/Listing/index.test.tsx +1 -1
- package/src/Listing/index.tsx +80 -36
- package/src/Listing/models.ts +1 -0
- package/src/Listing/useStyleTable.ts +1 -0
- package/src/Module/Module.cypress.spec.tsx +129 -0
- package/src/Module/index.tsx +2 -4
- package/src/PopoverMenu/index.tsx +6 -5
- package/src/RichTextEditor/RichTextEditor.tsx +12 -1
- package/src/SortableItems/index.tsx +2 -7
- package/src/ThemeProvider/index.tsx +24 -0
- package/src/TimePeriods/CustomTimePeriod/CompactCustomTimePeriod.styles.ts +6 -7
- package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +8 -3
- package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +0 -2
- package/src/TimePeriods/DateTimePickerInput.tsx +56 -19
- package/src/TimePeriods/ResolutionTimePeriod.cypress.spec.tsx +12 -9
- package/src/TimePeriods/TimePeriods.cypress.spec.tsx +9 -33
- package/src/TimePeriods/helpers/index.ts +1 -1
- package/src/TimePeriods/index.stories.tsx +12 -4
- package/src/TimePeriods/index.tsx +2 -2
- package/src/Typography/FluidTypography/FluidTypography.cypress.spec.tsx +27 -0
- package/src/Typography/FluidTypography/index.stories.tsx +2 -2
- package/src/Typography/FluidTypography/index.tsx +21 -28
- package/src/api/QueryProvider.tsx +1 -1
- package/src/api/TestQueryProvider.tsx +1 -1
- package/src/api/index.ts +3 -3
- package/src/api/useFetchQuery/index.ts +27 -23
- package/src/api/useMutationQuery/index.test.ts +4 -4
- package/src/api/useMutationQuery/index.ts +60 -25
- package/src/components/Button/Icon/IconButton.tsx +6 -2
- package/src/components/DataTable/DataListing.tsx +6 -0
- package/src/components/DataTable/DataTable.cypress.spec.tsx +174 -0
- package/src/components/DataTable/DataTable.stories.tsx +40 -0
- package/src/components/DataTable/DataTable.styles.ts +3 -0
- package/src/components/DataTable/DataTable.tsx +3 -3
- package/src/components/DataTable/Item/DataTableItem.styles.ts +7 -2
- package/src/components/DataTable/Item/DataTableItem.tsx +4 -60
- package/src/components/DataTable/index.ts +3 -1
- package/src/components/Form/AccessRights/AccessRights.cypress.spec.tsx +13 -13
- package/src/components/Form/AccessRights/ShareInput/ContactSwitch.tsx +3 -3
- package/src/components/Form/AccessRights/ShareInput/ShareInput.tsx +1 -0
- package/src/components/Form/AccessRights/storiesData.ts +22 -22
- package/src/components/Form/Dashboard/DashboardDuplicationForm.tsx +85 -0
- package/src/components/Form/Dashboard/DashboardForm.tsx +15 -12
- package/src/components/Form/Dashboard/index.ts +1 -0
- package/src/components/Form/FormActions.tsx +7 -2
- package/src/components/ItemComposition/ItemComposition.styles.ts +2 -2
- package/src/components/Layout/PageLayout/PageLayout.tsx +1 -1
- package/src/components/Layout/PageLayout/PageLayoutActions.tsx +1 -0
- package/src/components/Layout/PageLayout/PageLayoutBody.tsx +1 -0
- package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +5 -1
- package/src/components/Layout/PageLayout/PageQuickAccess.tsx +76 -0
- package/src/components/Layout/PageLayout/index.ts +3 -1
- package/src/components/Layout/PageLayout.cypress.spec.tsx +66 -0
- package/src/components/List/Item/ListItem.tsx +3 -3
- package/src/components/Modal/ConfirmationModal/ConfirmationModal.cypress.spec.tsx +168 -0
- package/src/components/Modal/ConfirmationModal/ConfirmationModal.stories.tsx +62 -0
- package/src/components/Modal/ConfirmationModal/ConfirmationModal.tsx +87 -0
- package/src/components/Modal/Modal.styles.ts +8 -3
- package/src/components/Modal/index.ts +2 -0
- package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.stories.tsx +3 -3
- package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.tsx +1 -1
- package/src/components/Tooltip/ConfirmationTooltip/models.ts +1 -1
- package/src/index.ts +2 -2
- package/src/queryParameters/url/index.ts +5 -1
- package/src/utils/index.ts +1 -1
- package/src/utils/useFullscreen/Fullscreen.cypress.spec.tsx +1 -3
- package/src/utils/useFullscreen/useFullscreenListener.ts +10 -7
- package/src/utils/useInfiniteScrollListing.ts +4 -1
- package/src/utils/{useLicenseExpirationWarning.cypress.spec.tsx → useLicenseExpirationWarning.test.tsx} +48 -37
- package/src/utils/useLicenseExpirationWarning.ts +18 -18
- package/src/utils/usePluralizedTranslation.ts +21 -0
- package/src/Typography/FluidTypography/useFluidResizeObserver.ts +0 -56
- package/src/screens/dashboard/DashboardsDetail.stories.tsx +0 -108
- package/src/screens/dashboard/DashboardsOverview.stories.tsx +0 -281
- package/src/utils/useDateTimePickerAdapter.ts +0 -309
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from '@tanstack/react-query';
|
|
9
9
|
import { JsonDecoder } from 'ts.data.json';
|
|
10
10
|
import anylogger from 'anylogger';
|
|
11
|
-
import { includes } from 'ramda';
|
|
11
|
+
import { includes, omit } from 'ramda';
|
|
12
12
|
|
|
13
13
|
import { CatchErrorProps, customFetch, ResponseError } from '../customFetch';
|
|
14
14
|
import useSnackbar from '../../Snackbar/useSnackbar';
|
|
@@ -22,6 +22,11 @@ export enum Method {
|
|
|
22
22
|
PUT = 'PUT'
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
interface Variables<TMeta, T> {
|
|
26
|
+
_meta?: TMeta;
|
|
27
|
+
payload?: T;
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
export type UseMutationQueryProps<T, TMeta> = {
|
|
26
31
|
baseEndpoint?: string;
|
|
27
32
|
catchError?: (props: CatchErrorProps) => void;
|
|
@@ -31,14 +36,39 @@ export type UseMutationQueryProps<T, TMeta> = {
|
|
|
31
36
|
getEndpoint: (_meta: TMeta) => string;
|
|
32
37
|
httpCodesBypassErrorSnackbar?: Array<number>;
|
|
33
38
|
method: Method;
|
|
34
|
-
|
|
39
|
+
onError?: (
|
|
40
|
+
error: ResponseError,
|
|
41
|
+
variables: Variables<TMeta, T>,
|
|
42
|
+
context: unknown
|
|
43
|
+
) => unknown;
|
|
44
|
+
onMutate?: (variables: Variables<TMeta, T>) => Promise<unknown> | unknown;
|
|
45
|
+
onSuccess?: (
|
|
46
|
+
data: ResponseError | T,
|
|
47
|
+
variables: Variables<TMeta, T>,
|
|
48
|
+
context: unknown
|
|
49
|
+
) => unknown;
|
|
50
|
+
} & Omit<
|
|
51
|
+
UseMutationOptions<{ _meta?: TMeta; payload: T }>,
|
|
52
|
+
'mutationFn' | 'onError' | 'onMutate' | 'onSuccess' | 'mutateAsync' | 'mutate'
|
|
53
|
+
>;
|
|
35
54
|
|
|
36
55
|
const log = anylogger('API Request');
|
|
37
56
|
|
|
38
|
-
export type UseMutationQueryState<T> =
|
|
57
|
+
export type UseMutationQueryState<T, TMeta> = Omit<
|
|
58
|
+
UseMutationResult<T | ResponseError>,
|
|
59
|
+
'isError' | 'mutate' | 'mutateAsync'
|
|
60
|
+
> & {
|
|
39
61
|
isError: boolean;
|
|
40
62
|
isMutating: boolean;
|
|
41
|
-
|
|
63
|
+
mutate: (variables: Variables<TMeta, T>) => ResponseError | T;
|
|
64
|
+
mutateAsync: (
|
|
65
|
+
variables: Variables<TMeta, T>,
|
|
66
|
+
rest?: Pick<
|
|
67
|
+
UseMutationQueryProps<T, TMeta>,
|
|
68
|
+
'onError' | 'onMutate' | 'onSettled' | 'onSuccess'
|
|
69
|
+
>
|
|
70
|
+
) => Promise<ResponseError | T>;
|
|
71
|
+
};
|
|
42
72
|
|
|
43
73
|
const useMutationQuery = <T extends object, TMeta>({
|
|
44
74
|
getEndpoint,
|
|
@@ -51,17 +81,20 @@ const useMutationQuery = <T extends object, TMeta>({
|
|
|
51
81
|
onMutate,
|
|
52
82
|
onError,
|
|
53
83
|
onSuccess,
|
|
84
|
+
onSettled,
|
|
54
85
|
baseEndpoint
|
|
55
|
-
}: UseMutationQueryProps<T, TMeta>): UseMutationQueryState<T> => {
|
|
86
|
+
}: UseMutationQueryProps<T, TMeta>): UseMutationQueryState<T, TMeta> => {
|
|
56
87
|
const { showErrorMessage } = useSnackbar();
|
|
57
88
|
|
|
58
89
|
const queryData = useMutation<
|
|
59
90
|
T | ResponseError,
|
|
60
91
|
ResponseError,
|
|
61
|
-
|
|
62
|
-
>(
|
|
63
|
-
|
|
64
|
-
|
|
92
|
+
Variables<TMeta, T>
|
|
93
|
+
>({
|
|
94
|
+
mutationFn: (
|
|
95
|
+
variables: Variables<TMeta, T>
|
|
96
|
+
): Promise<T | ResponseError> => {
|
|
97
|
+
const { _meta, payload } = variables || {};
|
|
65
98
|
|
|
66
99
|
return customFetch<T>({
|
|
67
100
|
baseEndpoint,
|
|
@@ -78,19 +111,18 @@ const useMutationQuery = <T extends object, TMeta>({
|
|
|
78
111
|
payload
|
|
79
112
|
});
|
|
80
113
|
},
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
onSuccess?.(data, variables, context);
|
|
114
|
+
onError,
|
|
115
|
+
onMutate,
|
|
116
|
+
onSettled,
|
|
117
|
+
onSuccess: (data, variables, context) => {
|
|
118
|
+
if (data?.isError) {
|
|
119
|
+
onError?.(data, variables, context);
|
|
120
|
+
|
|
121
|
+
return;
|
|
91
122
|
}
|
|
123
|
+
onSuccess?.(data, variables, context);
|
|
92
124
|
}
|
|
93
|
-
);
|
|
125
|
+
});
|
|
94
126
|
|
|
95
127
|
const manageError = (): void => {
|
|
96
128
|
const data = queryData.data as ResponseError | undefined;
|
|
@@ -107,14 +139,17 @@ const useMutationQuery = <T extends object, TMeta>({
|
|
|
107
139
|
}
|
|
108
140
|
};
|
|
109
141
|
|
|
110
|
-
useEffect(
|
|
111
|
-
|
|
112
|
-
|
|
142
|
+
useEffect(
|
|
143
|
+
() => {
|
|
144
|
+
manageError();
|
|
145
|
+
},
|
|
146
|
+
useDeepCompare([queryData.data])
|
|
147
|
+
);
|
|
113
148
|
|
|
114
149
|
return {
|
|
115
|
-
...queryData,
|
|
150
|
+
...omit(['isError'], queryData),
|
|
116
151
|
isError: (queryData.data as ResponseError | undefined)?.isError || false,
|
|
117
|
-
isMutating: queryData.
|
|
152
|
+
isMutating: queryData.isPending
|
|
118
153
|
};
|
|
119
154
|
};
|
|
120
155
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import React, { ReactElement, ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
IconButton as MuiIconButton,
|
|
5
|
+
IconButtonProps as MuiIconButtonProps
|
|
6
|
+
} from '@mui/material';
|
|
4
7
|
|
|
5
8
|
import { AriaLabelingAttributes } from '../../../@types/aria-attributes';
|
|
6
9
|
import { DataTestAttributes } from '../../../@types/data-attributes';
|
|
@@ -23,7 +26,8 @@ type IconButtonProps = {
|
|
|
23
26
|
size?: 'small' | 'medium' | 'large';
|
|
24
27
|
variant?: 'primary' | 'secondary' | 'ghost';
|
|
25
28
|
} & AriaLabelingAttributes &
|
|
26
|
-
DataTestAttributes
|
|
29
|
+
DataTestAttributes &
|
|
30
|
+
MuiIconButtonProps;
|
|
27
31
|
|
|
28
32
|
/**
|
|
29
33
|
* @todo re-factor as `iconVariant: 'icon-only'` Button variant, and remove IconButton component (reason: code duplication)
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Box } from '@mui/material';
|
|
2
|
+
import Delete from '@mui/icons-material/Delete';
|
|
3
|
+
|
|
4
|
+
import { IconButton } from '../..';
|
|
5
|
+
import { ColumnType } from '../../Listing/models';
|
|
6
|
+
|
|
7
|
+
import { DataTable } from '.';
|
|
8
|
+
|
|
9
|
+
const data = Array(5)
|
|
10
|
+
.fill(0)
|
|
11
|
+
.map((_, idx) => ({
|
|
12
|
+
description: `Description ${idx}`,
|
|
13
|
+
id: idx,
|
|
14
|
+
title: `Entity ${idx}`
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const CardActions = (): JSX.Element => {
|
|
18
|
+
return (
|
|
19
|
+
<IconButton ariaLabel="Delete" title="Delete" onClick={cy.stub()}>
|
|
20
|
+
<Delete fontSize="small" />
|
|
21
|
+
</IconButton>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const initializeDataTableGrid = ({ hasActions, hasCardAction }): void => {
|
|
26
|
+
cy.viewport(1200, 590);
|
|
27
|
+
cy.mount({
|
|
28
|
+
Component: (
|
|
29
|
+
<DataTable variant="grid">
|
|
30
|
+
{data.map(({ title, description }) => (
|
|
31
|
+
<DataTable.Item
|
|
32
|
+
Actions={<CardActions />}
|
|
33
|
+
description={description}
|
|
34
|
+
hasActions={hasActions}
|
|
35
|
+
hasCardAction={hasCardAction}
|
|
36
|
+
key={title}
|
|
37
|
+
title={title}
|
|
38
|
+
/>
|
|
39
|
+
))}
|
|
40
|
+
</DataTable>
|
|
41
|
+
)
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const initializeDataTableEmpty = (canCreate = false): void => {
|
|
46
|
+
cy.viewport(1200, 590);
|
|
47
|
+
cy.mount({
|
|
48
|
+
Component: (
|
|
49
|
+
<DataTable isEmpty variant="grid">
|
|
50
|
+
<DataTable.EmptyState
|
|
51
|
+
canCreate={canCreate}
|
|
52
|
+
labels={{
|
|
53
|
+
actions: {
|
|
54
|
+
create: 'Create'
|
|
55
|
+
},
|
|
56
|
+
title: 'Welcome'
|
|
57
|
+
}}
|
|
58
|
+
onCreate={cy.stub()}
|
|
59
|
+
/>
|
|
60
|
+
</DataTable>
|
|
61
|
+
)
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const initializeDataTableListing = (): void => {
|
|
66
|
+
cy.viewport(1200, 590);
|
|
67
|
+
cy.mount({
|
|
68
|
+
Component: (
|
|
69
|
+
<Box sx={{ height: '100vh' }}>
|
|
70
|
+
<DataTable variant="listing">
|
|
71
|
+
<DataTable.Listing
|
|
72
|
+
columns={[
|
|
73
|
+
{
|
|
74
|
+
getFormattedString: (row) => row.title,
|
|
75
|
+
id: 'title',
|
|
76
|
+
label: 'Title',
|
|
77
|
+
type: ColumnType.string
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
getFormattedString: (row) => row.description,
|
|
81
|
+
id: 'description',
|
|
82
|
+
label: 'Description',
|
|
83
|
+
type: ColumnType.string
|
|
84
|
+
}
|
|
85
|
+
]}
|
|
86
|
+
rows={data}
|
|
87
|
+
/>
|
|
88
|
+
</DataTable>
|
|
89
|
+
</Box>
|
|
90
|
+
)
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
describe('DataTable: Grid', () => {
|
|
95
|
+
it('displays items with title and description only', () => {
|
|
96
|
+
initializeDataTableGrid({
|
|
97
|
+
hasActions: false,
|
|
98
|
+
hasCardAction: false
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
data.forEach(({ title, description }) => {
|
|
102
|
+
cy.contains(title).should('be.visible');
|
|
103
|
+
cy.contains(description).should('be.visible');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
cy.makeSnapshot();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('displays items with actions', () => {
|
|
110
|
+
initializeDataTableGrid({
|
|
111
|
+
hasActions: true,
|
|
112
|
+
hasCardAction: false
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
cy.findAllByLabelText('Delete').should('have.length', 5);
|
|
116
|
+
|
|
117
|
+
cy.makeSnapshot();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('displays items with card action only', () => {
|
|
121
|
+
initializeDataTableGrid({
|
|
122
|
+
hasActions: false,
|
|
123
|
+
hasCardAction: true
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
cy.findAllByLabelText('view').should('have.length', 5);
|
|
127
|
+
|
|
128
|
+
cy.makeSnapshot();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('displays items with card action and bottom actions', () => {
|
|
132
|
+
initializeDataTableGrid({
|
|
133
|
+
hasActions: true,
|
|
134
|
+
hasCardAction: true
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
cy.findAllByLabelText('view').should('have.length', 5);
|
|
138
|
+
cy.findAllByLabelText('Delete').should('have.length', 5);
|
|
139
|
+
|
|
140
|
+
cy.makeSnapshot();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('DataTable: Empty', () => {
|
|
145
|
+
it('displays the title', () => {
|
|
146
|
+
initializeDataTableEmpty();
|
|
147
|
+
|
|
148
|
+
cy.contains('Welcome').should('be.visible');
|
|
149
|
+
|
|
150
|
+
cy.makeSnapshot();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('displays the title and the action button', () => {
|
|
154
|
+
initializeDataTableEmpty(true);
|
|
155
|
+
|
|
156
|
+
cy.contains('Welcome').should('be.visible');
|
|
157
|
+
cy.contains('Create').should('be.visible');
|
|
158
|
+
|
|
159
|
+
cy.makeSnapshot();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('DataTable: Listing', () => {
|
|
164
|
+
it('displays the listing', () => {
|
|
165
|
+
initializeDataTableListing();
|
|
166
|
+
|
|
167
|
+
data.forEach(({ title, description }) => {
|
|
168
|
+
cy.contains(title).should('be.visible');
|
|
169
|
+
cy.contains(description).should('be.visible');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
cy.makeSnapshot();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
|
|
3
|
+
import { Box } from '@mui/material';
|
|
4
|
+
|
|
5
|
+
import { ColumnType } from '../../Listing/models';
|
|
6
|
+
|
|
3
7
|
import { DataTable } from './index';
|
|
4
8
|
|
|
5
9
|
const meta: Meta<typeof DataTable> = {
|
|
@@ -49,3 +53,39 @@ export const withFixedHeightContainer: Story = {
|
|
|
49
53
|
</div>
|
|
50
54
|
)
|
|
51
55
|
};
|
|
56
|
+
|
|
57
|
+
const ListingTemplate = (args): JSX.Element => (
|
|
58
|
+
<Box sx={{ height: '80vh' }}>
|
|
59
|
+
<DataTable {...args} />
|
|
60
|
+
</Box>
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
export const listing: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
children: (
|
|
66
|
+
<DataTable.Listing
|
|
67
|
+
columns={[
|
|
68
|
+
{
|
|
69
|
+
getFormattedString: (row) => row.title,
|
|
70
|
+
id: 'title',
|
|
71
|
+
label: 'Title',
|
|
72
|
+
type: ColumnType.string
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
getFormattedString: (row) => row.description,
|
|
76
|
+
id: 'description',
|
|
77
|
+
label: 'Description',
|
|
78
|
+
type: ColumnType.string
|
|
79
|
+
}
|
|
80
|
+
]}
|
|
81
|
+
rows={[...Array(5)].map((_, i) => ({
|
|
82
|
+
description: `Item description ${i}`,
|
|
83
|
+
id: i,
|
|
84
|
+
title: `Item ${i}`
|
|
85
|
+
}))}
|
|
86
|
+
/>
|
|
87
|
+
),
|
|
88
|
+
variant: 'listing'
|
|
89
|
+
},
|
|
90
|
+
render: ListingTemplate
|
|
91
|
+
};
|
|
@@ -10,6 +10,9 @@ const useStyles = makeStyles()((theme) => ({
|
|
|
10
10
|
gridGap: theme.spacing(2.5),
|
|
11
11
|
gridTemplateColumns: `repeat(auto-fill, ${theme.spacing(45)})`
|
|
12
12
|
},
|
|
13
|
+
'&[data-variant="listing"]': {
|
|
14
|
+
height: '100%'
|
|
15
|
+
},
|
|
13
16
|
'&[data-variant][data-is-empty="true"]': {
|
|
14
17
|
display: 'flex',
|
|
15
18
|
justifyContent: 'center',
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ReactElement, ReactNode } from 'react';
|
|
2
2
|
|
|
3
3
|
import { useStyles } from './DataTable.styles';
|
|
4
4
|
|
|
5
5
|
type DataTableProps = {
|
|
6
|
-
children
|
|
6
|
+
children?: ReactNode | Array<ReactNode>;
|
|
7
7
|
isEmpty?: boolean;
|
|
8
|
-
variant
|
|
8
|
+
variant: 'grid' | 'listing';
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
/** *
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { makeStyles } from 'tss-react/mui';
|
|
2
2
|
|
|
3
3
|
const useStyles = makeStyles()((theme) => ({
|
|
4
|
+
actions: {
|
|
5
|
+
display: 'flex',
|
|
6
|
+
flexDirection: 'row',
|
|
7
|
+
justifyContent: 'space-between'
|
|
8
|
+
},
|
|
4
9
|
dataTableItem: {
|
|
5
10
|
'& .MuiCardActionArea-root': {
|
|
6
11
|
alignItems: 'flex-start',
|
|
7
12
|
display: 'flex',
|
|
8
13
|
flexDirection: 'column',
|
|
9
14
|
height: '100%',
|
|
10
|
-
justifyContent: '
|
|
15
|
+
justifyContent: 'flex-start'
|
|
11
16
|
},
|
|
12
17
|
'& .MuiCardActions-root': {
|
|
13
18
|
'& > span': {
|
|
@@ -15,13 +20,13 @@ const useStyles = makeStyles()((theme) => ({
|
|
|
15
20
|
gap: theme.spacing(1)
|
|
16
21
|
},
|
|
17
22
|
display: 'flex',
|
|
18
|
-
|
|
19
23
|
justifyContent: 'space-between'
|
|
20
24
|
},
|
|
21
25
|
borderRadius: theme.shape.borderRadius,
|
|
22
26
|
display: 'flex',
|
|
23
27
|
flexDirection: 'column',
|
|
24
28
|
height: '186px',
|
|
29
|
+
justifyContent: 'space-between',
|
|
25
30
|
p: {
|
|
26
31
|
color: theme.palette.text.secondary,
|
|
27
32
|
letterSpacing: '0',
|
|
@@ -7,32 +7,15 @@ import {
|
|
|
7
7
|
CardContent as MuiCardContent,
|
|
8
8
|
Typography as MuiTypography
|
|
9
9
|
} from '@mui/material';
|
|
10
|
-
import {
|
|
11
|
-
Delete as DeleteIcon,
|
|
12
|
-
Settings as SettingsIcon,
|
|
13
|
-
Share as ShareIcon
|
|
14
|
-
} from '@mui/icons-material';
|
|
15
|
-
|
|
16
|
-
import { IconButton } from '../../Button';
|
|
17
|
-
import { ConfirmationTooltip } from '../../Tooltip/ConfirmationTooltip';
|
|
18
10
|
|
|
19
11
|
import { useStyles } from './DataTableItem.styles';
|
|
20
12
|
|
|
21
13
|
export interface DataTableItemProps {
|
|
14
|
+
Actions?: JSX.Element;
|
|
22
15
|
description?: string;
|
|
23
16
|
hasActions?: boolean;
|
|
24
17
|
hasCardAction?: boolean;
|
|
25
|
-
labelsDelete: {
|
|
26
|
-
cancel: string;
|
|
27
|
-
confirm: {
|
|
28
|
-
label: string;
|
|
29
|
-
secondaryLabel?: string;
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
18
|
onClick?: () => void;
|
|
33
|
-
onDelete?: () => void;
|
|
34
|
-
onEdit?: () => void;
|
|
35
|
-
onEditAccessRights?: () => void;
|
|
36
19
|
title: string;
|
|
37
20
|
}
|
|
38
21
|
|
|
@@ -44,10 +27,7 @@ const DataTableItem = forwardRef(
|
|
|
44
27
|
hasCardAction = false,
|
|
45
28
|
hasActions = false,
|
|
46
29
|
onClick,
|
|
47
|
-
|
|
48
|
-
onDelete,
|
|
49
|
-
onEditAccessRights,
|
|
50
|
-
labelsDelete
|
|
30
|
+
Actions
|
|
51
31
|
}: DataTableItemProps,
|
|
52
32
|
ref
|
|
53
33
|
): ReactElement => {
|
|
@@ -75,44 +55,8 @@ const DataTableItem = forwardRef(
|
|
|
75
55
|
</ActionArea>
|
|
76
56
|
{hasActions && (
|
|
77
57
|
<MuiCardActions>
|
|
78
|
-
<span
|
|
79
|
-
|
|
80
|
-
<ConfirmationTooltip
|
|
81
|
-
confirmVariant="error"
|
|
82
|
-
labels={labelsDelete}
|
|
83
|
-
onConfirm={onDelete}
|
|
84
|
-
>
|
|
85
|
-
{(openTooltip) => (
|
|
86
|
-
<IconButton
|
|
87
|
-
aria-label="delete"
|
|
88
|
-
data-testid="delete"
|
|
89
|
-
icon={<DeleteIcon />}
|
|
90
|
-
size="small"
|
|
91
|
-
variant="ghost"
|
|
92
|
-
onClick={openTooltip}
|
|
93
|
-
/>
|
|
94
|
-
)}
|
|
95
|
-
</ConfirmationTooltip>
|
|
96
|
-
)}
|
|
97
|
-
</span>
|
|
98
|
-
<span>
|
|
99
|
-
<IconButton
|
|
100
|
-
aria-label="edit access rights"
|
|
101
|
-
data-testid="edit-access-rights"
|
|
102
|
-
icon={<ShareIcon />}
|
|
103
|
-
size="small"
|
|
104
|
-
variant="primary"
|
|
105
|
-
onClick={() => onEditAccessRights?.()}
|
|
106
|
-
/>
|
|
107
|
-
<IconButton
|
|
108
|
-
aria-label="edit"
|
|
109
|
-
data-testid="edit"
|
|
110
|
-
icon={<SettingsIcon />}
|
|
111
|
-
size="small"
|
|
112
|
-
variant="primary"
|
|
113
|
-
onClick={() => onEdit?.()}
|
|
114
|
-
/>
|
|
115
|
-
</span>
|
|
58
|
+
<span />
|
|
59
|
+
<span>{Actions}</span>
|
|
116
60
|
</MuiCardActions>
|
|
117
61
|
)}
|
|
118
62
|
</MuiCard>
|
|
@@ -2,9 +2,11 @@ import { DataTable as DataTableRoot } from './DataTable';
|
|
|
2
2
|
import { DataTableItem } from './Item/DataTableItem';
|
|
3
3
|
import { DataTableItemSkeleton } from './Item/DataTableItemSkeleton';
|
|
4
4
|
import { DataTableEmptyState } from './EmptyState/DataTableEmptyState';
|
|
5
|
+
import { DataListing } from './DataListing';
|
|
5
6
|
|
|
6
7
|
export const DataTable = Object.assign(DataTableRoot, {
|
|
7
8
|
EmptyState: DataTableEmptyState,
|
|
8
9
|
Item: DataTableItem,
|
|
9
|
-
ItemSkeleton: DataTableItemSkeleton
|
|
10
|
+
ItemSkeleton: DataTableItemSkeleton,
|
|
11
|
+
Listing: DataListing
|
|
10
12
|
});
|
|
@@ -116,15 +116,15 @@ describe('Access rights', () => {
|
|
|
116
116
|
it('displays a removed chip when the corresponding icon is clicked', () => {
|
|
117
117
|
initialize({});
|
|
118
118
|
|
|
119
|
-
cy.findByTestId(`remove-
|
|
119
|
+
cy.findByTestId(`remove-Kathy Schmitt`).should(
|
|
120
120
|
'have.attr',
|
|
121
121
|
'data-removed',
|
|
122
122
|
'false'
|
|
123
123
|
);
|
|
124
124
|
|
|
125
|
-
cy.findByTestId(`remove-
|
|
125
|
+
cy.findByTestId(`remove-Kathy Schmitt`).click();
|
|
126
126
|
|
|
127
|
-
cy.findByTestId(`remove-
|
|
127
|
+
cy.findByTestId(`remove-Kathy Schmitt`).should(
|
|
128
128
|
'have.attr',
|
|
129
129
|
'data-removed',
|
|
130
130
|
'true'
|
|
@@ -137,18 +137,18 @@ describe('Access rights', () => {
|
|
|
137
137
|
it('restores the contact when the contact is removed and the corresponding icon is clicked', () => {
|
|
138
138
|
initialize({});
|
|
139
139
|
|
|
140
|
-
cy.findByTestId(`remove-
|
|
140
|
+
cy.findByTestId(`remove-Kathy Schmitt`).click();
|
|
141
141
|
|
|
142
|
-
cy.findByTestId(`remove-
|
|
142
|
+
cy.findByTestId(`remove-Kathy Schmitt`).should(
|
|
143
143
|
'have.attr',
|
|
144
144
|
'data-removed',
|
|
145
145
|
'true'
|
|
146
146
|
);
|
|
147
147
|
cy.contains(labels.list.removed).should('be.visible');
|
|
148
148
|
|
|
149
|
-
cy.findByTestId(`remove-
|
|
149
|
+
cy.findByTestId(`remove-Kathy Schmitt`).click();
|
|
150
150
|
|
|
151
|
-
cy.findByTestId(`remove-
|
|
151
|
+
cy.findByTestId(`remove-Kathy Schmitt`).should(
|
|
152
152
|
'have.attr',
|
|
153
153
|
'data-removed',
|
|
154
154
|
'false'
|
|
@@ -161,9 +161,9 @@ describe('Access rights', () => {
|
|
|
161
161
|
it('submits the new acces rights list without the removed contact', () => {
|
|
162
162
|
const { save } = initialize({});
|
|
163
163
|
|
|
164
|
-
cy.findByTestId(`remove-
|
|
164
|
+
cy.findByTestId(`remove-Kathy Schmitt`).click();
|
|
165
165
|
|
|
166
|
-
cy.findByTestId(`remove-
|
|
166
|
+
cy.findByTestId(`remove-Kathy Schmitt`).should(
|
|
167
167
|
'have.attr',
|
|
168
168
|
'data-removed',
|
|
169
169
|
'true'
|
|
@@ -183,7 +183,7 @@ describe('Access rights', () => {
|
|
|
183
183
|
it('submits the new acces rights list with the updated contact', () => {
|
|
184
184
|
const { save } = initialize({});
|
|
185
185
|
|
|
186
|
-
cy.findByTestId(`role-
|
|
186
|
+
cy.findByTestId(`role-Kathy Schmitt`).parent().click();
|
|
187
187
|
|
|
188
188
|
cy.get('li[data-value="editor"]').click();
|
|
189
189
|
cy.contains(labels.list.updated).should('be.visible');
|
|
@@ -201,7 +201,7 @@ describe('Access rights', () => {
|
|
|
201
201
|
it('removes the updated chip when the contact role is updated and its initial role is assigned back', () => {
|
|
202
202
|
initialize({});
|
|
203
203
|
|
|
204
|
-
cy.findByTestId(`role-
|
|
204
|
+
cy.findByTestId(`role-Kathy Schmitt`).parent().click();
|
|
205
205
|
|
|
206
206
|
cy.get('li[data-value="editor"]').click();
|
|
207
207
|
cy.contains(labels.list.updated).should('be.visible');
|
|
@@ -291,10 +291,10 @@ describe('Access rights', () => {
|
|
|
291
291
|
|
|
292
292
|
cy.contains('Entity Group 10').should('be.visible');
|
|
293
293
|
|
|
294
|
-
cy.findByTestId(`role-
|
|
294
|
+
cy.findByTestId(`role-Kathy Schmitt`).parent().click();
|
|
295
295
|
cy.get('li[data-value="editor"]').click();
|
|
296
296
|
|
|
297
|
-
cy.findByTestId('remove-
|
|
297
|
+
cy.findByTestId('remove-Linda Schultz').click();
|
|
298
298
|
|
|
299
299
|
cy.contains('1 added | 1 updated | 1 removed').should('be.visible');
|
|
300
300
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useAtom } from 'jotai';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
|
|
4
4
|
import { FormControlLabel, Radio, RadioGroup } from '@mui/material';
|
|
@@ -17,7 +17,7 @@ const ContactSwitch = ({ labels }: Props): JSX.Element => {
|
|
|
17
17
|
const { classes } = useContactSwitchStyles();
|
|
18
18
|
const { t } = useTranslation();
|
|
19
19
|
|
|
20
|
-
const setContactType =
|
|
20
|
+
const [contactType, setContactType] = useAtom(contactTypeAtom);
|
|
21
21
|
|
|
22
22
|
const change = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
|
23
23
|
setContactType(event.target.value as ContactType);
|
|
@@ -29,7 +29,7 @@ const ContactSwitch = ({ labels }: Props): JSX.Element => {
|
|
|
29
29
|
<RadioGroup
|
|
30
30
|
row
|
|
31
31
|
className={classes.inputs}
|
|
32
|
-
|
|
32
|
+
value={contactType}
|
|
33
33
|
onChange={change}
|
|
34
34
|
>
|
|
35
35
|
<FormControlLabel
|
|
@@ -52,6 +52,7 @@ const ShareInput = ({ labels, endpoints, roles }: Props): JSX.Element => {
|
|
|
52
52
|
? t(labels.autocompleteContactGroup)
|
|
53
53
|
: t(labels.autocompleteContact)
|
|
54
54
|
)}
|
|
55
|
+
queryKey={isContactGroup ? labels.contactGroup : labels.contact}
|
|
55
56
|
renderOption={renderOption}
|
|
56
57
|
value={selectedContact}
|
|
57
58
|
onChange={selectContact}
|