@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.
Files changed (137) hide show
  1. package/package.json +36 -29
  2. package/public/mockServiceWorker.js +1 -1
  3. package/src/Button/Icon/index.tsx +1 -1
  4. package/src/Button/Save/StartIcon.tsx +3 -3
  5. package/src/Button/Save/index.tsx +9 -5
  6. package/src/Checkbox/Checkbox.tsx +2 -2
  7. package/src/Checkbox/CheckboxGroup/index.tsx +2 -2
  8. package/src/Dashboard/Item.tsx +1 -1
  9. package/src/Dashboard/Layout.tsx +2 -2
  10. package/src/Dialog/Confirm/index.tsx +10 -2
  11. package/src/Dialog/index.tsx +9 -2
  12. package/src/FallbackPage/FallbackPage.tsx +3 -3
  13. package/src/FileDropZone/index.tsx +3 -1
  14. package/src/Form/Form.cypress.spec.tsx +133 -0
  15. package/src/Form/Inputs/List/Content.tsx +62 -0
  16. package/src/Form/Inputs/List/List.styles.ts +29 -0
  17. package/src/Form/Inputs/List/List.tsx +58 -0
  18. package/src/Form/Inputs/List/useList.ts +81 -0
  19. package/src/Form/Inputs/index.tsx +3 -1
  20. package/src/Form/Inputs/models.ts +9 -1
  21. package/src/Graph/BarStack/BarStack.cypress.spec.tsx +154 -0
  22. package/src/Graph/BarStack/BarStack.stories.tsx +123 -0
  23. package/src/Graph/BarStack/BarStack.styles.ts +36 -0
  24. package/src/Graph/BarStack/BarStack.tsx +14 -0
  25. package/src/Graph/BarStack/ResponsiveBarStack.tsx +208 -0
  26. package/src/Graph/BarStack/index.ts +1 -0
  27. package/src/Graph/BarStack/models.ts +19 -0
  28. package/src/Graph/BarStack/useResponsiveBarStack.ts +139 -0
  29. package/src/Graph/Gauge/Gauge.cypress.spec.tsx +102 -0
  30. package/src/Graph/Gauge/Gauge.tsx +1 -1
  31. package/src/Graph/HeatMap/HeatMap.cypress.spec.tsx +145 -0
  32. package/src/Graph/HeatMap/HeatMap.stories.tsx +0 -25
  33. package/src/Graph/HeatMap/ResponsiveHeatMap.tsx +8 -2
  34. package/src/Graph/Legend/Legend.tsx +21 -0
  35. package/src/Graph/Legend/index.ts +1 -0
  36. package/src/Graph/Legend/models.ts +11 -0
  37. package/src/Graph/LineChart/BasicComponents/Lines/Threshold/Circle.tsx +2 -2
  38. package/src/Graph/LineChart/BasicComponents/Thresholds.tsx +2 -2
  39. package/src/Graph/LineChart/BasicComponents/useFilterLines.ts +1 -1
  40. package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/GuidingLines.tsx +2 -2
  41. package/src/Graph/LineChart/InteractiveComponents/Annotations/EventAnnotations.tsx +1 -1
  42. package/src/Graph/LineChart/Legend/Legend.styles.ts +1 -1
  43. package/src/Graph/LineChart/Legend/LegendHeader.tsx +1 -1
  44. package/src/Graph/LineChart/Legend/useInteractiveValues.ts +2 -2
  45. package/src/Graph/LineChart/Legend/useLegend.ts +3 -3
  46. package/src/Graph/LineChart/helpers/doc.ts +16 -13
  47. package/src/Graph/LineChart/helpers/index.ts +1 -1
  48. package/src/Graph/LineChart/index.stories.tsx +4 -2
  49. package/src/Graph/LineChart/index.tsx +1 -1
  50. package/src/Graph/PieChart/PieChart.cypress.spec.tsx +169 -0
  51. package/src/Graph/PieChart/PieChart.stories.tsx +194 -0
  52. package/src/Graph/PieChart/PieChart.styles.ts +39 -0
  53. package/src/Graph/PieChart/PieChart.tsx +14 -0
  54. package/src/Graph/PieChart/ResponsivePie.tsx +251 -0
  55. package/src/Graph/PieChart/index.ts +1 -0
  56. package/src/Graph/PieChart/models.ts +19 -0
  57. package/src/Graph/PieChart/useResponsivePie.ts +86 -0
  58. package/src/Graph/SingleBar/SingleBar.cypress.spec.tsx +121 -0
  59. package/src/Graph/SingleBar/Thresholds.tsx +2 -2
  60. package/src/Graph/Text/Text.cypress.spec.tsx +101 -0
  61. package/src/Graph/Text/Text.stories.tsx +60 -4
  62. package/src/Graph/Text/Text.tsx +1 -1
  63. package/src/Graph/common/testUtils.ts +71 -0
  64. package/src/Graph/common/timeSeries/index.ts +22 -14
  65. package/src/Graph/common/utils.ts +19 -0
  66. package/src/Graph/index.ts +3 -0
  67. package/src/Graph/translatedLabels.ts +1 -0
  68. package/src/InputField/Select/Autocomplete/Connected/index.tsx +10 -7
  69. package/src/InputField/Select/Autocomplete/Draggable/SortableList.tsx +1 -1
  70. package/src/InputField/Select/Autocomplete/Draggable/SortableListContent.tsx +1 -1
  71. package/src/InputField/Select/Autocomplete/Draggable/index.tsx +1 -1
  72. package/src/InputField/Select/Autocomplete/index.tsx +121 -115
  73. package/src/InputField/Select/IconPopover/index.tsx +2 -2
  74. package/src/InputField/Select/index.tsx +1 -1
  75. package/src/InputField/Text/index.tsx +2 -2
  76. package/src/Listing/ActionBar/index.tsx +9 -8
  77. package/src/Listing/Cell/DataCell.styles.ts +3 -0
  78. package/src/Listing/Cell/DataCell.tsx +23 -5
  79. package/src/Listing/Header/ListingHeader.tsx +1 -1
  80. package/src/Listing/Listing.cypress.spec.tsx +80 -4
  81. package/src/Listing/Listing.styles.ts +4 -7
  82. package/src/Listing/index.stories.tsx +37 -3
  83. package/src/Listing/index.test.tsx +1 -1
  84. package/src/Listing/index.tsx +4 -3
  85. package/src/Listing/models.ts +1 -0
  86. package/src/Module/Module.cypress.spec.tsx +129 -0
  87. package/src/Module/index.tsx +2 -4
  88. package/src/RichTextEditor/RichTextEditor.tsx +12 -1
  89. package/src/SortableItems/index.tsx +2 -7
  90. package/src/ThemeProvider/index.tsx +24 -0
  91. package/src/TimePeriods/CustomTimePeriod/CompactCustomTimePeriod.styles.ts +6 -7
  92. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +8 -3
  93. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +0 -2
  94. package/src/TimePeriods/DateTimePickerInput.tsx +56 -19
  95. package/src/TimePeriods/ResolutionTimePeriod.cypress.spec.tsx +12 -9
  96. package/src/TimePeriods/TimePeriods.cypress.spec.tsx +9 -33
  97. package/src/TimePeriods/helpers/index.ts +1 -1
  98. package/src/TimePeriods/index.stories.tsx +12 -4
  99. package/src/TimePeriods/index.tsx +2 -2
  100. package/src/api/QueryProvider.tsx +1 -1
  101. package/src/api/TestQueryProvider.tsx +1 -1
  102. package/src/api/useFetchQuery/index.ts +27 -23
  103. package/src/api/useMutationQuery/index.test.ts +4 -4
  104. package/src/api/useMutationQuery/index.ts +60 -25
  105. package/src/components/Button/Icon/IconButton.tsx +6 -2
  106. package/src/components/DataTable/DataListing.tsx +6 -0
  107. package/src/components/DataTable/DataTable.cypress.spec.tsx +193 -0
  108. package/src/components/DataTable/DataTable.stories.tsx +40 -0
  109. package/src/components/DataTable/DataTable.styles.ts +3 -0
  110. package/src/components/DataTable/DataTable.tsx +3 -3
  111. package/src/components/DataTable/Item/DataTableItem.styles.ts +7 -2
  112. package/src/components/DataTable/Item/DataTableItem.tsx +4 -4
  113. package/src/components/DataTable/index.ts +3 -1
  114. package/src/components/Form/AccessRights/ShareInput/ContactSwitch.tsx +3 -3
  115. package/src/components/Form/AccessRights/ShareInput/ShareInput.tsx +1 -0
  116. package/src/components/Form/Dashboard/DashboardForm.tsx +15 -12
  117. package/src/components/Layout/PageLayout/PageLayout.tsx +1 -1
  118. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +1 -0
  119. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +1 -0
  120. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +5 -1
  121. package/src/components/Layout/PageLayout/PageQuickAccess.tsx +76 -0
  122. package/src/components/Layout/PageLayout/index.ts +3 -1
  123. package/src/components/Layout/PageLayout.cypress.spec.tsx +66 -0
  124. package/src/components/Modal/Modal.styles.ts +1 -1
  125. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.stories.tsx +3 -3
  126. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.tsx +1 -1
  127. package/src/components/Tooltip/ConfirmationTooltip/models.ts +1 -1
  128. package/src/index.ts +2 -2
  129. package/src/queryParameters/url/index.ts +5 -1
  130. package/src/utils/index.ts +1 -1
  131. package/src/utils/useFullscreen/useFullscreenListener.ts +10 -7
  132. package/src/utils/{useLicenseExpirationWarning.cypress.spec.tsx → useLicenseExpirationWarning.test.tsx} +48 -37
  133. package/src/utils/useLicenseExpirationWarning.ts +18 -18
  134. package/src/utils/usePluralizedTranslation.ts +21 -0
  135. package/src/screens/dashboard/DashboardsDetail.stories.tsx +0 -108
  136. package/src/screens/dashboard/DashboardsOverview.stories.tsx +0 -281
  137. package/src/utils/useDateTimePickerAdapter.ts +0 -309
