@centreon/ui 24.4.48 → 24.4.49
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 +36 -29
- package/public/mockServiceWorker.js +1 -1
- 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/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/index.tsx +3 -1
- package/src/Form/Inputs/models.ts +9 -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 +36 -0
- package/src/Graph/BarStack/BarStack.tsx +14 -0
- package/src/Graph/BarStack/ResponsiveBarStack.tsx +208 -0
- package/src/Graph/BarStack/index.ts +1 -0
- package/src/Graph/BarStack/models.ts +19 -0
- package/src/Graph/BarStack/useResponsiveBarStack.ts +139 -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 +251 -0
- package/src/Graph/PieChart/index.ts +1 -0
- package/src/Graph/PieChart/models.ts +19 -0
- package/src/Graph/PieChart/useResponsivePie.ts +86 -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/index.tsx +121 -115
- package/src/InputField/Select/IconPopover/index.tsx +2 -2
- package/src/InputField/Select/index.tsx +1 -1
- 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 +80 -4
- package/src/Listing/Listing.styles.ts +4 -7
- package/src/Listing/index.stories.tsx +37 -3
- package/src/Listing/index.test.tsx +1 -1
- package/src/Listing/index.tsx +4 -3
- package/src/Listing/models.ts +1 -0
- package/src/Module/Module.cypress.spec.tsx +129 -0
- package/src/Module/index.tsx +2 -4
- 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/api/QueryProvider.tsx +1 -1
- package/src/api/TestQueryProvider.tsx +1 -1
- 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 +193 -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 -4
- package/src/components/DataTable/index.ts +3 -1
- package/src/components/Form/AccessRights/ShareInput/ContactSwitch.tsx +3 -3
- package/src/components/Form/AccessRights/ShareInput/ShareInput.tsx +1 -0
- package/src/components/Form/Dashboard/DashboardForm.tsx +15 -12
- 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/Modal/Modal.styles.ts +1 -1
- 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/useFullscreenListener.ts +10 -7
- 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/screens/dashboard/DashboardsDetail.stories.tsx +0 -108
- package/src/screens/dashboard/DashboardsOverview.stories.tsx +0 -281
- package/src/utils/useDateTimePickerAdapter.ts +0 -309
|
@@ -1,48 +1,85 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import dayjs, { Dayjs } from 'dayjs';
|
|
4
|
+
import { useAtomValue } from 'jotai';
|
|
5
|
+
import { equals } from 'ramda';
|
|
4
6
|
|
|
5
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
DateTimePicker,
|
|
9
|
+
DateTimePickerProps,
|
|
10
|
+
LocalizationProvider
|
|
11
|
+
} from '@mui/x-date-pickers';
|
|
12
|
+
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
|
|
13
|
+
|
|
14
|
+
import { userAtom } from '@centreon/ui-context';
|
|
6
15
|
|
|
7
16
|
import { CustomTimePeriodProperty } from './models';
|
|
8
17
|
|
|
18
|
+
interface ChangeDateProps {
|
|
19
|
+
date: Date;
|
|
20
|
+
property: CustomTimePeriodProperty | string;
|
|
21
|
+
}
|
|
22
|
+
|
|
9
23
|
interface Props {
|
|
10
|
-
changeDate: (props) => void;
|
|
24
|
+
changeDate: (props: ChangeDateProps) => void;
|
|
11
25
|
date: Date | null;
|
|
12
26
|
desktopMediaQuery?: string;
|
|
13
27
|
disabled?: boolean;
|
|
14
28
|
maxDate?: Date;
|
|
15
29
|
minDate?: Date;
|
|
16
|
-
|
|
30
|
+
minDateTime?: Date;
|
|
31
|
+
property: CustomTimePeriodProperty | string;
|
|
17
32
|
}
|
|
18
33
|
|
|
19
34
|
const DateTimePickerInput = ({
|
|
20
35
|
date,
|
|
21
36
|
maxDate,
|
|
22
37
|
minDate,
|
|
38
|
+
minDateTime,
|
|
23
39
|
property,
|
|
24
40
|
changeDate,
|
|
25
41
|
disabled = false,
|
|
26
|
-
desktopMediaQuery
|
|
27
|
-
|
|
28
|
-
|
|
42
|
+
desktopMediaQuery,
|
|
43
|
+
...rest
|
|
44
|
+
}: Props & DateTimePickerProps<dayjs.Dayjs>): JSX.Element => {
|
|
45
|
+
const desktopPickerMediaQuery =
|
|
46
|
+
'@media (min-width: 1024px) or (pointer: fine)';
|
|
47
|
+
|
|
48
|
+
const { timezone, locale } = useAtomValue(userAtom);
|
|
49
|
+
|
|
50
|
+
const isUTC = equals(timezone, 'UTC');
|
|
29
51
|
|
|
30
52
|
const changeTime = (newValue: dayjs.Dayjs | null): void => {
|
|
31
53
|
changeDate({ date: dayjs(newValue).toDate(), property });
|
|
32
54
|
};
|
|
33
55
|
|
|
56
|
+
const formatDate = useCallback(
|
|
57
|
+
(currentDate: Date | null): Dayjs => {
|
|
58
|
+
return isUTC ? dayjs.utc(currentDate) : dayjs.tz(currentDate, timezone);
|
|
59
|
+
},
|
|
60
|
+
[isUTC, timezone]
|
|
61
|
+
);
|
|
62
|
+
|
|
34
63
|
return (
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
64
|
+
<LocalizationProvider
|
|
65
|
+
adapterLocale={locale.substring(0, 2)}
|
|
66
|
+
dateAdapter={AdapterDayjs}
|
|
67
|
+
dateLibInstance={dayjs}
|
|
68
|
+
>
|
|
69
|
+
<DateTimePicker<dayjs.Dayjs>
|
|
70
|
+
dayOfWeekFormatter={(dayOfWeek) =>
|
|
71
|
+
dayOfWeek.substring(0, 2).toLocaleUpperCase()
|
|
72
|
+
}
|
|
73
|
+
desktopModeMediaQuery={desktopMediaQuery ?? desktopPickerMediaQuery}
|
|
74
|
+
disabled={disabled}
|
|
75
|
+
maxDate={maxDate && formatDate(maxDate)}
|
|
76
|
+
minDate={minDate && formatDate(minDate)}
|
|
77
|
+
minDateTime={minDateTime && formatDate(minDateTime)}
|
|
78
|
+
value={formatDate(date)}
|
|
79
|
+
onChange={changeTime}
|
|
80
|
+
{...rest}
|
|
81
|
+
/>
|
|
82
|
+
</LocalizationProvider>
|
|
46
83
|
);
|
|
47
84
|
};
|
|
48
85
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
|
-
import 'dayjs/locale/
|
|
2
|
+
import 'dayjs/locale/en';
|
|
3
3
|
import localizedFormatPlugin from 'dayjs/plugin/localizedFormat';
|
|
4
4
|
import timezonePlugin from 'dayjs/plugin/timezone';
|
|
5
5
|
import utcPlugin from 'dayjs/plugin/utc';
|
|
@@ -19,18 +19,18 @@ dayjs.extend(localizedFormatPlugin);
|
|
|
19
19
|
|
|
20
20
|
const data = [
|
|
21
21
|
{
|
|
22
|
-
locale: '
|
|
23
|
-
resolution: { height:
|
|
22
|
+
locale: 'en_US',
|
|
23
|
+
resolution: { height: 590, width: 500 },
|
|
24
24
|
timezone: 'Europe/Paris'
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
|
-
locale: '
|
|
28
|
-
resolution: { height:
|
|
27
|
+
locale: 'en_US',
|
|
28
|
+
resolution: { height: 590, width: 200 },
|
|
29
29
|
timezone: 'Europe/Paris'
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
|
-
locale: '
|
|
33
|
-
resolution: { height:
|
|
32
|
+
locale: 'en_US',
|
|
33
|
+
resolution: { height: 590, width: 1024 },
|
|
34
34
|
timezone: 'Europe/Paris'
|
|
35
35
|
}
|
|
36
36
|
];
|
|
@@ -45,7 +45,7 @@ data.forEach((item) =>
|
|
|
45
45
|
|
|
46
46
|
store.set(userAtom, {
|
|
47
47
|
...retrievedUser,
|
|
48
|
-
locale: '
|
|
48
|
+
locale: 'en_US',
|
|
49
49
|
timezone: 'Europe/Paris'
|
|
50
50
|
});
|
|
51
51
|
|
|
@@ -59,7 +59,10 @@ data.forEach((item) =>
|
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
it(`displays correctly the dates design when screen resolution is ${width}px`, () => {
|
|
62
|
-
cy.
|
|
62
|
+
cy.makeSnapshotWithCustomResolution({
|
|
63
|
+
resolution: { height, width },
|
|
64
|
+
title: `${width}px`
|
|
65
|
+
});
|
|
63
66
|
});
|
|
64
67
|
})
|
|
65
68
|
);
|
|
@@ -3,15 +3,10 @@ import localizedFormatPlugin from 'dayjs/plugin/localizedFormat';
|
|
|
3
3
|
import timezonePlugin from 'dayjs/plugin/timezone';
|
|
4
4
|
import utcPlugin from 'dayjs/plugin/utc';
|
|
5
5
|
import { Provider, createStore } from 'jotai';
|
|
6
|
-
import { equals
|
|
6
|
+
import { equals } from 'ramda';
|
|
7
7
|
import { renderHook } from '@testing-library/react';
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
useLocaleDateTimeFormat,
|
|
13
|
-
useDateTimePickerAdapter
|
|
14
|
-
} from '@centreon/ui';
|
|
9
|
+
import { useLocaleDateTimeFormat } from '@centreon/ui';
|
|
15
10
|
import { ListingVariant, userAtom } from '@centreon/ui-context';
|
|
16
11
|
|
|
17
12
|
import { CustomTimePeriodProperty } from './models';
|
|
@@ -297,10 +292,6 @@ testData.forEach((item) =>
|
|
|
297
292
|
beforeEach(() => {
|
|
298
293
|
cy.viewport('macbook-13');
|
|
299
294
|
|
|
300
|
-
const { result } = renderHook(() => useDateTimePickerAdapter());
|
|
301
|
-
|
|
302
|
-
const { Adapter } = result.current;
|
|
303
|
-
|
|
304
295
|
const store = createStore();
|
|
305
296
|
store.set(userAtom, {
|
|
306
297
|
...retrievedUser,
|
|
@@ -311,31 +302,16 @@ testData.forEach((item) =>
|
|
|
311
302
|
cy.mount({
|
|
312
303
|
Component: (
|
|
313
304
|
<Provider store={store}>
|
|
314
|
-
<
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
property={CustomTimePeriodProperty.start}
|
|
320
|
-
/>
|
|
321
|
-
</LocalizationProvider>
|
|
305
|
+
<DateTimePickerInput
|
|
306
|
+
changeDate={cy.stub()}
|
|
307
|
+
date={new Date(item.initialDate)}
|
|
308
|
+
property={CustomTimePeriodProperty.start}
|
|
309
|
+
/>
|
|
322
310
|
</Provider>
|
|
323
311
|
)
|
|
324
312
|
});
|
|
325
313
|
});
|
|
326
314
|
|
|
327
|
-
it('checks input calendar value contains correct date', () => {
|
|
328
|
-
const { result } = renderHook(() => useLocaleDateTimeFormat());
|
|
329
|
-
const { format } = result.current;
|
|
330
|
-
|
|
331
|
-
const dateInput = format({
|
|
332
|
-
date: dayjs(item.initialDate).tz(item.timezone),
|
|
333
|
-
formatString: 'L hh:mm A'
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
cy.get('input').should('have.value', dateInput);
|
|
337
|
-
});
|
|
338
|
-
|
|
339
315
|
it(`displays the correct number of days for the current month when the ${item.button} button is clicked`, () => {
|
|
340
316
|
cy.get('button').click();
|
|
341
317
|
item.data.forEach((element) => {
|
|
@@ -389,7 +365,7 @@ testData.forEach((item) =>
|
|
|
389
365
|
|
|
390
366
|
cy.get('[role="rowgroup"]').children().as('listWeeks');
|
|
391
367
|
|
|
392
|
-
cy.get('@listWeeks').should('have.length',
|
|
368
|
+
cy.get('@listWeeks').should('have.length', numberWeeks);
|
|
393
369
|
cy.get('@listWeeks').eq(0).as('firstWeek');
|
|
394
370
|
cy.get('@firstWeek').children().as('listDaysInFirstWeek');
|
|
395
371
|
|
|
@@ -416,7 +392,7 @@ testData.forEach((item) =>
|
|
|
416
392
|
|
|
417
393
|
cy.get('[role="rowgroup"]').children().as('listWeeks');
|
|
418
394
|
|
|
419
|
-
cy.get('@listWeeks').should('have.length',
|
|
395
|
+
cy.get('@listWeeks').should('have.length', numberWeeks);
|
|
420
396
|
|
|
421
397
|
cy.get('@listWeeks')
|
|
422
398
|
.eq(numberWeeks - 1)
|
|
@@ -88,20 +88,27 @@ const args = {
|
|
|
88
88
|
]
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
+
const parameters = {
|
|
92
|
+
chromatic: { diffThreshold: 0.1 }
|
|
93
|
+
};
|
|
94
|
+
|
|
91
95
|
export const BasicTimePeriod: Story = {
|
|
92
96
|
...Template,
|
|
93
|
-
argTypes
|
|
97
|
+
argTypes,
|
|
98
|
+
parameters
|
|
94
99
|
};
|
|
95
100
|
|
|
96
101
|
export const WithExtraTimePeriods: Story = {
|
|
97
102
|
...Template,
|
|
98
103
|
argTypes,
|
|
99
|
-
args
|
|
104
|
+
args,
|
|
105
|
+
parameters
|
|
100
106
|
};
|
|
101
107
|
|
|
102
108
|
export const WithExternalComponent: Story = {
|
|
103
109
|
...TemplateWithExternalComponent,
|
|
104
|
-
argTypes
|
|
110
|
+
argTypes,
|
|
111
|
+
parameters
|
|
105
112
|
};
|
|
106
113
|
|
|
107
114
|
export const SimpleTimePeriod: StorySimpleTimePeriod = {
|
|
@@ -109,5 +116,6 @@ export const SimpleTimePeriod: StorySimpleTimePeriod = {
|
|
|
109
116
|
args: {
|
|
110
117
|
endDate: dayjs(Date.now()).toDate(),
|
|
111
118
|
startDate: dayjs(Date.now()).subtract(29, 'day').toDate()
|
|
112
|
-
}
|
|
119
|
+
},
|
|
120
|
+
parameters
|
|
113
121
|
};
|
|
@@ -20,7 +20,7 @@ import { useStyles } from './TimePeriods.styles';
|
|
|
20
20
|
import {
|
|
21
21
|
CustomTimePeriod as CustomTimePeriodModel,
|
|
22
22
|
EndStartInterval,
|
|
23
|
-
TimePeriod
|
|
23
|
+
TimePeriod as TimePeriodModel
|
|
24
24
|
} from './models';
|
|
25
25
|
import useTimePeriod from './useTimePeriod';
|
|
26
26
|
|
|
@@ -34,7 +34,7 @@ interface Parameters extends EndStartInterval {
|
|
|
34
34
|
export interface Props {
|
|
35
35
|
adjustTimePeriodData?: Omit<CustomTimePeriodModel, 'timelineEventsLimit'>;
|
|
36
36
|
disabled?: boolean;
|
|
37
|
-
extraTimePeriods?: Array<Omit<
|
|
37
|
+
extraTimePeriods?: Array<Omit<TimePeriodModel, 'timelineEventsLimit'>>;
|
|
38
38
|
getIsError?: (value: boolean) => void;
|
|
39
39
|
getParameters?: ({ start, end, timelineEventsLimit }: Parameters) => void;
|
|
40
40
|
renderExternalComponent?: ReactNode;
|
|
@@ -27,7 +27,9 @@ export interface UseFetchQueryProps<T> {
|
|
|
27
27
|
getQueryKey: () => QueryKey;
|
|
28
28
|
httpCodesBypassErrorSnackbar?: Array<number>;
|
|
29
29
|
isPaginated?: boolean;
|
|
30
|
-
queryOptions?:
|
|
30
|
+
queryOptions?: {
|
|
31
|
+
suspense?: boolean;
|
|
32
|
+
} & Omit<
|
|
31
33
|
UseQueryOptions<T | ResponseError, Error, T | ResponseError, QueryKey>,
|
|
32
34
|
'queryKey' | 'queryFn'
|
|
33
35
|
>;
|
|
@@ -35,11 +37,12 @@ export interface UseFetchQueryProps<T> {
|
|
|
35
37
|
|
|
36
38
|
export type UseFetchQueryState<T> = {
|
|
37
39
|
data?: T;
|
|
40
|
+
error: Omit<ResponseError, 'isError'> | null;
|
|
38
41
|
fetchQuery: () => Promise<T | ResponseError>;
|
|
39
42
|
prefetchNextPage: ({ page, getPrefetchQueryKey }) => void;
|
|
40
43
|
prefetchPreviousPage: ({ page, getPrefetchQueryKey }) => void;
|
|
41
44
|
prefetchQuery: ({ endpointParams, queryKey }) => void;
|
|
42
|
-
} & Omit<QueryObserverBaseResult, 'data'>;
|
|
45
|
+
} & Omit<QueryObserverBaseResult, 'data' | 'error'>;
|
|
43
46
|
|
|
44
47
|
export interface PrefetchEndpointParams {
|
|
45
48
|
page: number;
|
|
@@ -64,9 +67,8 @@ const useFetchQuery = <T extends object>({
|
|
|
64
67
|
|
|
65
68
|
const { showErrorMessage } = useSnackbar();
|
|
66
69
|
|
|
67
|
-
const queryData = useQuery<T | ResponseError, Error>(
|
|
68
|
-
|
|
69
|
-
({ signal }): Promise<T | ResponseError> =>
|
|
70
|
+
const queryData = useQuery<T | ResponseError, Error>({
|
|
71
|
+
queryFn: ({ signal }): Promise<T | ResponseError> =>
|
|
70
72
|
customFetch<T>({
|
|
71
73
|
baseEndpoint,
|
|
72
74
|
catchError,
|
|
@@ -76,10 +78,9 @@ const useFetchQuery = <T extends object>({
|
|
|
76
78
|
headers: new Headers(fetchHeaders),
|
|
77
79
|
signal
|
|
78
80
|
}),
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
81
|
+
queryKey: getQueryKey(),
|
|
82
|
+
...queryOptions
|
|
83
|
+
});
|
|
83
84
|
|
|
84
85
|
const queryClient = useQueryClient();
|
|
85
86
|
|
|
@@ -99,9 +100,8 @@ const useFetchQuery = <T extends object>({
|
|
|
99
100
|
};
|
|
100
101
|
|
|
101
102
|
const prefetchQuery = ({ endpointParams, queryKey }): void => {
|
|
102
|
-
queryClient.prefetchQuery(
|
|
103
|
-
|
|
104
|
-
({ signal }): Promise<T | ResponseError> =>
|
|
103
|
+
queryClient.prefetchQuery({
|
|
104
|
+
queryFn: ({ signal }): Promise<T | ResponseError> =>
|
|
105
105
|
customFetch<T>({
|
|
106
106
|
baseEndpoint,
|
|
107
107
|
catchError,
|
|
@@ -110,8 +110,9 @@ const useFetchQuery = <T extends object>({
|
|
|
110
110
|
endpoint: getEndpoint(endpointParams),
|
|
111
111
|
headers: new Headers(fetchHeaders),
|
|
112
112
|
signal
|
|
113
|
-
})
|
|
114
|
-
|
|
113
|
+
}),
|
|
114
|
+
queryKey
|
|
115
|
+
});
|
|
115
116
|
};
|
|
116
117
|
|
|
117
118
|
const prefetchNextPage = ({ page, getPrefetchQueryKey }): void => {
|
|
@@ -141,9 +142,8 @@ const useFetchQuery = <T extends object>({
|
|
|
141
142
|
};
|
|
142
143
|
|
|
143
144
|
const fetchQuery = (): Promise<T | ResponseError> => {
|
|
144
|
-
return queryClient.fetchQuery(
|
|
145
|
-
|
|
146
|
-
({ signal }): Promise<T | ResponseError> =>
|
|
145
|
+
return queryClient.fetchQuery({
|
|
146
|
+
queryFn: ({ signal }): Promise<T | ResponseError> =>
|
|
147
147
|
customFetch<T>({
|
|
148
148
|
baseEndpoint,
|
|
149
149
|
catchError,
|
|
@@ -152,8 +152,9 @@ const useFetchQuery = <T extends object>({
|
|
|
152
152
|
endpoint: getEndpoint(),
|
|
153
153
|
headers: new Headers(fetchHeaders),
|
|
154
154
|
signal
|
|
155
|
-
})
|
|
156
|
-
|
|
155
|
+
}),
|
|
156
|
+
queryKey: getQueryKey()
|
|
157
|
+
});
|
|
157
158
|
};
|
|
158
159
|
|
|
159
160
|
const data = useMemo(
|
|
@@ -174,13 +175,16 @@ const useFetchQuery = <T extends object>({
|
|
|
174
175
|
return;
|
|
175
176
|
}
|
|
176
177
|
|
|
177
|
-
queryClient.cancelQueries(getQueryKey());
|
|
178
|
+
queryClient.cancelQueries({ queryKey: getQueryKey() });
|
|
178
179
|
};
|
|
179
180
|
}, []);
|
|
180
181
|
|
|
181
|
-
useEffect(
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
useEffect(
|
|
183
|
+
() => {
|
|
184
|
+
manageError();
|
|
185
|
+
},
|
|
186
|
+
useDeepCompare([queryData.data])
|
|
187
|
+
);
|
|
184
188
|
|
|
185
189
|
return {
|
|
186
190
|
...omit(['data', 'error'], queryData),
|
|
@@ -48,7 +48,7 @@ describe('useFetchQuery', () => {
|
|
|
48
48
|
method: Method.POST
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
result.current.mutate(user);
|
|
51
|
+
result.current.mutate({ payload: user });
|
|
52
52
|
|
|
53
53
|
await waitFor(() => {
|
|
54
54
|
expect(result.current?.isError).toEqual(false);
|
|
@@ -64,7 +64,7 @@ describe('useFetchQuery', () => {
|
|
|
64
64
|
method: Method.POST
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
result.current.mutate(user);
|
|
67
|
+
result.current.mutate({ payload: user });
|
|
68
68
|
|
|
69
69
|
await waitFor(() => {
|
|
70
70
|
expect(result.current?.isError).toEqual(true);
|
|
@@ -85,7 +85,7 @@ describe('useFetchQuery', () => {
|
|
|
85
85
|
method: Method.POST
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
result.current.mutate(user);
|
|
88
|
+
result.current.mutate({ payload: user });
|
|
89
89
|
|
|
90
90
|
await waitFor(() => {
|
|
91
91
|
expect(result.current?.isError).toEqual(true);
|
|
@@ -109,7 +109,7 @@ describe('useFetchQuery', () => {
|
|
|
109
109
|
method: Method.POST
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
result.current.mutate(user);
|
|
112
|
+
result.current.mutate({ payload: user });
|
|
113
113
|
|
|
114
114
|
await waitFor(() => {
|
|
115
115
|
expect(mockedShowErrorMessage).not.toHaveBeenCalled();
|
|
@@ -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)
|