@centreon/ui 23.10.28 → 23.10.30-MON-19324-dashboard-section-navigation.0

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 (82) hide show
  1. package/package.json +12 -2
  2. package/src/Dashboard/DashboardLayout.stories.tsx +3 -5
  3. package/src/Dashboard/Layout.tsx +4 -4
  4. package/src/Dashboard/utils.ts +28 -28
  5. package/src/ThemeProvider/index.tsx +6 -5
  6. package/src/ThemeProvider/palettes.ts +385 -208
  7. package/src/api/TestQueryProvider.tsx +2 -1
  8. package/src/api/useFetchQuery/index.ts +8 -7
  9. package/src/api/useMutationQuery/index.ts +11 -10
  10. package/src/base/tokens/README.md +3 -0
  11. package/src/base/tokens/source/$metadata.json +5 -0
  12. package/src/base/tokens/source/base.json +142 -0
  13. package/src/base/tokens/themes/base.tokens.d.ts +38 -0
  14. package/src/base/tokens/themes/base.tokens.json +695 -0
  15. package/src/base/tokens/themes/base.tokens.ts +38 -0
  16. package/src/base/tokens/themes/ui-dark.tokens.d.ts +38 -0
  17. package/src/base/tokens/themes/ui-dark.tokens.json +695 -0
  18. package/src/base/tokens/themes/ui-dark.tokens.ts +38 -0
  19. package/src/base/tokens/themes/ui-light.tokens.d.ts +38 -0
  20. package/src/base/tokens/themes/ui-light.tokens.json +695 -0
  21. package/src/base/tokens/themes/ui-light.tokens.ts +38 -0
  22. package/src/components/Button/Button.styles.ts +17 -3
  23. package/src/components/Button/Button.tsx +6 -3
  24. package/src/components/Button/Icon/IconButton.styles.ts +9 -2
  25. package/src/components/DataTable/Item/DataTableItem.styles.ts +5 -2
  26. package/src/components/DataTable/Item/DataTableItem.tsx +36 -18
  27. package/src/components/Header/Header.styles.ts +0 -27
  28. package/src/components/Header/PageHeader/PageHeader.stories.tsx +88 -0
  29. package/src/components/Header/PageHeader/PageHeader.styles.ts +99 -0
  30. package/src/components/Header/PageHeader/PageHeader.tsx +15 -0
  31. package/src/components/Header/PageHeader/PageHeaderActions.tsx +17 -0
  32. package/src/components/Header/PageHeader/PageHeaderMain.tsx +15 -0
  33. package/src/components/Header/PageHeader/PageHeaderMenu.tsx +15 -0
  34. package/src/components/Header/PageHeader/PageHeaderTitle.tsx +44 -0
  35. package/src/components/Header/PageHeader/index.ts +12 -0
  36. package/src/components/Header/index.ts +1 -1
  37. package/src/components/Layout/AreaIndicator.styles.ts +33 -0
  38. package/src/components/Layout/AreaIndicator.tsx +35 -0
  39. package/src/components/Layout/PageLayout/PageLayout.stories.tsx +81 -0
  40. package/src/components/Layout/PageLayout/PageLayout.styles.ts +37 -0
  41. package/src/components/Layout/PageLayout/PageLayout.tsx +21 -0
  42. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +15 -0
  43. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +24 -0
  44. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +15 -0
  45. package/src/components/Layout/PageLayout/index.ts +10 -0
  46. package/src/components/Layout/index.ts +1 -0
  47. package/src/components/Menu/Button/MenuButton.stories.tsx +16 -0
  48. package/src/components/Menu/Button/MenuButton.styles.ts +27 -0
  49. package/src/components/Menu/Button/MenuButton.tsx +59 -0
  50. package/src/components/Menu/Button/index.ts +1 -0
  51. package/src/components/Menu/Menu.stories.tsx +71 -0
  52. package/src/components/Menu/Menu.styles.ts +70 -0
  53. package/src/components/Menu/Menu.tsx +25 -0
  54. package/src/components/Menu/MenuDivider.tsx +13 -0
  55. package/src/components/Menu/MenuItem.tsx +36 -0
  56. package/src/components/Menu/MenuItems.tsx +35 -0
  57. package/src/components/Menu/index.ts +12 -0
  58. package/src/components/Menu/useMenu.tsx +79 -0
  59. package/src/components/Modal/index.ts +2 -0
  60. package/src/components/Tooltip/TextOverflowTooltip/TextOverflowTooltip.stories.tsx +71 -0
  61. package/src/components/Tooltip/TextOverflowTooltip/TextOverflowTooltip.styles.ts +5 -0
  62. package/src/components/Tooltip/TextOverflowTooltip/TextOverflowTooltip.tsx +115 -0
  63. package/src/components/Tooltip/TextOverflowTooltip/index.tsx +1 -0
  64. package/src/components/Tooltip/Tooltip.stories.tsx +41 -0
  65. package/src/components/Tooltip/Tooltip.styles.ts +5 -0
  66. package/src/components/Tooltip/Tooltip.tsx +46 -0
  67. package/src/components/Tooltip/index.ts +1 -0
  68. package/src/components/index.ts +3 -0
  69. package/src/index.ts +0 -2
  70. package/src/screens/dashboard/DashboardsDetail.stories.tsx +108 -0
  71. package/src/screens/dashboard/DashboardsOverview.stories.tsx +110 -102
  72. package/src/utils/useIntersectionObserver.ts +4 -2
  73. package/src/utils/useResizeObserver.ts +6 -0
  74. package/test/setupTests.js +3 -0
  75. package/src/components/Header/Header.stories.tsx +0 -27
  76. package/src/components/Header/Header.tsx +0 -23
  77. package/src/layout/TiledListingPage/TiledListingActions.tsx +0 -15
  78. package/src/layout/TiledListingPage/TiledListingContent.tsx +0 -21
  79. package/src/layout/TiledListingPage/TiledListingList.tsx +0 -11
  80. package/src/layout/TiledListingPage/TiledListingPage.styles.ts +0 -16
  81. package/src/layout/TiledListingPage/TiledListingPage.tsx +0 -15
  82. package/src/layout/TiledListingPage/index.ts +0 -4