@@ -1,48 +1,85 @@
1
- import dayjs from 'dayjs';
1
+ import { useCallback } from 'react';
2
2
 
3
- import { DateTimePicker } from '@mui/x-date-pickers';
3
+ import dayjs, { Dayjs } from 'dayjs';
4
+ import { useAtomValue } from 'jotai';
5
+ import { equals } from 'ramda';
4
6
 
5
- import { useDateTimePickerAdapter } from '@centreon/ui';
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
- property: CustomTimePeriodProperty;
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
- }: Props): JSX.Element => {
28
- const { desktopPickerMediaQuery } = useDateTimePickerAdapter();
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
- <DateTimePicker<dayjs.Dayjs>
36
- dayOfWeekFormatter={(day: string): string =>
37
- day.substring(0, 2).toUpperCase()
38
- }
39
- desktopModeMediaQuery={desktopMediaQuery ?? desktopPickerMediaQuery}
40
- disabled={disabled}
41
- maxDate={maxDate && dayjs(maxDate)}
42
- minDate={minDate && dayjs(minDate)}
43
- value={dayjs(date)}
44
- onChange={changeTime}
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/fr';
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: 'fr_FR',
23
- resolution: { height: 720, width: 500 },
22
+ locale: 'en_US',
23
+ resolution: { height: 590, width: 500 },
24
24
  timezone: 'Europe/Paris'
25
25
  },
