@centreon/ui 24.5.2 → 24.5.3
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 +8 -4
- package/public/mockServiceWorker.js +81 -100
- package/src/ActionsList/index.stories.tsx +7 -1
- package/src/Dashboard/Dashboard.styles.ts +3 -2
- package/src/Dashboard/Item.tsx +11 -2
- package/src/Dashboard/Layout.tsx +4 -2
- package/src/Graph/BarStack/BarStack.stories.tsx +8 -6
- package/src/Graph/BarStack/ResponsiveBarStack.tsx +3 -3
- package/src/Graph/HeatMap/HeatMap.stories.tsx +20 -0
- package/src/Graph/LineChart/index.stories.tsx +1 -1
- package/src/Graph/PieChart/PieChart.stories.tsx +11 -15
- package/src/Graph/PieChart/ResponsivePie.tsx +1 -1
- package/src/Graph/Tree/DescendantNodes.tsx +1 -0
- package/src/Graph/Tree/Links.tsx +15 -2
- package/src/Graph/Tree/Tree.cypress.spec.tsx +24 -0
- package/src/Graph/Tree/Tree.stories.tsx +17 -1
- package/src/Graph/Tree/models.ts +3 -0
- package/src/TimePeriods/index.stories.tsx +7 -12
- package/src/TopCounterElements/TopCounterLayout.tsx +3 -4
- package/src/TopCounterElements/useCloseOnLegacyPage.tsx +9 -6
- package/src/api/QueryProvider.tsx +1 -1
- package/src/api/logger.ts +11 -0
- package/src/api/useFetchQuery/index.test.ts +0 -5
- package/src/api/useFetchQuery/index.ts +2 -5
- package/src/api/useGraphQuery/index.ts +7 -1
- package/src/api/useMutationQuery/index.ts +2 -5
- package/src/api/useRequest/index.test.ts +0 -3
- package/src/api/useRequest/index.ts +3 -6
- package/src/components/Form/AccessRights/AccessRights.cypress.spec.tsx +27 -13
- package/src/components/Form/AccessRights/AccessRights.stories.tsx +0 -19
- package/src/components/Form/AccessRights/AccessRights.styles.ts +1 -1
- package/src/components/Form/AccessRights/AccessRights.tsx +6 -5
- package/src/components/Form/AccessRights/Actions/Actions.styles.ts +3 -7
- package/src/components/Form/AccessRights/Actions/Actions.tsx +15 -32
- package/src/components/Form/AccessRights/Actions/useActions.ts +4 -37
- package/src/components/Form/AccessRights/models.ts +0 -3
- package/src/components/Form/AccessRights/storiesData.ts +0 -3
- package/src/components/Form/AccessRights/useAccessRightsChange.ts +30 -0
- package/src/components/Form/AccessRights/utils.ts +18 -0
- package/src/components/Form/Dashboard/translatedLabels.ts +0 -1
- package/src/components/List/Item/ListItem.styles.ts +2 -2
- package/src/components/Tabs/Tab.styles.ts +25 -0
- package/src/components/Tabs/TabPanel.tsx +22 -0
- package/src/components/Tabs/Tabs.cypress.spec.tsx +70 -0
- package/src/components/Tabs/Tabs.stories.tsx +55 -0
- package/src/components/Tabs/Tabs.tsx +55 -0
- package/src/components/Tabs/index.ts +6 -0
- package/src/components/Zoom/Minimap.tsx +4 -2
- package/src/components/Zoom/Zoom.cypress.spec.tsx +13 -13
- package/src/components/Zoom/Zoom.tsx +4 -1
- package/src/components/Zoom/ZoomContent.tsx +5 -2
- package/src/components/index.ts +1 -0
- package/src/index.ts +1 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/resourcesStatusURL.ts +166 -0
- package/src/utils/usePluralizedTranslation.test.ts +159 -0
- package/src/utils/usePluralizedTranslation.ts +20 -3
- package/src/components/Form/Dashboard/DashboardForm.stories.ts +0 -39
package/src/Graph/Tree/models.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface BaseProp {
|
|
|
14
14
|
name: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
export type Link = 'curve' | 'line' | 'step';
|
|
18
|
+
|
|
17
19
|
export interface ChildrenProps<TData> {
|
|
18
20
|
ancestors: Array<Node<TData>>;
|
|
19
21
|
depth: number;
|
|
@@ -48,5 +50,6 @@ export interface TreeProps<TData> {
|
|
|
48
50
|
) => string | number | undefined;
|
|
49
51
|
getStrokeOpacity?: (props: LinkProps<TData>) => string | number | undefined;
|
|
50
52
|
getStrokeWidth?: (props: LinkProps<TData>) => string | number | undefined;
|
|
53
|
+
type?: Link;
|
|
51
54
|
};
|
|
52
55
|
}
|
|
@@ -10,6 +10,9 @@ import TimePeriod from '.';
|
|
|
10
10
|
|
|
11
11
|
const meta: Meta<typeof TimePeriod> = {
|
|
12
12
|
component: TimePeriod,
|
|
13
|
+
parameters: {
|
|
14
|
+
chromatic: { disableSnapshot: true }
|
|
15
|
+
},
|
|
13
16
|
tags: ['autodocs']
|
|
14
17
|
};
|
|
15
18
|
|
|
@@ -88,27 +91,20 @@ const args = {
|
|
|
88
91
|
]
|
|
89
92
|
};
|
|
90
93
|
|
|
91
|
-
const parameters = {
|
|
92
|
-
chromatic: { diffThreshold: 0.1 }
|
|
93
|
-
};
|
|
94
|
-
|
|
95
94
|
export const BasicTimePeriod: Story = {
|
|
96
95
|
...Template,
|
|
97
|
-
argTypes
|
|
98
|
-
parameters
|
|
96
|
+
argTypes
|
|
99
97
|
};
|
|
100
98
|
|
|
101
99
|
export const WithExtraTimePeriods: Story = {
|
|
102
100
|
...Template,
|
|
103
101
|
argTypes,
|
|
104
|
-
args
|
|
105
|
-
parameters
|
|
102
|
+
args
|
|
106
103
|
};
|
|
107
104
|
|
|
108
105
|
export const WithExternalComponent: Story = {
|
|
109
106
|
...TemplateWithExternalComponent,
|
|
110
|
-
argTypes
|
|
111
|
-
parameters
|
|
107
|
+
argTypes
|
|
112
108
|
};
|
|
113
109
|
|
|
114
110
|
export const SimpleTimePeriod: StorySimpleTimePeriod = {
|
|
@@ -116,6 +112,5 @@ export const SimpleTimePeriod: StorySimpleTimePeriod = {
|
|
|
116
112
|
args: {
|
|
117
113
|
endDate: dayjs(Date.now()).toDate(),
|
|
118
114
|
startDate: dayjs(Date.now()).subtract(29, 'day').toDate()
|
|
119
|
-
}
|
|
120
|
-
parameters
|
|
115
|
+
}
|
|
121
116
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import { makeStyles } from 'tss-react/mui';
|
|
4
4
|
|
|
5
|
-
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
|
6
5
|
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
|
7
|
-
import
|
|
6
|
+
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
|
8
7
|
import type { SvgIcon } from '@mui/material';
|
|
8
|
+
import { Badge, ClickAwayListener } from '@mui/material';
|
|
9
9
|
|
|
10
10
|
import useCloseOnLegacyPage from './useCloseOnLegacyPage';
|
|
11
11
|
|
|
@@ -115,7 +115,6 @@ const TopCounterLayout = ({
|
|
|
115
115
|
}: TopCounterLayoutProps): JSX.Element => {
|
|
116
116
|
const { classes, cx } = useStyles();
|
|
117
117
|
const [toggled, setToggled] = useState(false);
|
|
118
|
-
|
|
119
118
|
const subMenuId = title.replace(/[^A-Za-z]/, '-');
|
|
120
119
|
useCloseOnLegacyPage({ setToggled });
|
|
121
120
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Dispatch, SetStateAction, useEffect } from 'react';
|
|
2
2
|
|
|
3
|
+
import { isNil } from 'ramda';
|
|
3
4
|
import { useLocation } from 'react-router-dom';
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
@@ -14,21 +15,23 @@ const useCloseOnLegacyPage = ({ setToggled }: Props): void => {
|
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
useEffect(() => {
|
|
17
|
-
const iframe = document.getElementById(
|
|
18
|
+
const iframe = document.getElementById(
|
|
19
|
+
'main-content'
|
|
20
|
+
) as HTMLIFrameElement | null;
|
|
18
21
|
|
|
19
|
-
if (!isLegacyRoute) {
|
|
22
|
+
if (!isLegacyRoute || isNil(iframe)) {
|
|
20
23
|
return () => undefined;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
const closeSubMenuOnLegacyPage = (): void => {
|
|
24
|
-
iframe
|
|
27
|
+
iframe?.contentWindow?.document?.addEventListener('click', closeSubMenu);
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
iframe
|
|
30
|
+
iframe?.addEventListener('load', closeSubMenuOnLegacyPage);
|
|
28
31
|
|
|
29
32
|
return () => {
|
|
30
|
-
iframe
|
|
31
|
-
iframe
|
|
33
|
+
iframe?.removeEventListener('load', closeSubMenuOnLegacyPage);
|
|
34
|
+
iframe?.contentWindow?.document?.removeEventListener(
|
|
32
35
|
'click',
|
|
33
36
|
closeSubMenu
|
|
34
37
|
);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { renderHook, waitFor, RenderHookResult } from '@testing-library/react';
|
|
2
2
|
import fetchMock from 'jest-fetch-mock';
|
|
3
|
-
import anyLogger from 'anylogger';
|
|
4
3
|
|
|
5
4
|
import TestQueryProvider from '../TestQueryProvider';
|
|
6
5
|
|
|
@@ -77,8 +76,6 @@ describe('useFetchQuery', () => {
|
|
|
77
76
|
expect(mockedShowErrorMessage).toHaveBeenCalledWith('custom message');
|
|
78
77
|
});
|
|
79
78
|
|
|
80
|
-
expect(anyLogger().error).toHaveBeenCalledWith('custom message');
|
|
81
|
-
|
|
82
79
|
await waitFor(() => {
|
|
83
80
|
expect(result.current.error).toStrictEqual({
|
|
84
81
|
additionalInformation: {
|
|
@@ -102,8 +99,6 @@ describe('useFetchQuery', () => {
|
|
|
102
99
|
await waitFor(() => {
|
|
103
100
|
expect(mockedShowErrorMessage).toHaveBeenCalledWith('error');
|
|
104
101
|
});
|
|
105
|
-
|
|
106
|
-
expect(anyLogger().error).toHaveBeenCalledWith('error');
|
|
107
102
|
});
|
|
108
103
|
|
|
109
104
|
it('shows a default failure message via the Snackbar as fallback', async () => {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
|
|
3
|
-
import 'ulog';
|
|
4
3
|
import {
|
|
5
4
|
QueryKey,
|
|
6
5
|
QueryObserverBaseResult,
|
|
@@ -9,12 +8,12 @@ import {
|
|
|
9
8
|
UseQueryOptions
|
|
10
9
|
} from '@tanstack/react-query';
|
|
11
10
|
import { JsonDecoder } from 'ts.data.json';
|
|
12
|
-
import anylogger from 'anylogger';
|
|
13
11
|
import { has, includes, isNil, not, omit } from 'ramda';
|
|
14
12
|
|
|
15
13
|
import { CatchErrorProps, customFetch, ResponseError } from '../customFetch';
|
|
16
14
|
import useSnackbar from '../../Snackbar/useSnackbar';
|
|
17
15
|
import { useDeepCompare } from '../../utils';
|
|
16
|
+
import { errorLog } from '../logger';
|
|
18
17
|
|
|
19
18
|
export interface UseFetchQueryProps<T> {
|
|
20
19
|
baseEndpoint?: string;
|
|
@@ -48,8 +47,6 @@ export interface PrefetchEndpointParams {
|
|
|
48
47
|
page: number;
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
const log = anylogger('API Request');
|
|
52
|
-
|
|
53
50
|
const useFetchQuery = <T extends object>({
|
|
54
51
|
getEndpoint,
|
|
55
52
|
getQueryKey,
|
|
@@ -87,7 +84,7 @@ const useFetchQuery = <T extends object>({
|
|
|
87
84
|
const manageError = (): void => {
|
|
88
85
|
const data = queryData.data as ResponseError | undefined;
|
|
89
86
|
if (data?.isError) {
|
|
90
|
-
|
|
87
|
+
errorLog(data.message);
|
|
91
88
|
const hasACorrespondingHttpCode = includes(
|
|
92
89
|
data?.statusCode || 0,
|
|
93
90
|
httpCodesBypassErrorSnackbar
|
|
@@ -24,6 +24,7 @@ interface CustomTimePeriod {
|
|
|
24
24
|
interface UseMetricsQueryProps {
|
|
25
25
|
baseEndpoint: string;
|
|
26
26
|
bypassMetricsExclusion?: boolean;
|
|
27
|
+
bypassQueryParams?: boolean;
|
|
27
28
|
includeAllResources?: boolean;
|
|
28
29
|
metrics: Array<Metric>;
|
|
29
30
|
refreshCount?: number;
|
|
@@ -90,7 +91,8 @@ const useGraphQuery = ({
|
|
|
90
91
|
timePeriodType: 1
|
|
91
92
|
},
|
|
92
93
|
refreshInterval = false,
|
|
93
|
-
refreshCount
|
|
94
|
+
refreshCount,
|
|
95
|
+
bypassQueryParams = false
|
|
94
96
|
}: UseMetricsQueryProps): UseMetricsQueryState => {
|
|
95
97
|
const timePeriodToUse = equals(timePeriod?.timePeriodType, -1)
|
|
96
98
|
? {
|
|
@@ -114,6 +116,10 @@ const useGraphQuery = ({
|
|
|
114
116
|
isLoading
|
|
115
117
|
} = useFetchQuery<PerformanceGraphData>({
|
|
116
118
|
getEndpoint: () => {
|
|
119
|
+
if (bypassQueryParams) {
|
|
120
|
+
return baseEndpoint;
|
|
121
|
+
}
|
|
122
|
+
|
|
117
123
|
const endpoint = buildListingEndpoint({
|
|
118
124
|
baseEndpoint,
|
|
119
125
|
parameters: {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import 'ulog';
|
|
2
1
|
import { useEffect } from 'react';
|
|
3
2
|
|
|
4
3
|
import {
|
|
@@ -7,12 +6,12 @@ import {
|
|
|
7
6
|
UseMutationResult
|
|
8
7
|
} from '@tanstack/react-query';
|
|
9
8
|
import { JsonDecoder } from 'ts.data.json';
|
|
10
|
-
import anylogger from 'anylogger';
|
|
11
9
|
import { includes, omit } from 'ramda';
|
|
12
10
|
|
|
13
11
|
import { CatchErrorProps, customFetch, ResponseError } from '../customFetch';
|
|
14
12
|
import useSnackbar from '../../Snackbar/useSnackbar';
|
|
15
13
|
import { useDeepCompare } from '../../utils';
|
|
14
|
+
import { errorLog } from '../logger';
|
|
16
15
|
|
|
17
16
|
export enum Method {
|
|
18
17
|
DELETE = 'DELETE',
|
|
@@ -52,8 +51,6 @@ export type UseMutationQueryProps<T, TMeta> = {
|
|
|
52
51
|
'mutationFn' | 'onError' | 'onMutate' | 'onSuccess' | 'mutateAsync' | 'mutate'
|
|
53
52
|
>;
|
|
54
53
|
|
|
55
|
-
const log = anylogger('API Request');
|
|
56
|
-
|
|
57
54
|
export type UseMutationQueryState<T, TMeta> = Omit<
|
|
58
55
|
UseMutationResult<T | ResponseError>,
|
|
59
56
|
'isError' | 'mutate' | 'mutateAsync'
|
|
@@ -127,7 +124,7 @@ const useMutationQuery = <T extends object, TMeta>({
|
|
|
127
124
|
const manageError = (): void => {
|
|
128
125
|
const data = queryData.data as ResponseError | undefined;
|
|
129
126
|
if (data?.isError) {
|
|
130
|
-
|
|
127
|
+
errorLog(data.message);
|
|
131
128
|
const hasACorrespondingHttpCode = includes(
|
|
132
129
|
data?.statusCode || 0,
|
|
133
130
|
httpCodesBypassErrorSnackbar
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import anyLogger from 'anylogger';
|
|
3
2
|
import { RenderHookResult, renderHook, act } from '@testing-library/react';
|
|
4
3
|
|
|
5
4
|
import useRequest, { RequestResult, RequestParams } from '.';
|
|
@@ -77,8 +76,6 @@ describe(useRequest, () => {
|
|
|
77
76
|
});
|
|
78
77
|
});
|
|
79
78
|
|
|
80
|
-
expect(anyLogger().error).toHaveBeenCalledWith(response);
|
|
81
|
-
|
|
82
79
|
expect(mockedShowErrorMessage).toHaveBeenCalledWith('failure');
|
|
83
80
|
});
|
|
84
81
|
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react';
|
|
2
2
|
|
|
3
|
-
import 'ulog';
|
|
4
3
|
import axios from 'axios';
|
|
5
4
|
import { pathOr, defaultTo, path, includes, or } from 'ramda';
|
|
6
|
-
import anylogger from 'anylogger';
|
|
7
5
|
import { JsonDecoder } from 'ts.data.json';
|
|
8
6
|
|
|
9
7
|
import useCancelTokenSource from '../useCancelTokenSource';
|
|
10
8
|
import useSnackbar from '../../Snackbar/useSnackbar';
|
|
11
|
-
|
|
12
|
-
const log = anylogger('API Request');
|
|
9
|
+
import { errorLog, warnLog } from '../logger';
|
|
13
10
|
|
|
14
11
|
export interface RequestParams<TResult> {
|
|
15
12
|
decoder?: JsonDecoder.Decoder<TResult>;
|
|
@@ -41,7 +38,7 @@ const useRequest = <TResult>({
|
|
|
41
38
|
}, []);
|
|
42
39
|
|
|
43
40
|
const showRequestErrorMessage = (error): void => {
|
|
44
|
-
|
|
41
|
+
errorLog(error.message);
|
|
45
42
|
|
|
46
43
|
const message = or(
|
|
47
44
|
pathOr(undefined, ['response', 'data', 'message'], error),
|
|
@@ -68,7 +65,7 @@ const useRequest = <TResult>({
|
|
|
68
65
|
.catch((error) => {
|
|
69
66
|
setSending(false);
|
|
70
67
|
if (axios.isCancel(error)) {
|
|
71
|
-
|
|
68
|
+
warnLog(error);
|
|
72
69
|
|
|
73
70
|
throw error;
|
|
74
71
|
}
|
|
@@ -15,11 +15,11 @@ import {
|
|
|
15
15
|
|
|
16
16
|
const initialize = ({
|
|
17
17
|
initialValues = simpleAccessRights,
|
|
18
|
-
loading = false
|
|
19
|
-
link = 'link'
|
|
18
|
+
loading = false
|
|
20
19
|
}): unknown => {
|
|
21
20
|
const cancel = cy.stub();
|
|
22
21
|
const save = cy.stub();
|
|
22
|
+
const change = cy.stub();
|
|
23
23
|
|
|
24
24
|
cy.interceptAPIRequest({
|
|
25
25
|
alias: 'getContacts',
|
|
@@ -47,10 +47,10 @@ const initialize = ({
|
|
|
47
47
|
}}
|
|
48
48
|
initialValues={initialValues}
|
|
49
49
|
labels={labels}
|
|
50
|
-
link={link}
|
|
51
50
|
loading={loading}
|
|
52
51
|
roles={roles}
|
|
53
52
|
submit={save}
|
|
53
|
+
onChange={change}
|
|
54
54
|
/>
|
|
55
55
|
</Provider>
|
|
56
56
|
</TestQueryProvider>
|
|
@@ -60,6 +60,7 @@ const initialize = ({
|
|
|
60
60
|
|
|
61
61
|
return {
|
|
62
62
|
cancel,
|
|
63
|
+
change,
|
|
63
64
|
save
|
|
64
65
|
};
|
|
65
66
|
};
|
|
@@ -74,21 +75,12 @@ describe('Access rights', () => {
|
|
|
74
75
|
cy.findByLabelText('Add a contact').should('be.visible');
|
|
75
76
|
cy.findByTestId('add_role').should('be.disabled');
|
|
76
77
|
cy.findByTestId('add').should('be.disabled');
|
|
77
|
-
cy.findByLabelText('Copy link').should('be.visible');
|
|
78
78
|
cy.findByLabelText('Cancel').should('be.visible');
|
|
79
79
|
cy.findByLabelText('Save').should('be.visible');
|
|
80
80
|
|
|
81
81
|
cy.makeSnapshot();
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
it('displays the access rights without link', () => {
|
|
85
|
-
initialize({ link: null });
|
|
86
|
-
|
|
87
|
-
cy.findByLabelText('Copy link').should('not.exist');
|
|
88
|
-
|
|
89
|
-
cy.makeSnapshot();
|
|
90
|
-
});
|
|
91
|
-
|
|
92
84
|
it('displays the access rights with an empty list', () => {
|
|
93
85
|
initialize({ initialValues: emptyAccessRights });
|
|
94
86
|
|
|
@@ -97,7 +89,7 @@ describe('Access rights', () => {
|
|
|
97
89
|
cy.makeSnapshot();
|
|
98
90
|
});
|
|
99
91
|
|
|
100
|
-
it('displays the access rights
|
|
92
|
+
it('displays the access rights list', () => {
|
|
101
93
|
initialize({});
|
|
102
94
|
|
|
103
95
|
simpleAccessRights.forEach(({ name, email, isContactGroup, role }) => {
|
|
@@ -370,4 +362,26 @@ describe('Access rights', () => {
|
|
|
370
362
|
|
|
371
363
|
cy.makeSnapshot();
|
|
372
364
|
});
|
|
365
|
+
|
|
366
|
+
it('calls the change function when the corresponding prop is set and the form is updated', () => {
|
|
367
|
+
const { change } = initialize({});
|
|
368
|
+
|
|
369
|
+
cy.contains(labels.add.contact).click();
|
|
370
|
+
cy.findByLabelText(labels.add.autocompleteContact).click();
|
|
371
|
+
|
|
372
|
+
cy.waitForRequest('@getContacts');
|
|
373
|
+
|
|
374
|
+
cy.contains('Entity 10').click();
|
|
375
|
+
|
|
376
|
+
cy.findByTestId('add').click();
|
|
377
|
+
|
|
378
|
+
cy.contains('Entity 10').should('be.visible');
|
|
379
|
+
|
|
380
|
+
cy.findByTestId('role-Entity 10').should('have.value', 'viewer');
|
|
381
|
+
cy.contains(labels.list.added)
|
|
382
|
+
.should('be.visible')
|
|
383
|
+
.then(() => {
|
|
384
|
+
expect(change).to.have.callCount(2);
|
|
385
|
+
});
|
|
386
|
+
});
|
|
373
387
|
});
|
|
@@ -47,7 +47,6 @@ export const Default: Story = {
|
|
|
47
47
|
},
|
|
48
48
|
initialValues: defaultAccessRights,
|
|
49
49
|
labels,
|
|
50
|
-
link: 'link',
|
|
51
50
|
roles,
|
|
52
51
|
submit: () => undefined
|
|
53
52
|
},
|
|
@@ -63,7 +62,6 @@ export const AccessRightsWithStates: Story = {
|
|
|
63
62
|
},
|
|
64
63
|
initialValues: accessRightsWithStates,
|
|
65
64
|
labels,
|
|
66
|
-
link: 'link',
|
|
67
65
|
roles,
|
|
68
66
|
submit: () => undefined
|
|
69
67
|
},
|
|
@@ -79,22 +77,6 @@ export const withEmptyState: Story = {
|
|
|
79
77
|
},
|
|
80
78
|
initialValues: emptyAccessRights,
|
|
81
79
|
labels,
|
|
82
|
-
link: 'link',
|
|
83
|
-
roles,
|
|
84
|
-
submit: () => undefined
|
|
85
|
-
},
|
|
86
|
-
render: Template
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
export const withoutLink: Story = {
|
|
90
|
-
args: {
|
|
91
|
-
cancel: () => undefined,
|
|
92
|
-
endpoints: {
|
|
93
|
-
contact: '/contact',
|
|
94
|
-
contactGroup: '/contactGroup'
|
|
95
|
-
},
|
|
96
|
-
initialValues: defaultAccessRights,
|
|
97
|
-
labels,
|
|
98
80
|
roles,
|
|
99
81
|
submit: () => undefined
|
|
100
82
|
},
|
|
@@ -110,7 +92,6 @@ export const loading: Story = {
|
|
|
110
92
|
},
|
|
111
93
|
initialValues: emptyAccessRights,
|
|
112
94
|
labels,
|
|
113
|
-
link: 'link',
|
|
114
95
|
loading: true,
|
|
115
96
|
roles,
|
|
116
97
|
submit: () => undefined
|
|
@@ -8,16 +8,17 @@ import Provider from './Provider';
|
|
|
8
8
|
import ShareInput from './ShareInput/ShareInput';
|
|
9
9
|
import Stats from './Stats/Stats';
|
|
10
10
|
import { AccessRightInitialValues, Endpoints, Labels } from './models';
|
|
11
|
+
import { useAccessRightsChange } from './useAccessRightsChange';
|
|
11
12
|
import { useAccessRightsInitValues } from './useAccessRightsInitValues';
|
|
12
13
|
|
|
13
14
|
interface Props {
|
|
14
|
-
cancel
|
|
15
|
+
cancel?: ({ dirty, values }) => void;
|
|
15
16
|
endpoints: Endpoints;
|
|
16
17
|
initialValues: Array<AccessRightInitialValues>;
|
|
17
18
|
isSubmitting?: boolean;
|
|
18
19
|
labels: Labels;
|
|
19
|
-
link?: string;
|
|
20
20
|
loading?: boolean;
|
|
21
|
+
onChange?: (values: Array<AccessRightInitialValues>) => void;
|
|
21
22
|
roles: Array<SelectEntry>;
|
|
22
23
|
submit: (values: Array<AccessRightInitialValues>) => Promise<void>;
|
|
23
24
|
}
|
|
@@ -28,13 +29,14 @@ export const AccessRights = ({
|
|
|
28
29
|
endpoints,
|
|
29
30
|
submit,
|
|
30
31
|
cancel,
|
|
31
|
-
link,
|
|
32
32
|
loading,
|
|
33
33
|
labels,
|
|
34
|
-
isSubmitting
|
|
34
|
+
isSubmitting,
|
|
35
|
+
onChange
|
|
35
36
|
}: Props): JSX.Element => {
|
|
36
37
|
const { classes } = useAccessRightsStyles();
|
|
37
38
|
const clear = useAccessRightsInitValues({ initialValues });
|
|
39
|
+
useAccessRightsChange(onChange);
|
|
38
40
|
|
|
39
41
|
return (
|
|
40
42
|
<div className={classes.container}>
|
|
@@ -46,7 +48,6 @@ export const AccessRights = ({
|
|
|
46
48
|
clear={clear}
|
|
47
49
|
isSubmitting={isSubmitting}
|
|
48
50
|
labels={labels.actions}
|
|
49
|
-
link={link}
|
|
50
51
|
submit={submit}
|
|
51
52
|
/>
|
|
52
53
|
</div>
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { makeStyles } from 'tss-react/mui';
|
|
2
2
|
|
|
3
3
|
export const useActionsStyles = makeStyles()((theme) => ({
|
|
4
|
-
actions: {
|
|
5
|
-
backgroundColor: theme.palette.background.paper,
|
|
6
|
-
display: 'flex',
|
|
7
|
-
justifyContent: 'space-between'
|
|
8
|
-
},
|
|
9
4
|
cancelAndSave: {
|
|
10
5
|
display: 'flex',
|
|
11
|
-
|
|
12
|
-
gap: theme.spacing(2)
|
|
6
|
+
flexDirection: 'row',
|
|
7
|
+
gap: theme.spacing(2),
|
|
8
|
+
justifyContent: 'flex-end'
|
|
13
9
|
}
|
|
14
10
|
}));
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useTranslation } from 'react-i18next';
|
|
2
2
|
|
|
3
|
-
import LinkIcon from '@mui/icons-material/Link';
|
|
4
3
|
import { CircularProgress } from '@mui/material';
|
|
5
4
|
|
|
6
5
|
import { Button } from '../../..';
|
|
@@ -10,11 +9,10 @@ import { useActions } from './useActions';
|
|
|
10
9
|
import { useActionsStyles } from './Actions.styles';
|
|
11
10
|
|
|
12
11
|
interface Props {
|
|
13
|
-
cancel
|
|
12
|
+
cancel?: ({ dirty, values }) => void;
|
|
14
13
|
clear: () => void;
|
|
15
14
|
isSubmitting?: boolean;
|
|
16
15
|
labels: Labels['actions'];
|
|
17
|
-
link?: string;
|
|
18
16
|
submit: (values: Array<AccessRightInitialValues>) => Promise<void>;
|
|
19
17
|
}
|
|
20
18
|
|
|
@@ -22,17 +20,15 @@ const Actions = ({
|
|
|
22
20
|
labels,
|
|
23
21
|
cancel,
|
|
24
22
|
submit,
|
|
25
|
-
link,
|
|
26
23
|
isSubmitting,
|
|
27
24
|
clear
|
|
28
25
|
}: Props): JSX.Element => {
|
|
29
26
|
const { t } = useTranslation();
|
|
30
27
|
const { classes } = useActionsStyles();
|
|
31
28
|
|
|
32
|
-
const { dirty,
|
|
29
|
+
const { dirty, save, formattedValues } = useActions({
|
|
33
30
|
clear,
|
|
34
31
|
labels,
|
|
35
|
-
link,
|
|
36
32
|
submit
|
|
37
33
|
});
|
|
38
34
|
|
|
@@ -41,21 +37,8 @@ const Actions = ({
|
|
|
41
37
|
};
|
|
42
38
|
|
|
43
39
|
return (
|
|
44
|
-
<div className={classes.
|
|
45
|
-
{
|
|
46
|
-
<Button
|
|
47
|
-
aria-label={t(labels.copyLink)}
|
|
48
|
-
icon={<LinkIcon />}
|
|
49
|
-
iconVariant="start"
|
|
50
|
-
variant="ghost"
|
|
51
|
-
onClick={copyLink}
|
|
52
|
-
>
|
|
53
|
-
{t(labels.copyLink)}
|
|
54
|
-
</Button>
|
|
55
|
-
) : (
|
|
56
|
-
<div />
|
|
57
|
-
)}
|
|
58
|
-
<div className={classes.cancelAndSave}>
|
|
40
|
+
<div className={classes.cancelAndSave}>
|
|
41
|
+
{cancel && (
|
|
59
42
|
<Button
|
|
60
43
|
aria-label={t(labels.cancel)}
|
|
61
44
|
variant="secondary"
|
|
@@ -63,17 +46,17 @@ const Actions = ({
|
|
|
63
46
|
>
|
|
64
47
|
{t(labels.cancel)}
|
|
65
48
|
</Button>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
</
|
|
49
|
+
)}
|
|
50
|
+
<Button
|
|
51
|
+
aria-label={t(labels.save)}
|
|
52
|
+
disabled={isSubmitting || !dirty}
|
|
53
|
+
icon={isSubmitting ? <CircularProgress size={24} /> : null}
|
|
54
|
+
iconVariant={isSubmitting ? 'start' : 'none'}
|
|
55
|
+
variant="primary"
|
|
56
|
+
onClick={save}
|
|
57
|
+
>
|
|
58
|
+
{t(labels.save)}
|
|
59
|
+
</Button>
|
|
77
60
|
</div>
|
|
78
61
|
);
|
|
79
62
|
};
|