@@ -0,0 +1,71 @@
1
+ /* eslint-disable react-hooks/rules-of-hooks */
2
+ import { useEffect, useState } from 'react';
3
+
4
+ import { Meta, StoryObj } from '@storybook/react';
5
+
6
+ // import { expect } from '@storybook/jest';
7
+ import { TextOverflowTooltip } from './TextOverflowTooltip';
8
+
9
+ const meta: Meta<typeof TextOverflowTooltip> = {
10
+ args: {},
11
+ component: TextOverflowTooltip,
12
+ parameters: {
13
+ layout: 'centered'
14
+ }
15
+ };
16
+
17
+ export default meta;
18
+ type Story = StoryObj<typeof TextOverflowTooltip>;
19
+
20
+ export const Default: Story = {
21
+ args: {
22
+ label:
23
+ 'Qui deserunt pariatur quis. Duis nisi velit culpa labore ipsum reprehenderit sunt laborum anim sint quis magna consequat amet. Voluptate tempor nostrud eiusmod enim qui reprehenderit.'
24
+ },
25
+ render: ({ label, ...args }) => {
26
+ const [labelValue, setLabelValue] = useState(label);
27
+ useEffect(() => setLabelValue(label), [label]);
28
+
29
+ return (
30
+ <TextOverflowTooltip {...args} data-testid="tooltip" label={labelValue}>
31
+ <p
32
+ data-testid="paragraph"
33
+ style={{
34
+ overflow: 'hidden',
35
+ textOverflow: 'ellipsis',
36
+ whiteSpace: 'nowrap',
37
+ width: '40vw'
38
+ }}
39
+ >
40
+ {labelValue}
41
+ </p>
42
+ </TextOverflowTooltip>
43
+ );
44
+ }
45
+ };
46
+
47
+ export const AsMultiLine: Story = {
48
+ args: {
49
+ ...Default.args
50
+ },
51
+ render: ({ label, ...args }) => {
52
+ const [labelValue, setLabelValue] = useState(label);
53
+ useEffect(() => setLabelValue(label), [label]);
54
+
55
+ return (
56
+ <TextOverflowTooltip {...args} label={labelValue}>
57
+ <p
58
+ style={{
59
+ WebkitBoxOrient: 'vertical',
60
+ WebkitLineClamp: '2',
61
+ display: '-webkit-box',
62
+ overflow: 'hidden',
63
+ width: '40vw'
64
+ }}
65
+ >
66
+ {labelValue}
67
+ </p>
68
+ </TextOverflowTooltip>
69
+ );
70
+ }
71
+ };
@@ -0,0 +1,5 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ export const useStyles = makeStyles()(() => ({
4
+ textOverflowTooltip: {}
5
+ }));
@@ -0,0 +1,115 @@
1
+ import { ReactElement, useCallback, useEffect, useRef } from 'react';
2
+
3
+ import { atom, useAtom } from 'jotai';
4
+
5
+ import { Tooltip, TooltipProps } from '../Tooltip';
6
+ import { useResizeObserver } from '../../../utils/useResizeObserver';
7
+
8
+ import { useStyles } from './TextOverflowTooltip.styles';
9
+
10
+ type TextOverflowTooltipProps = Omit<TooltipProps, 'followCursor' | 'hasCaret'>;
11
+
12
+ /** @description
13
+ * This component is used to display a tooltip when the text is too long, and displays the full text on hover.
14
+ *
15
+ * It's up to the child component to define it's `width` / `max-width`, and ellipsis.
16
+ *
17
+ * For single lines use :
18
+ * ```
19
+ * width: var(--width-of-the-text);
20
+ * white-space: nowrap;
21
+ * ```
22
+ *
23
+ * For multi-lines use :
24
+ * ```
25
+ * width: var(--width-of-the-text);
26
+ * display: -webkit-box;
27
+ * -webkit-box-orient: vertical;
28
+ * -webkit-line-clamp: var(--max-lines-of-the-text);
29
+ * ```
30
+ */
31
+
32
+ const TextOverflowTooltip = ({
33
+ children,
34
+ label,
35
+ position = 'bottom',
36
+ isOpen,
37
+ ...tooltipProps
38
+ }: TextOverflowTooltipProps): ReactElement => {
39
+ const { classes } = useStyles();
40
+
41
+ const stateAtom = useRef(
42
+ atom<{
43
+ hasOverflow: boolean;
44
+ isOpen: boolean;
45
+ }>({ hasOverflow: false, isOpen: false })
46
+ ).current;
47
+
48
+ const [state, setState] = useAtom(stateAtom);
49
+
50
+ const onMouseEnter = useCallback(
51
+ () =>
52
+ state.hasOverflow &&
53
+ setState({
54
+ ...state,
55
+ isOpen: true
56
+ }),
57
+ [state.hasOverflow]
58
+ );
59
+
60
+ const onMouseLeave = useCallback(
61
+ () =>
62
+ state.hasOverflow &&
63
+ setState({
64
+ ...state,
65
+ isOpen: false
66
+ }),
67
+ [state.hasOverflow]
68
+ );
69
+
70
+ const elRef = useRef<HTMLDivElement>(null);
71
+
72
+ const onResize = (): void => {
73
+ const { firstElementChild: el } = elRef.current || {};
74
+ if (el instanceof HTMLElement) {
75
+ setState({
76
+ ...state,
77
+ hasOverflow:
78
+ el.scrollWidth > el.offsetWidth || el.scrollHeight > el.offsetHeight
79
+ });
80
+ }
81
+ };
82
+
83
+ useResizeObserver({
84
+ onResize,
85
+ ref: elRef
86
+ });
87
+
88
+ useEffect(() => {
89
+ if (isOpen) setState({ ...state, isOpen });
90
+ onResize();
91
+ }, [isOpen, label]);
92
+
93
+ return (
94
+ <Tooltip
95
+ followCursor
96
+ hasCaret={false}
97
+ isOpen={state.isOpen}
98
+ label={label}
99
+ position={position}
100
+ {...tooltipProps}
101
+ >
102
+ <div
103
+ className={classes.textOverflowTooltip}
104
+ data-has-overflow={state.hasOverflow}
105
+ ref={elRef}
106
+ onMouseEnter={onMouseEnter}
107
+ onMouseLeave={onMouseLeave}
108
+ >
109
+ {children}
110
+ </div>
111
+ </Tooltip>
112
+ );
113
+ };
114
+
115
+ export { TextOverflowTooltip };
@@ -0,0 +1 @@
1
+ export * from './TextOverflowTooltip';
@@ -0,0 +1,41 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { Info as InfoIcon } from '@mui/icons-material';
4
+
5
+ import { Tooltip } from './Tooltip';
6
+
7
+ const meta: Meta<typeof Tooltip> = {
8
+ argTypes: {
9
+ position: {
10
+ control: {
11
+ options: ['top', 'bottom', 'left', 'right']
12
+ }
13
+ }
14
+ },
15
+ component: Tooltip,
16
+ parameters: {
17
+ layout: 'centered'
18
+ }
19
+ };
20
+
21
+ export default meta;
22
+ type Story = StoryObj<typeof Tooltip>;
23
+
24
+ export const Default: Story = {
25
+ args: {
26
+ children: <InfoIcon />,
27
+ label: 'Tooltip content'
28
+ }
29
+ };
30
+
31
+ export const WrappedContent: Story = {
32
+ args: {
33
+ children: <InfoIcon />,
34
+ followCursor: false,
35
+ hasCaret: true,
36
+ isOpen: true,
37
+ label:
38
+ 'Qui deserunt pariatur quis. Duis nisi velit culpa labore ipsum reprehenderit sunt laborum anim sint quis magna consequat amet. Voluptate tempor nostrud eiusmod enim qui reprehenderit.',
39
+ position: 'right'
40
+ }
41
+ };
@@ -0,0 +1,5 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ export const useStyles = makeStyles()(() => ({
4
+ tooltip: {}
5
+ }));
@@ -0,0 +1,46 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+
3
+ import { Tooltip as MuiTooltip } from '@mui/material';
4
+
5
+ import { AriaLabelingAttributes } from '../../@types/aria-attributes';
6
+ import { DataTestAttributes } from '../../@types/data-attributes';
7
+
8
+ import { useStyles } from './Tooltip.styles';
9
+
10
+ export type TooltipProps = {
11
+ children: ReactElement;
12
+ followCursor?: boolean;
13
+ hasCaret?: boolean;
14
+ isOpen?: boolean;
15
+ label: ReactNode;
16
+ position?: 'top' | 'bottom' | 'left' | 'right';
17
+ } & AriaLabelingAttributes &
18
+ DataTestAttributes;
19
+
20
+ const Tooltip = ({
21
+ children,
22
+ label,
23
+ position = 'bottom',
24
+ followCursor = true,
25
+ isOpen,
26
+ hasCaret = false,
27
+ ...attr
28
+ }: TooltipProps): ReactElement => {
29
+ const { classes } = useStyles();
30
+
31
+ return (
32
+ <MuiTooltip
33
+ arrow={hasCaret}
34
+ className={classes.tooltip}
35
+ followCursor={followCursor}
36
+ open={isOpen}
37
+ placement={position}
38
+ title={label}
39
+ {...attr}
40
+ >
41
+ {children}
42
+ </MuiTooltip>
43
+ );
44
+ };
45
+
46
+ export { Tooltip };
@@ -0,0 +1 @@
1
+ export * from './Tooltip';
@@ -3,3 +3,6 @@ export * from './Modal';
3
3
  export * from './Form';