26
26
  {
27
- locale: 'fr_FR',
28
- resolution: { height: 720, width: 200 },
27
+ locale: 'en_US',
28
+ resolution: { height: 590, width: 200 },
29
29
  timezone: 'Europe/Paris'
30
30
  },
31
31
  {
32
- locale: 'fr_FR',
33
- resolution: { height: 720, width: 1024 },
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: 'fr_FR',
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.makeSnapshot(`${width}px`);
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, inc } from 'ramda';
6
+ import { equals } from 'ramda';
7
7
  import { renderHook } from '@testing-library/react';
8
8
 
9
- import { LocalizationProvider } from '@mui/x-date-pickers';
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
- <LocalizationProvider adapterLocale="en" dateAdapter={Adapter}>
315
- <DateTimePickerInput
316
- changeDate={cy.stub()}
317
- date={new Date(item.initialDate)}
318
- desktopMediaQuery="@media (min-width: 1024px)"
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', inc(numberWeeks));
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', inc(numberWeeks));
395
+ cy.get('@listWeeks').should('have.length', numberWeeks);
420
396
 
421
397
  cy.get('@listWeeks')
422
398
  .eq(numberWeeks - 1)
@@ -23,4 +23,4 @@ export const getTimePeriodById = ({
23
23
  id,
24
24
  timePeriods
25
25
  }: TimePeriodById): TimePeriod =>
