@centreon/ui 24.5.1 → 24.5.2
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 +3 -7
- package/public/mockServiceWorker.js +100 -81
- package/src/ActionsList/index.stories.tsx +1 -7
- package/src/Dashboard/Dashboard.styles.ts +2 -3
- package/src/Dashboard/Item.tsx +2 -11
- package/src/Dashboard/Layout.tsx +2 -4
- package/src/Graph/Tree/DescendantNodes.tsx +0 -1
- package/src/Graph/Tree/Links.tsx +2 -15
- package/src/Graph/Tree/Tree.cypress.spec.tsx +0 -24
- package/src/Graph/Tree/Tree.stories.tsx +1 -17
- package/src/Graph/Tree/models.ts +0 -3
- package/src/TopCounterElements/TopCounterLayout.tsx +4 -3
- package/src/TopCounterElements/useCloseOnLegacyPage.tsx +6 -9
- package/src/api/QueryProvider.tsx +1 -1
- package/src/api/useFetchQuery/index.test.ts +5 -0
- package/src/api/useFetchQuery/index.ts +5 -2
- package/src/api/useGraphQuery/index.ts +1 -7
- package/src/api/useMutationQuery/index.ts +5 -2
- package/src/api/useRequest/index.test.ts +3 -0
- package/src/api/useRequest/index.ts +6 -3
- package/src/components/Form/AccessRights/AccessRights.cypress.spec.tsx +13 -27
- package/src/components/Form/AccessRights/AccessRights.stories.tsx +19 -0
- package/src/components/Form/AccessRights/AccessRights.styles.ts +1 -1
- package/src/components/Form/AccessRights/AccessRights.tsx +5 -6
- package/src/components/Form/AccessRights/Actions/Actions.styles.ts +7 -3
- package/src/components/Form/AccessRights/Actions/Actions.tsx +32 -15
- package/src/components/Form/AccessRights/Actions/useActions.ts +37 -4
- package/src/components/Form/AccessRights/models.ts +3 -0
- package/src/components/Form/AccessRights/storiesData.ts +3 -0
- package/src/components/List/Item/ListItem.styles.ts +2 -2
- package/src/components/Zoom/Minimap.tsx +2 -4
- package/src/components/Zoom/Zoom.cypress.spec.tsx +13 -13
- package/src/components/Zoom/Zoom.tsx +1 -4
- package/src/components/Zoom/ZoomContent.tsx +2 -5
- package/src/components/index.ts +0 -1
- package/src/index.ts +1 -1
- package/src/utils/index.ts +0 -1
- package/src/utils/usePluralizedTranslation.ts +3 -20
- package/src/api/logger.ts +0 -11
- package/src/components/Form/AccessRights/useAccessRightsChange.ts +0 -30
- package/src/components/Form/AccessRights/utils.ts +0 -18
- package/src/components/Tabs/Tab.styles.ts +0 -25
- package/src/components/Tabs/TabPanel.tsx +0 -22
- package/src/components/Tabs/Tabs.cypress.spec.tsx +0 -70
- package/src/components/Tabs/Tabs.stories.tsx +0 -55
- package/src/components/Tabs/Tabs.tsx +0 -55
- package/src/components/Tabs/index.ts +0 -6
- package/src/utils/resourcesStatusURL.ts +0 -166
- package/src/utils/usePluralizedTranslation.test.ts +0 -159
|
@@ -24,7 +24,6 @@ interface CustomTimePeriod {
|
|
|
24
24
|
interface UseMetricsQueryProps {
|
|
25
25
|
baseEndpoint: string;
|
|
26
26
|
bypassMetricsExclusion?: boolean;
|
|
27
|
-
bypassQueryParams?: boolean;
|
|
28
27
|
includeAllResources?: boolean;
|
|
29
28
|
metrics: Array<Metric>;
|
|
30
29
|
refreshCount?: number;
|
|
@@ -91,8 +90,7 @@ const useGraphQuery = ({
|
|
|
91
90
|
timePeriodType: 1
|
|
92
91
|
},
|
|
93
92
|
refreshInterval = false,
|
|
94
|
-
refreshCount
|
|
95
|
-
bypassQueryParams = false
|
|
93
|
+
refreshCount
|
|
96
94
|
}: UseMetricsQueryProps): UseMetricsQueryState => {
|
|
97
95
|
const timePeriodToUse = equals(timePeriod?.timePeriodType, -1)
|
|
98
96
|
? {
|
|
@@ -116,10 +114,6 @@ const useGraphQuery = ({
|
|
|
116
114
|
isLoading
|
|
117
115
|
} = useFetchQuery<PerformanceGraphData>({
|
|
118
116
|
getEndpoint: () => {
|
|
119
|
-
if (bypassQueryParams) {
|
|
120
|
-
return baseEndpoint;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
117
|
const endpoint = buildListingEndpoint({
|
|
124
118
|
baseEndpoint,
|
|
125
119
|
parameters: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import 'ulog';
|
|
1
2
|
import { useEffect } from 'react';
|
|
2
3
|
|
|
3
4
|
import {
|
|
@@ -6,12 +7,12 @@ import {
|
|
|
6
7
|
UseMutationResult
|
|
7
8
|
} from '@tanstack/react-query';
|
|
8
9
|
import { JsonDecoder } from 'ts.data.json';
|
|
10
|
+
import anylogger from 'anylogger';
|
|
9
11
|
import { includes, omit } from 'ramda';
|
|
10
12
|
|
|
11
13
|
import { CatchErrorProps, customFetch, ResponseError } from '../customFetch';
|
|
12
14
|
import useSnackbar from '../../Snackbar/useSnackbar';
|
|
13
15
|
import { useDeepCompare } from '../../utils';
|
|
14
|
-
import { errorLog } from '../logger';
|
|
15
16
|
|
|
16
17
|
export enum Method {
|
|
17
18
|
DELETE = 'DELETE',
|
|
@@ -51,6 +52,8 @@ export type UseMutationQueryProps<T, TMeta> = {
|
|
|
51
52
|
'mutationFn' | 'onError' | 'onMutate' | 'onSuccess' | 'mutateAsync' | 'mutate'
|
|
52
53
|
>;
|
|
53
54
|
|
|
55
|
+
const log = anylogger('API Request');
|
|
56
|
+
|
|
54
57
|
export type UseMutationQueryState<T, TMeta> = Omit<
|
|
55
58
|
UseMutationResult<T | ResponseError>,
|
|
56
59
|
'isError' | 'mutate' | 'mutateAsync'
|
|
@@ -124,7 +127,7 @@ const useMutationQuery = <T extends object, TMeta>({
|
|
|
124
127
|
const manageError = (): void => {
|
|
125
128
|
const data = queryData.data as ResponseError | undefined;
|
|
126
129
|
if (data?.isError) {
|
|
127
|
-
|
|
130
|
+
log.error(data.message);
|
|
128
131
|
const hasACorrespondingHttpCode = includes(
|
|
129
132
|
data?.statusCode || 0,
|
|
130
133
|
httpCodesBypassErrorSnackbar
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
+
import anyLogger from 'anylogger';
|
|
2
3
|
import { RenderHookResult, renderHook, act } from '@testing-library/react';
|
|
3
4
|
|
|
4
5
|
import useRequest, { RequestResult, RequestParams } from '.';
|
|
@@ -76,6 +77,8 @@ describe(useRequest, () => {
|
|
|
76
77
|
});
|
|
77
78
|
});
|
|
78
79
|
|
|
80
|
+
expect(anyLogger().error).toHaveBeenCalledWith(response);
|
|
81
|
+
|
|
79
82
|
expect(mockedShowErrorMessage).toHaveBeenCalledWith('failure');
|
|
80
83
|
});
|
|
81
84
|
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react';
|
|
2
2
|
|
|
3
|
+
import 'ulog';
|
|
3
4
|
import axios from 'axios';
|
|
4
5
|
import { pathOr, defaultTo, path, includes, or } from 'ramda';
|
|
6
|
+
import anylogger from 'anylogger';
|
|
5
7
|
import { JsonDecoder } from 'ts.data.json';
|
|
6
8
|
|
|
7
9
|
import useCancelTokenSource from '../useCancelTokenSource';
|
|
8
10
|
import useSnackbar from '../../Snackbar/useSnackbar';
|
|
9
|
-
|
|
11
|
+
|
|
12
|
+
const log = anylogger('API Request');
|
|
10
13
|
|
|
11
14
|
export interface RequestParams<TResult> {
|
|
12
15
|
decoder?: JsonDecoder.Decoder<TResult>;
|
|
@@ -38,7 +41,7 @@ const useRequest = <TResult>({
|
|
|
38
41
|
}, []);
|
|
39
42
|
|
|
40
43
|
const showRequestErrorMessage = (error): void => {
|
|
41
|
-
|
|
44
|
+
log.error(error);
|
|
42
45
|
|
|
43
46
|
const message = or(
|
|
44
47
|
pathOr(undefined, ['response', 'data', 'message'], error),
|
|
@@ -65,7 +68,7 @@ const useRequest = <TResult>({
|
|
|
65
68
|
.catch((error) => {
|
|
66
69
|
setSending(false);
|
|
67
70
|
if (axios.isCancel(error)) {
|
|
68
|
-
|
|
71
|
+
log.warn(error);
|
|
69
72
|
|
|
70
73
|
throw error;
|
|
71
74
|
}
|
|
@@ -15,11 +15,11 @@ import {
|
|
|
15
15
|
|
|
16
16
|
const initialize = ({
|
|
17
17
|
initialValues = simpleAccessRights,
|
|
18
|
-
loading = false
|
|
18
|
+
loading = false,
|
|
19
|
+
link = 'link'
|
|
19
20
|
}): unknown => {
|
|
20
21
|
const cancel = cy.stub();
|
|
21
22
|
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}
|
|
50
51
|
loading={loading}
|
|
51
52
|
roles={roles}
|
|
52
53
|
submit={save}
|
|
53
|
-
onChange={change}
|
|
54
54
|
/>
|
|
55
55
|
</Provider>
|
|
56
56
|
</TestQueryProvider>
|
|
@@ -60,7 +60,6 @@ const initialize = ({
|
|
|
60
60
|
|
|
61
61
|
return {
|
|
62
62
|
cancel,
|
|
63
|
-
change,
|
|
64
63
|
save
|
|
65
64
|
};
|
|
66
65
|
};
|
|
@@ -75,12 +74,21 @@ describe('Access rights', () => {
|
|
|
75
74
|
cy.findByLabelText('Add a contact').should('be.visible');
|
|
76
75
|
cy.findByTestId('add_role').should('be.disabled');
|
|
77
76
|
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
|
+
|
|
84
92
|
it('displays the access rights with an empty list', () => {
|
|
85
93
|
initialize({ initialValues: emptyAccessRights });
|
|
86
94
|
|
|
@@ -89,7 +97,7 @@ describe('Access rights', () => {
|
|
|
89
97
|
cy.makeSnapshot();
|
|
90
98
|
});
|
|
91
99
|
|
|
92
|
-
it('displays the access rights list', () => {
|
|
100
|
+
it('displays the access rights with an empty list', () => {
|
|
93
101
|
initialize({});
|
|
94
102
|
|
|
95
103
|
simpleAccessRights.forEach(({ name, email, isContactGroup, role }) => {
|
|
@@ -362,26 +370,4 @@ describe('Access rights', () => {
|
|
|
362
370
|
|
|
363
371
|
cy.makeSnapshot();
|
|
364
372
|
});
|
|
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
|
-
});
|
|
387
373
|
});
|
|
@@ -47,6 +47,7 @@ export const Default: Story = {
|
|
|
47
47
|
},
|
|
48
48
|
initialValues: defaultAccessRights,
|
|
49
49
|
labels,
|
|
50
|
+
link: 'link',
|
|
50
51
|
roles,
|
|
51
52
|
submit: () => undefined
|
|
52
53
|
},
|
|
@@ -62,6 +63,7 @@ export const AccessRightsWithStates: Story = {
|
|
|
62
63
|
},
|
|
63
64
|
initialValues: accessRightsWithStates,
|
|
64
65
|
labels,
|
|
66
|
+
link: 'link',
|
|
65
67
|
roles,
|
|
66
68
|
submit: () => undefined
|
|
67
69
|
},
|
|
@@ -77,6 +79,22 @@ export const withEmptyState: Story = {
|
|
|
77
79
|
},
|
|
78
80
|
initialValues: emptyAccessRights,
|
|
79
81
|
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,
|
|
80
98
|
roles,
|
|
81
99
|
submit: () => undefined
|
|
82
100
|
},
|
|
@@ -92,6 +110,7 @@ export const loading: Story = {
|
|
|
92
110
|
},
|
|
93
111
|
initialValues: emptyAccessRights,
|
|
94
112
|
labels,
|
|
113
|
+
link: 'link',
|
|
95
114
|
loading: true,
|
|
96
115
|
roles,
|
|
97
116
|
submit: () => undefined
|
|
@@ -8,17 +8,16 @@ 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';
|
|
12
11
|
import { useAccessRightsInitValues } from './useAccessRightsInitValues';
|
|
13
12
|
|
|
14
13
|
interface Props {
|
|
15
|
-
cancel
|
|
14
|
+
cancel: ({ dirty, values }) => void;
|
|
16
15
|
endpoints: Endpoints;
|
|
17
16
|
initialValues: Array<AccessRightInitialValues>;
|
|
18
17
|
isSubmitting?: boolean;
|
|
19
18
|
labels: Labels;
|
|
19
|
+
link?: string;
|
|
20
20
|
loading?: boolean;
|
|
21
|
-
onChange?: (values: Array<AccessRightInitialValues>) => void;
|
|
22
21
|
roles: Array<SelectEntry>;
|
|
23
22
|
submit: (values: Array<AccessRightInitialValues>) => Promise<void>;
|
|
24
23
|
}
|
|
@@ -29,14 +28,13 @@ export const AccessRights = ({
|
|
|
29
28
|
endpoints,
|
|
30
29
|
submit,
|
|
31
30
|
cancel,
|
|
31
|
+
link,
|
|
32
32
|
loading,
|
|
33
33
|
labels,
|
|
34
|
-
isSubmitting
|
|
35
|
-
onChange
|
|
34
|
+
isSubmitting
|
|
36
35
|
}: Props): JSX.Element => {
|
|
37
36
|
const { classes } = useAccessRightsStyles();
|
|
38
37
|
const clear = useAccessRightsInitValues({ initialValues });
|
|
39
|
-
useAccessRightsChange(onChange);
|
|
40
38
|
|
|
41
39
|
return (
|
|
42
40
|
<div className={classes.container}>
|
|
@@ -48,6 +46,7 @@ export const AccessRights = ({
|
|
|
48
46
|
clear={clear}
|
|
49
47
|
isSubmitting={isSubmitting}
|
|
50
48
|
labels={labels.actions}
|
|
49
|
+
link={link}
|
|
51
50
|
submit={submit}
|
|
52
51
|
/>
|
|
53
52
|
</div>
|
|
@@ -1,10 +1,14 @@
|
|
|
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
|
+
},
|
|
4
9
|
cancelAndSave: {
|
|
5
10
|
display: 'flex',
|
|
6
|
-
|
|
7
|
-
gap: theme.spacing(2)
|
|
8
|
-
justifyContent: 'flex-end'
|
|
11
|
+
flex: 'row',
|
|
12
|
+
gap: theme.spacing(2)
|
|
9
13
|
}
|
|
10
14
|
}));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useTranslation } from 'react-i18next';
|
|
2
2
|
|
|
3
|
+
import LinkIcon from '@mui/icons-material/Link';
|
|
3
4
|
import { CircularProgress } from '@mui/material';
|
|
4
5
|
|
|
5
6
|
import { Button } from '../../..';
|
|
@@ -9,10 +10,11 @@ import { useActions } from './useActions';
|
|
|
9
10
|
import { useActionsStyles } from './Actions.styles';
|
|
10
11
|
|
|
11
12
|
interface Props {
|
|
12
|
-
cancel
|
|
13
|
+
cancel: ({ dirty, values }) => void;
|
|
13
14
|
clear: () => void;
|
|
14
15
|
isSubmitting?: boolean;
|
|
15
16
|
labels: Labels['actions'];
|
|
17
|
+
link?: string;
|
|
16
18
|
submit: (values: Array<AccessRightInitialValues>) => Promise<void>;
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -20,15 +22,17 @@ const Actions = ({
|
|
|
20
22
|
labels,
|
|
21
23
|
cancel,
|
|
22
24
|
submit,
|
|
25
|
+
link,
|
|
23
26
|
isSubmitting,
|
|
24
27
|
clear
|
|
25
28
|
}: Props): JSX.Element => {
|
|
26
29
|
const { t } = useTranslation();
|
|
27
30
|
const { classes } = useActionsStyles();
|
|
28
31
|
|
|
29
|
-
const { dirty, save, formattedValues } = useActions({
|
|
32
|
+
const { dirty, copyLink, save, formattedValues } = useActions({
|
|
30
33
|
clear,
|
|
31
34
|
labels,
|
|
35
|
+
link,
|
|
32
36
|
submit
|
|
33
37
|
});
|
|
34
38
|
|
|
@@ -37,8 +41,21 @@ const Actions = ({
|
|
|
37
41
|
};
|
|
38
42
|
|
|
39
43
|
return (
|
|
40
|
-
<div className={classes.
|
|
41
|
-
{
|
|
44
|
+
<div className={classes.actions}>
|
|
45
|
+
{link ? (
|
|
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}>
|
|
42
59
|
<Button
|
|
43
60
|
aria-label={t(labels.cancel)}
|
|
44
61
|
variant="secondary"
|
|
@@ -46,17 +63,17 @@ const Actions = ({
|
|
|
46
63
|
>
|
|
47
64
|
{t(labels.cancel)}
|
|
48
65
|
</Button>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
</
|
|
66
|
+
<Button
|
|
67
|
+
aria-label={t(labels.save)}
|
|
68
|
+
disabled={isSubmitting || !dirty}
|
|
69
|
+
icon={isSubmitting ? <CircularProgress size={24} /> : null}
|
|
70
|
+
iconVariant={isSubmitting ? 'start' : 'none'}
|
|
71
|
+
variant="primary"
|
|
72
|
+
onClick={save}
|
|
73
|
+
>
|
|
74
|
+
{t(labels.save)}
|
|
75
|
+
</Button>
|
|
76
|
+
</div>
|
|
60
77
|
</div>
|
|
61
78
|
);
|
|
62
79
|
};
|
|
@@ -1,32 +1,64 @@
|
|
|
1
1
|
import { useAtomValue } from 'jotai';
|
|
2
|
-
import { equals } from 'ramda';
|
|
2
|
+
import { equals, omit } from 'ramda';
|
|
3
3
|
|
|
4
4
|
import { initialValuesAtom, valuesAtom } from '../atoms';
|
|
5
|
-
import { AccessRightInitialValues, Labels } from '../models';
|
|
6
|
-
import {
|
|
5
|
+
import { AccessRight, AccessRightInitialValues, Labels } from '../models';
|
|
6
|
+
import { useCopyToClipboard } from '../../../..';
|
|
7
|
+
|
|
8
|
+
const formatValue = (accessRight: AccessRight): AccessRightInitialValues => {
|
|
9
|
+
return omit(['isAdded', 'isUpdated', 'isRemoved'], accessRight);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const formatValueForSubmition = (
|
|
13
|
+
accessRight: AccessRight
|
|
14
|
+
): AccessRightInitialValues => {
|
|
15
|
+
return {
|
|
16
|
+
...formatValue(accessRight),
|
|
17
|
+
id: Number((accessRight.id as string).split('_')[1])
|
|
18
|
+
};
|
|
19
|
+
};
|
|
7
20
|
|
|
8
21
|
interface Props {
|
|
9
22
|
clear: () => void;
|
|
10
23
|
labels: Labels['actions'];
|
|
24
|
+
link?: string;
|
|
11
25
|
submit: (values: Array<AccessRightInitialValues>) => Promise<void>;
|
|
12
26
|
}
|
|
13
27
|
|
|
14
28
|
interface UseActionsState {
|
|
29
|
+
copyLink: () => void;
|
|
15
30
|
dirty: boolean;
|
|
16
31
|
formattedValues: Array<AccessRightInitialValues>;
|
|
17
32
|
save: () => void;
|
|
18
33
|
}
|
|
19
34
|
|
|
20
|
-
export const useActions = ({
|
|
35
|
+
export const useActions = ({
|
|
36
|
+
link,
|
|
37
|
+
labels,
|
|
38
|
+
submit,
|
|
39
|
+
clear
|
|
40
|
+
}: Props): UseActionsState => {
|
|
21
41
|
const values = useAtomValue(valuesAtom);
|
|
22
42
|
const initialValues = useAtomValue(initialValuesAtom);
|
|
23
43
|
|
|
44
|
+
const { copy } = useCopyToClipboard({
|
|
45
|
+
errorMessage: labels.copyError,
|
|
46
|
+
successMessage: labels.copySuccess
|
|
47
|
+
});
|
|
48
|
+
|
|
24
49
|
const formattedValues = values
|
|
25
50
|
.filter(({ isRemoved }) => !isRemoved)
|
|
26
51
|
.map(formatValue);
|
|
27
52
|
|
|
28
53
|
const dirty = !equals(initialValues, formattedValues);
|
|
29
54
|
|
|
55
|
+
const copyLink = (): void => {
|
|
56
|
+
if (!link) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
copy(link);
|
|
60
|
+
};
|
|
61
|
+
|
|
30
62
|
const save = (): void => {
|
|
31
63
|
submit(
|
|
32
64
|
values.filter(({ isRemoved }) => !isRemoved).map(formatValueForSubmition)
|
|
@@ -39,6 +71,7 @@ export const useActions = ({ submit, clear }: Props): UseActionsState => {
|
|
|
39
71
|
};
|
|
40
72
|
|
|
41
73
|
return {
|
|
74
|
+
copyLink,
|
|
42
75
|
dirty,
|
|
43
76
|
formattedValues,
|
|
44
77
|
save
|
|
@@ -105,6 +105,9 @@ export const buildResult = (isGroup): Listing<SelectEntry> => ({
|
|
|
105
105
|
export const labels: Labels = {
|
|
106
106
|
actions: {
|
|
107
107
|
cancel: 'Cancel',
|
|
108
|
+
copyError: 'Failed to copy',
|
|
109
|
+
copyLink: 'Copy link',
|
|
110
|
+
copySuccess: 'Copied',
|
|
108
111
|
save: 'Save'
|
|
109
112
|
},
|
|
110
113
|
add: {
|
|
@@ -18,10 +18,10 @@ export const useStyles = makeStyles()((theme) => ({
|
|
|
18
18
|
display: 'flex',
|
|
19
19
|
flexGrow: 1,
|
|
20
20
|
gap: theme.spacing(2),
|
|
21
|
+
maxWidth: '520px',
|
|
21
22
|
overflow: 'hidden',
|
|
22
23
|
paddingBottom: theme.spacing(1),
|
|
23
|
-
paddingTop: theme.spacing(1)
|
|
24
|
-
width: '100%'
|
|
24
|
+
paddingTop: theme.spacing(1)
|
|
25
25
|
},
|
|
26
26
|
secondary: {
|
|
27
27
|
alignItems: 'center',
|
|
@@ -16,7 +16,6 @@ interface Props extends Omit<UseMinimapProps, 'minimapScale' | 'scale'> {
|
|
|
16
16
|
left: number;
|
|
17
17
|
top: number;
|
|
18
18
|
};
|
|
19
|
-
id?: number | string;
|
|
20
19
|
isDraggingFromContainer: boolean;
|
|
21
20
|
}
|
|
22
21
|
|
|
@@ -27,8 +26,7 @@ const Minimap = ({
|
|
|
27
26
|
width,
|
|
28
27
|
contentClientRect,
|
|
29
28
|
isDraggingFromContainer,
|
|
30
|
-
diffBetweenContentAndSvg
|
|
31
|
-
id
|
|
29
|
+
diffBetweenContentAndSvg
|
|
32
30
|
}: Props): JSX.Element => {
|
|
33
31
|
const { classes } = useZoomStyles();
|
|
34
32
|
|
|
@@ -84,7 +82,7 @@ const Minimap = ({
|
|
|
84
82
|
);
|
|
85
83
|
|
|
86
84
|
return (
|
|
87
|
-
<g className={classes.minimap} clipPath=
|
|
85
|
+
<g className={classes.minimap} clipPath="url(#zoom-clip)">
|
|
88
86
|
<rect
|
|
89
87
|
className={classes.minimapBackground}
|
|
90
88
|
height={finalHeight}
|
|
@@ -63,7 +63,7 @@ describe('Zoom', () => {
|
|
|
63
63
|
it('displays the minimap when the prop is set', () => {
|
|
64
64
|
initialize({ showMinimap: true });
|
|
65
65
|
|
|
66
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
66
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
|
|
67
67
|
|
|
68
68
|
cy.makeSnapshot();
|
|
69
69
|
});
|
|
@@ -72,7 +72,7 @@ describe('Zoom', () => {
|
|
|
72
72
|
initialize({ showMinimap: true });
|
|
73
73
|
|
|
74
74
|
cy.get('g[transform="matrix(1, 0, 0, 1, 0, 0)"]');
|
|
75
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
75
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
|
|
76
76
|
|
|
77
77
|
cy.findByTestId('zoom in').click();
|
|
78
78
|
cy.findByTestId('zoom-content')
|
|
@@ -86,7 +86,7 @@ describe('Zoom', () => {
|
|
|
86
86
|
initialize({ showMinimap: true });
|
|
87
87
|
|
|
88
88
|
cy.get('g[transform="matrix(1, 0, 0, 1, 0, 0)"]');
|
|
89
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
89
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
|
|
90
90
|
|
|
91
91
|
cy.findByTestId('zoom out').click();
|
|
92
92
|
|
|
@@ -101,7 +101,7 @@ describe('Zoom', () => {
|
|
|
101
101
|
initialize({ showMinimap: true });
|
|
102
102
|
|
|
103
103
|
cy.get('g[transform="matrix(1, 0, 0, 1, 0, 0)"]');
|
|
104
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
104
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
|
|
105
105
|
|
|
106
106
|
cy.findByTestId('zoom-content').realMouseWheel({ deltaY: 20 });
|
|
107
107
|
|
|
@@ -116,7 +116,7 @@ describe('Zoom', () => {
|
|
|
116
116
|
initialize({ showMinimap: true });
|
|
117
117
|
|
|
118
118
|
cy.get('g[transform="matrix(1, 0, 0, 1, 0, 0)"]');
|
|
119
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
119
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
|
|
120
120
|
|
|
121
121
|
cy.findByTestId('zoom-content').realMouseWheel({ deltaY: -20 });
|
|
122
122
|
|
|
@@ -131,7 +131,7 @@ describe('Zoom', () => {
|
|
|
131
131
|
initialize({ showMinimap: true });
|
|
132
132
|
|
|
133
133
|
cy.get('g[transform="matrix(1, 0, 0, 1, 0, 0)"]');
|
|
134
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
134
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
|
|
135
135
|
|
|
136
136
|
cy.findByTestId('zoom-content').realMouseWheel({ deltaY: -20 });
|
|
137
137
|
|
|
@@ -150,7 +150,7 @@ describe('Zoom', () => {
|
|
|
150
150
|
initialize({ showMinimap: false });
|
|
151
151
|
|
|
152
152
|
cy.get('g[transform="matrix(1, 0, 0, 1, 0, 0)"]');
|
|
153
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
153
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('not.exist');
|
|
154
154
|
|
|
155
155
|
cy.makeSnapshot();
|
|
156
156
|
});
|
|
@@ -158,7 +158,7 @@ describe('Zoom', () => {
|
|
|
158
158
|
it('zooms in when the minimap is scrolled up', () => {
|
|
159
159
|
initialize({ showMinimap: true });
|
|
160
160
|
|
|
161
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
161
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
|
|
162
162
|
|
|
163
163
|
cy.findByTestId('minimap-interaction').realMouseWheel({ deltaY: -20 });
|
|
164
164
|
|
|
@@ -172,7 +172,7 @@ describe('Zoom', () => {
|
|
|
172
172
|
it('zooms out when the minimap is scrolled down', () => {
|
|
173
173
|
initialize({ showMinimap: true });
|
|
174
174
|
|
|
175
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
175
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
|
|
176
176
|
|
|
177
177
|
cy.findByTestId('minimap-interaction').realMouseWheel({ deltaY: 20 });
|
|
178
178
|
|
|
@@ -186,7 +186,7 @@ describe('Zoom', () => {
|
|
|
186
186
|
it('moves the view when the mouse is hover the content with the corresponding button pressed down', () => {
|
|
187
187
|
initialize({ showMinimap: true });
|
|
188
188
|
|
|
189
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
189
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
|
|
190
190
|
cy.get('svg').should('have.attr', 'height', '400');
|
|
191
191
|
|
|
192
192
|
cy.findByTestId('zoom-container')
|
|
@@ -205,7 +205,7 @@ describe('Zoom', () => {
|
|
|
205
205
|
it('displays the minimap in the bottom right when the prop to the corresponding value', () => {
|
|
206
206
|
initialize({ minimapPosition: 'bottom-right', showMinimap: true });
|
|
207
207
|
|
|
208
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
208
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
|
|
209
209
|
cy.get('svg').should('have.attr', 'height', '400');
|
|
210
210
|
|
|
211
211
|
cy.makeSnapshot();
|
|
@@ -214,7 +214,7 @@ describe('Zoom', () => {
|
|
|
214
214
|
it('applies a scale down on the minimap when the content is higher than the original height', () => {
|
|
215
215
|
initialize({ showMinimap: true, template: ContentWithMultipleShapes });
|
|
216
216
|
|
|
217
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
217
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
|
|
218
218
|
cy.get('svg').should('have.attr', 'height', '400');
|
|
219
219
|
|
|
220
220
|
cy.findByTestId('minimap-interaction')
|
|
@@ -232,7 +232,7 @@ describe('Zoom', () => {
|
|
|
232
232
|
template: ContentWithMultipleShapesWithNegativeTranslations
|
|
233
233
|
});
|
|
234
234
|
|
|
235
|
-
cy.get('g[clip-path="url(#zoom-clip
|
|
235
|
+
cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
|
|
236
236
|
cy.get('svg').should('have.attr', 'height', '400');
|
|
237
237
|
|
|
238
238
|
cy.findByTestId('minimap-interaction')
|