@centreon/ui 23.10.24 → 23.10.25-feature-MON-19319.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 (49) hide show
  1. package/package.json +8 -1
  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 +234 -208
  7. package/src/components/Button/Button.styles.ts +7 -2
  8. package/src/components/Header/Header.styles.ts +0 -27
  9. package/src/components/Header/PageHeader/PageHeader.stories.tsx +83 -0
  10. package/src/components/Header/PageHeader/PageHeader.styles.ts +105 -0
  11. package/src/components/Header/PageHeader/PageHeader.tsx +15 -0
  12. package/src/components/Header/PageHeader/PageHeaderActions.tsx +17 -0
  13. package/src/components/Header/PageHeader/PageHeaderMain.tsx +15 -0
  14. package/src/components/Header/PageHeader/PageHeaderMenu.tsx +15 -0
  15. package/src/components/Header/PageHeader/PageHeaderTitle.tsx +44 -0
  16. package/src/components/Header/PageHeader/index.ts +12 -0
  17. package/src/components/Header/index.ts +1 -1
  18. package/src/components/Layout/AreaIndicator.styles.ts +33 -0
  19. package/src/components/Layout/AreaIndicator.tsx +35 -0
  20. package/src/components/Layout/PageLayout/PageLayout.stories.tsx +81 -0
  21. package/src/components/Layout/PageLayout/PageLayout.styles.ts +30 -0
  22. package/src/components/Layout/PageLayout/PageLayout.tsx +21 -0
  23. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +15 -0
  24. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +24 -0
  25. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +15 -0
  26. package/src/components/Layout/PageLayout/index.ts +10 -0
  27. package/src/components/Layout/index.ts +1 -0
  28. package/src/components/Menu/Menu.tsx +0 -0
  29. package/src/components/Tooltip/TextOverflowTooltip/TextOverflowTooltip.stories.tsx +71 -0
  30. package/src/components/Tooltip/TextOverflowTooltip/TextOverflowTooltip.styles.ts +5 -0
  31. package/src/components/Tooltip/TextOverflowTooltip/TextOverflowTooltip.tsx +115 -0
  32. package/src/components/Tooltip/TextOverflowTooltip/index.tsx +1 -0
  33. package/src/components/Tooltip/Tooltip.stories.tsx +41 -0
  34. package/src/components/Tooltip/Tooltip.styles.ts +5 -0
  35. package/src/components/Tooltip/Tooltip.tsx +46 -0
  36. package/src/components/Tooltip/index.ts +1 -0
  37. package/src/components/index.ts +2 -0
  38. package/src/index.ts +0 -2
  39. package/src/screens/dashboard/DashboardsOverview.stories.tsx +109 -101
  40. package/src/utils/useResizeObserver.ts +6 -0
  41. package/test/setupTests.js +23 -20
  42. package/src/components/Header/Header.stories.tsx +0 -27
  43. package/src/components/Header/Header.tsx +0 -23
  44. package/src/layout/TiledListingPage/TiledListingActions.tsx +0 -15
  45. package/src/layout/TiledListingPage/TiledListingContent.tsx +0 -21
  46. package/src/layout/TiledListingPage/TiledListingList.tsx +0 -11
  47. package/src/layout/TiledListingPage/TiledListingPage.styles.ts +0 -16
  48. package/src/layout/TiledListingPage/TiledListingPage.tsx +0 -15
  49. 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,5 @@ 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';
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';
@@ -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,6 +59,9 @@ const meta: Meta = {
64
59
  },
65
60
  title: 'Dashboards overview'
66
61
  },
62
+ parameters: {
63
+ layout: 'fullscreen'
64
+ },
67
65
  title: 'screens/Dashboards'
68
66
  };
69
67
 
@@ -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
 
@@ -0,0 +1,6 @@
1
+ // https://github.com/ZeeCoder/use-resize-observer
2
+ import useResizeObserver from 'use-resize-observer';
3
+
4
+ export * from 'use-resize-observer';
5
+
6
+ export { useResizeObserver };
@@ -1,42 +1,45 @@
1
1
  /* eslint-disable no-underscore-dangle */
2
2
  /* eslint-disable no-undef */
3
+ // FIXME: testing library setup
4
+ // import '@testing-library/jest-dom';
5
+ // import '@testing-library/react';
3
6
  // import '@testing-library/jest-dom/extend-expect';
4
- import i18n from 'i18next';
5
- import { initReactI18next } from 'react-i18next';
6
- import fetchMock from 'jest-fetch-mock';
7
+ import i18n from "i18next";
8
+ import { initReactI18next } from "react-i18next";
9
+ import fetchMock from "jest-fetch-mock";
7
10
 
8
11
  const mockedMatchMedia = () => ({
9
12
  addListener: () => {},
10
13
  matches: false,
11
- removeListener: () => {}
14
+ removeListener: () => {},
12
15
  });
13
16
 
14
17
  window.matchMedia = window.matchMedia || mockedMatchMedia;
15
18
 
16
19
  document.createRange = () => ({
17
20
  commonAncestorContainer: {
18
- nodeName: 'BODY',
19
- ownerDocument: document
21
+ nodeName: "BODY",
22
+ ownerDocument: document,
20
23
  },
21
24
  setEnd: () => {},
22
- setStart: () => {}
25
+ setStart: () => {},
23
26
  });
24
27
 
25
28
  global.IntersectionObserver = class IntersectionObserver {
26
29
  observe() {
27
- this.a = '';
30
+ this.a = "";
28
31
 
29
32
  return null;
30
33
  }
31
34
 
32
35
  disconnect() {
33
- this.a = '';
36
+ this.a = "";
34
37
 
35
38
  return null;
36
39
  }
37
40
  };
38
41
 
39
- Object.defineProperty(window, 'matchMedia', {
42
+ Object.defineProperty(window, "matchMedia", {
40
43
  value: jest.fn().mockImplementation((query) => ({
41
44
  addEventListener: jest.fn(),
42
45
  addListener: jest.fn(),
@@ -45,42 +48,42 @@ Object.defineProperty(window, 'matchMedia', {
45
48
  media: query,
46
49
  onchange: null,
47
50
  removeEventListener: jest.fn(),
48
- removeListener: jest.fn()
51
+ removeListener: jest.fn(),
49
52
  })),
50
- writable: true
53
+ writable: true,
51
54
  });
52
55
 
53
- Object.defineProperty(Image.prototype, 'onload', {
56
+ Object.defineProperty(Image.prototype, "onload", {
54
57
  get() {
55
58
  return this._onload;
56
59
  },
57
60
  set(onload) {
58
61
  onloadRef = onload;
59
62
  this._onload = onload;
60
- }
63
+ },
61
64
  });
62
- Object.defineProperty(Image.prototype, 'onerror', {
65
+ Object.defineProperty(Image.prototype, "onerror", {
63
66
  get() {
64
67
  return this._onerror;
65
68
  },
66
69
  set(onerror) {
67
70
  onerrorRef = onerror;
68
71
  this._onerror = onerror;
69
- }
72
+ },
70
73
  });
71
74
 
72
75
  i18n.use(initReactI18next).init({
73
- fallbackLng: 'en',
76
+ fallbackLng: "en",
74
77
  keySeparator: false,
75
- lng: 'en',
78
+ lng: "en",
76
79
  nsSeparator: false,
77
- resources: {}
80
+ resources: {},
78
81
  });
79
82
 
80
83
  window.Image = () => ({
81
84
  onerror: () => {},
82
85
  onload: () => {},
83
- src: null
86
+ src: null,
84
87
  });
85
88
 
86
89
  fetchMock.enableMocks();