4
4
  export * from './Header';
5
5
  export * from './DataTable';
6
+ export * from './Layout';
7
+ export * from './Tooltip';
8
+ export * from './Menu';
package/src/index.ts CHANGED
@@ -140,8 +140,6 @@ export { default as ActionsList } from './ActionsList';
140
140
 
141
141
  export type { ResponseError, CatchErrorProps } from './api/customFetch';
142
142
 
143
- export * from './layout/TiledListingPage';
144
-
145
143
  export * from './utils';
146
144
  export { default as FluidTypography } from './Typography/FluidTypography';
147
145
  export * from './Dashboard';
@@ -0,0 +1,108 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+ import React, { ReactElement } from 'react';
3
+
4
+ import { Meta } from '@storybook/react';
5
+
6
+ import {
7
+ Add as AddIcon,
8
+ Settings as SettingsIcon,
9
+ Share as ShareIcon
10
+ } from '@mui/icons-material';
11
+
12
+ import {
13
+ Button,
14
+ IconButton,
15
+ Menu,
16
+ PageHeader,
17
+ PageLayout
18
+ } from '../../components';
19
+
20
+ const meta: Meta = {
21
+ args: {
22
+ actions: {}
23
+ },
24
+ parameters: {
25
+ layout: 'fullscreen'
26
+ },
27
+ title: 'screens/Dashboards detail'
28
+ };
29
+
30
+ export default meta;
31
+
32
+ const DefaultView = (args): ReactElement => {
33
+ const { data } = args;
34
+
35
+ return (
36
+ <PageLayout>
37
+ <PageLayout.Header>
38
+ <PageHeader>
39
+ <PageHeader.Main>
40
+ <PageHeader.Menu>
41
+ <Menu>
42
+ <Menu.Button />
43
+ <Menu.Items>
44
+ <Menu.Item>Menu Item</Menu.Item>
45
+ <Menu.Item>Menu Item</Menu.Item>
46
+ <Menu.Item>Menu Item</Menu.Item>
47
+ <Menu.Divider />
48
+ <Menu.Item>
49
+ <Button
50
+ icon={<AddIcon />}
51
+ iconVariant="start"
52
+ variant="ghost"
53
+ >
54
+ Add item
55
+ </Button>
56
+ </Menu.Item>
57
+ </Menu.Items>
58
+ </Menu>
59
+ </PageHeader.Menu>
60
+ <PageHeader.Title
61
+ description={data.dashboard.description}
62
+ title={data.dashboard.name}
63
+ />
64
+ </PageHeader.Main>
65
+ </PageHeader>
66
+ </PageLayout.Header>
67
+ <PageLayout.Body>
68
+ <PageLayout.Actions>
69
+ <IconButton
70
+ aria-label="edit"
71
+ data-testid="edit"
72
+ icon={<SettingsIcon />}
73
+ size="small"
74
+ variant="ghost"
75
+ />
76
+ <IconButton
77
+ aria-label="share"
78
+ data-testid="share"
79
+ icon={<ShareIcon />}
80
+ size="small"
81
+ variant="ghost"
82
+ />
83
+ </PageLayout.Actions>
84
+ </PageLayout.Body>
85
+ </PageLayout>
86
+ );
87
+ };
88
+
89
+ export const Default = {
90
+ args: {
91
+ data: {
92
+ dashboard: {
93
+ description:
94
+ 'Description et culpa sit commodo ea enim excepteur elit. Velit irure velit tempor culpa commodo eu adipisicing eu proident ullamco.',
95
+ id: 1,
96
+ name: 'Dashboard 1'
97
+ }
98
+ }
99
+ },
100
+ render: DefaultView
101
+ };
102
+
103
+ export const AsEditLayoutState = {
104
+ args: {
105
+ ...Default.args
106
+ },
107
+ render: DefaultView
108
+ };
@@ -11,16 +11,11 @@ import {
11
11
  DashboardForm,
12
12
  DashboardFormProps,
13
13
  DataTable,
14
- Header,
15
- Modal
14
+ Modal,
15
+ PageHeader,
16
+ PageLayout
16
17
  } from '../../components';