26
- find<TimePeriod>(propEq('id', id))(timePeriods) as TimePeriod;
26
+ find<TimePeriod>(propEq(id, 'id'))(timePeriods) as TimePeriod;
@@ -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<TimePeriod, 'timelineEventsLimit'>>;
37
+ extraTimePeriods?: Array<Omit<TimePeriodModel, 'timelineEventsLimit'>>;
38
38
  getIsError?: (value: boolean) => void;
39
39
  getParameters?: ({ start, end, timelineEventsLimit }: Parameters) => void;
40
40
  renderExternalComponent?: ReactNode;
@@ -7,7 +7,7 @@ const defaultCacheTime = 5 * 1_000;
7
7
  const client = new QueryClient({
8
8
  defaultOptions: {
9
9
  queries: {
10
- cacheTime: defaultCacheTime,
10
+ gcTime: defaultCacheTime,
11
11
  refetchOnWindowFocus: false,
12
12
  staleTime: defaultCacheTime,
13
13
  suspense: true
@@ -5,7 +5,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
5
5
  const client = new QueryClient({
6
6
  defaultOptions: {
7
7
  queries: {
8
- cacheTime: 0,
8
+ gcTime: 0,
9
9
  retry: false
10
10
  }
11
11
  }
@@ -27,7 +27,9 @@ export interface UseFetchQueryProps<T> {
27
27
  getQueryKey: () => QueryKey;
28
28
  httpCodesBypassErrorSnackbar?: Array<number>;
29
29
  isPaginated?: boolean;
30
- queryOptions?: Omit<
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
- getQueryKey(),
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
- ...queryOptions
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
- queryKey,
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
- getQueryKey(),
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
- manageError();
183
- }, useDeepCompare([queryData.data]));
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
- } & Omit<UseMutationOptions<T & { _meta?: TMeta }>, 'mutationFn'>;
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
- } & UseMutationResult<T | ResponseError>;
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
- T & { _meta: TMeta }
62
- >(
63
- (_payload: T & { _meta: TMeta }): Promise<T | ResponseError> => {
64
- const { _meta, ...payload } = _payload || {};
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
- onError,
83
- onMutate,
84
- onSuccess: (data, variables, context) => {
85
- if (data?.isError) {
86
- onError?.(data, variables, context);
87
-
88
- return;
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
- manageError();
112
- }, useDeepCompare([queryData.data]));
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.isLoading
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 { IconButton as MuiIconButton } from '@mui/material';
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,6 @@
1
+ import { RowId } from '../../Listing/models';
2
+ import { Listing, ListingProps } from '../..';
3
+
4
+ export const DataListing = <TRow extends { id: RowId }>(
5
+ props: ListingProps<TRow>
6
+ ): JSX.Element => <Listing<TRow> {...props} />;