17
18
  import { Default as DashboardFormDefaultStory } from '../../components/Form/Dashboard/DashboardForm.stories';
18
- import {
19
- TiledListingActions,
20
- TiledListingContent,
21
- TiledListingList,
22
- TiledListingPage
23
- } from '../../layout/TiledListingPage';
24
19
 
25
20
  const meta: Meta = {
26
21
  args: {
@@ -64,7 +59,10 @@ const meta: Meta = {
64
59
  },
65
60
  title: 'Dashboards overview'
66
61
  },
67
- title: 'screens/Dashboards'
62
+ parameters: {
63
+ layout: 'fullscreen'
64
+ },
65
+ title: 'screens/Dashboards overview'
68
66
  };
69
67
 
70
68
  export default meta;
@@ -134,10 +132,16 @@ const DefaultView = (args): JSX.Element => {
134
132
  };
135
133
 
136
134
  return (
137
- <TiledListingPage>
138
- <Header title={title} />
139
- <TiledListingList>
140
- <TiledListingActions>
135
+ <PageLayout>
136
+ <PageLayout.Header>
137
+ <PageHeader>
138
+ <PageHeader.Main>
139
+ <PageHeader.Title title={title} />
140
+ </PageHeader.Main>
141
+ </PageHeader>
142
+ </PageLayout.Header>
143
+ <PageLayout.Body>
144
+ <PageLayout.Actions>
141
145
  {dataDashboards.length !== 0 && (
142
146
  <Button
143
147
  aria-label="add"
@@ -150,101 +154,105 @@ const DefaultView = (args): JSX.Element => {
150
154
  {actions.create.label}
151
155
  </Button>
152
156
  )}
153
- </TiledListingActions>
154
- <TiledListingContent>
155
- <DataTable isEmpty={dataDashboards.length === 0}>
156
- {dataDashboards.length === 0 ? (
157
- <DataTable.EmptyState
158
- labels={list.emptyState.labels}
159
- onCreate={() =>
160
- setDialogState({ item: null, open: true, variant: 'create' })
161
- }
162
- />
163
- ) : (
164
- dataDashboards.map((dashboard) => (
165
- <DataTable.Item
166
- hasActions
167
- hasCardAction
168
- description={dashboard.description}
169
- key={dashboard.id}
170
- title={dashboard.name}
171
- onDelete={() =>
172
- setDeleteDialogState({ item: dashboard, open: true })
173
- }
174
- onEdit={() =>
175
- setDialogState({
176
- item: dashboard,
177
- open: true,
178
- variant: 'update'
179
- })
180
- }
181
- />
182
- ))
183
- )}
184
- </DataTable>
185
- </TiledListingContent>
186
- <Modal
187
- open={dialogState.open}
188
- onClose={() =>
189
- setDialogState({
190
- item: null,
191
- open: false,
192
- variant: dialogState.variant
193
- })
194
- }
195
- >
196
- <Modal.Header>
197
- {form.labels.title[dialogState.variant ?? 'create']}
198
- </Modal.Header>
199
- <Modal.Body>
200
- <DashboardForm
201
- labels={DashboardFormDefaultStory!.args!.labels!}
202
- resource={dialogState.item || undefined}
203
- variant={dialogState.variant}
204
- onCancel={() =>
157
+ </PageLayout.Actions>
158
+
159
+ <DataTable isEmpty={dataDashboards.length === 0}>
160
+ {dataDashboards.length === 0 ? (
161
+ <DataTable.EmptyState
162
+ labels={list.emptyState.labels}
163
+ onCreate={() =>
205
164
  setDialogState({
206
165
  item: null,
207
- open: false,
208
- variant: dialogState.variant
166
+ open: true,
167
+ variant: 'create'
209
168
  })
210
169
  }
211
- onSubmit={(values) =>
212
- dialogState.variant === 'create'
213
- ? createDashboard(values)
214
- : updateDashboard(values)
215
- }
216
170
  />
217
- </Modal.Body>
218
- </Modal>
219
- <Modal
220
- open={deleteDialogState.open}
221
- onClose={() =>
222
- setDeleteDialogState({
223
- ...deleteDialogState,
224
- open: false
225
- })
226
- }
227
- >
228
- <Modal.Header>{deleteConfirmation.labels.title}</Modal.Header>
229
- <Modal.Body>
230
- <p>
231
- {deleteConfirmation.labels.description(
232
- deleteDialogState.item?.name
233
- )}
234
- </p>
235
- </Modal.Body>
236
- <Modal.Actions
237
- isDanger
238
- labels={deleteConfirmation.labels.actions}
239
- onCancel={() => setDeleteDialogState({ item: null, open: false })}
240
- onConfirm={() => {
241
- deleteDashboard(deleteDialogState.item?.id);
242
- setDeleteDialogState({ item: null, open: false });
243
- }}
171
+ ) : (
172
+ dataDashboards.map((dashboard) => (
173
+ <DataTable.Item
174
+ hasActions
175
+ hasCardAction
176
+ description={dashboard.description}
177
+ key={dashboard.id}
178
+ title={dashboard.name}
179
+ onDelete={() =>
180
+ setDeleteDialogState({ item: dashboard, open: true })
181
+ }
182
+ onEdit={() =>
183
+ setDialogState({
184
+ item: dashboard,
185
+ open: true,
186
+ variant: 'update'
187
+ })
188
+ }
189
+ />
190
+ ))
191
+ )}
192
+ </DataTable>
193
+ </PageLayout.Body>
194
+
195
+ <Modal
196
+ open={dialogState.open}
197
+ onClose={() =>
198
+ setDialogState({
199
+ item: null,
200
+ open: false,
201
+ variant: dialogState.variant
202
+ })
203
+ }
204
+ >
205
+ <Modal.Header>
206
+ {form.labels.title[dialogState.variant ?? 'create']}
207
+ </Modal.Header>
208
+ <Modal.Body>
209
+ <DashboardForm
210
+ labels={DashboardFormDefaultStory!.args!.labels!}
211
+ resource={dialogState.item || undefined}
212
+ variant={dialogState.variant}
213
+ onCancel={() =>
214
+ setDialogState({
215
+ item: null,
216
+ open: false,
217
+ variant: dialogState.variant
218
+ })
219
+ }
220
+ onSubmit={(values) =>
221
+ dialogState.variant === 'create'
222
+ ? createDashboard(values)
223
+ : updateDashboard(values)
224
+ }
244
225
  />
245
- </Modal>
246
- </TiledListingList>
247
- </TiledListingPage>
226
+ </Modal.Body>
227
+ </Modal>
228
+ <Modal
229
+ open={deleteDialogState.open}
230
+ onClose={() =>
231
+ setDeleteDialogState({
232
+ ...deleteDialogState,
233
+ open: false
234
+ })
235
+ }
236
+ >
237
+ <Modal.Header>{deleteConfirmation.labels.title}</Modal.Header>
238
+ <Modal.Body>
239
+ <p>
240
+ {deleteConfirmation.labels.description(
241
+ deleteDialogState.item?.name
242
+ )}
243
+ </p>
244
+ </Modal.Body>
245
+ <Modal.Actions
246
+ isDanger
247
+ labels={deleteConfirmation.labels.actions}
248
+ onCancel={() => setDeleteDialogState({ item: null, open: false })}
249
+ onConfirm={() => {
250
+ deleteDashboard(deleteDialogState.item?.id);
251
+ setDeleteDialogState({ item: null, open: false });
252
+ }}
253
+ />
254
+ </Modal>
255
+ </PageLayout>
248
256
  );
249
257
  };
250
258
 
@@ -2,6 +2,7 @@ import { useRef, useCallback } from 'react';
2
2
 
3
3
  interface HookParam {
4
4
  action: () => void;
5
+ intersectionObserverOptions?: IntersectionObserverInit;
5
6
  loading: boolean;
6
7
  maxPage: number;
7
8
  page: number;
@@ -11,7 +12,8 @@ export const useIntersectionObserver = ({
11
12
  maxPage,
12
13
  page,
13
14
  loading,
14
- action
15
+ action,
16
+ intersectionObserverOptions
15
17
  }: HookParam): ((node) => void) => {
16
18
  const observer = useRef<IntersectionObserver | null>(null);
17
19
  const lastElementRef = useCallback(
@@ -30,7 +32,7 @@ export const useIntersectionObserver = ({
30
32
  if (entry.isIntersecting && page < maxPage) {
31
33
  action();
32
34
  }
33
- });
35
+ }, intersectionObserverOptions);
34
36
 
35
37
  if (node && observer.current) {
36
38
  observer.current.observe(node);