@centreon/ui 23.10.1-sync-release-33038.0 → 23.10.1-sync-release-34346.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 (40) hide show
  1. package/package.json +6 -1
  2. package/public/mockServiceWorker.js +2 -2
  3. package/src/InputField/Select/Autocomplete/Connected/index.tsx +12 -3
  4. package/src/Module/LicensedModule/LicenseCheck/index.tsx +24 -9
  5. package/src/Module/LicensedModule/index.tsx +7 -1
  6. package/src/Typography/Subtitle.tsx +55 -0
  7. package/src/api/useFetchQuery/index.ts +7 -1
  8. package/src/api/useMutationQuery/index.ts +8 -1
  9. package/src/components/Form/AccessRightsV2/AccessRights.cypress.spec.tsx +350 -0
  10. package/src/components/Form/AccessRightsV2/AccessRights.stories.tsx +119 -0
  11. package/src/components/Form/AccessRightsV2/AccessRights.styles.ts +10 -0
  12. package/src/components/Form/AccessRightsV2/AccessRights.tsx +60 -0
  13. package/src/components/Form/AccessRightsV2/Actions/Actions.styles.ts +14 -0
  14. package/src/components/Form/AccessRightsV2/Actions/Actions.tsx +81 -0
  15. package/src/components/Form/AccessRightsV2/Actions/useActions.ts +79 -0
  16. package/src/components/Form/AccessRightsV2/List/Item.tsx +90 -0
  17. package/src/components/Form/AccessRightsV2/List/List.styles.tsx +45 -0
  18. package/src/components/Form/AccessRightsV2/List/List.tsx +52 -0
  19. package/src/components/Form/AccessRightsV2/List/ListItemSkeleton.tsx +18 -0
  20. package/src/components/Form/AccessRightsV2/List/ListSkeleton.tsx +22 -0
  21. package/src/components/Form/AccessRightsV2/List/RemoveAccessRight.tsx +38 -0
  22. package/src/components/Form/AccessRightsV2/List/StateChip.tsx +28 -0
  23. package/src/components/Form/AccessRightsV2/List/useItem.ts +64 -0
  24. package/src/components/Form/AccessRightsV2/Provider.tsx +15 -0
  25. package/src/components/Form/AccessRightsV2/ShareInput/ContactSwitch.tsx +42 -0
  26. package/src/components/Form/AccessRightsV2/ShareInput/ShareInput.styles.ts +9 -0
  27. package/src/components/Form/AccessRightsV2/ShareInput/ShareInput.tsx +77 -0
  28. package/src/components/Form/AccessRightsV2/ShareInput/useShareInput.tsx +109 -0
  29. package/src/components/Form/AccessRightsV2/Stats/Stats.tsx +36 -0
  30. package/src/components/Form/AccessRightsV2/atoms.ts +97 -0
  31. package/src/components/Form/AccessRightsV2/common/RoleSelectField.styles.tsx +7 -0
  32. package/src/components/Form/AccessRightsV2/common/RoleSelectField.tsx +45 -0
  33. package/src/components/Form/AccessRightsV2/models.ts +54 -0
  34. package/src/components/Form/AccessRightsV2/storiesData.ts +221 -0
  35. package/src/components/Form/AccessRightsV2/useAccessRightsInitValues.ts +51 -0
  36. package/src/components/Form/index.ts +1 -0
  37. package/src/components/List/Item/ListItem.styles.ts +6 -0
  38. package/src/components/List/Item/ListItem.tsx +3 -1
  39. package/src/components/List/Item/Text.tsx +5 -3
  40. package/src/index.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centreon/ui",
3
- "version": "23.10.1-sync-release-33038.0",
3
+ "version": "23.10.1-sync-release-34346.0",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "eslint": "eslint ./src --ext .js,.jsx,.ts,.tsx --max-warnings 0",
@@ -119,6 +119,8 @@
119
119
  "d3-array": "3.2.0",
120
120
  "humanize-duration": "^3.27.3",
121
121
  "lexical": "0.11.3",
122
+ "msw": "0.49.1",
123
+ "msw-storybook-addon": "^1.10.0",
122
124
  "notistack": "^2.0.8",
123
125
  "react-grid-layout": "^1.3.4",
124
126
  "react-html-parser": "^2.0.2",
@@ -152,5 +154,8 @@
152
154
  "jest-junit": {
153
155
  "suiteName": "jest tests",
154
156
  "output": "./junit.xml"
157
+ },
158
+ "msw": {
159
+ "workerDirectory": "public"
155
160
  }
156
161
  }
@@ -2,7 +2,7 @@
2
2
  /* tslint:disable */
3
3
 
4
4
  /**
5
- * Mock Service Worker (1.2.1).
5
+ * Mock Service Worker (0.49.1).
6
6
  * @see https://github.com/mswjs/msw
7
7
  * - Please do NOT modify this file.
8
8
  * - Please do NOT serve this file on production.
@@ -300,4 +300,4 @@ function sleep(timeMs) {
300
300
  async function respondWithMock(response) {
301
301
  await sleep(response.delay)
302
302
  return new Response(response.body, response)
303
- }
303
+ }
@@ -33,6 +33,7 @@ import useFetchQuery from '../../../../api/useFetchQuery';
33
33
  export interface ConnectedAutoCompleteFieldProps<TData> {
34
34
  allowUniqOption?: boolean;
35
35
  baseEndpoint?: string;
36
+ changeIdValue: (item: TData) => number | string;
36
37
  conditionField?: keyof SelectEntry;
37
38
  field: string;
38
39
  getEndpoint: ({ search, page }) => string;
@@ -62,6 +63,7 @@ const ConnectedAutocompleteField = (
62
63
  queryKey,
63
64
  allowUniqOption,
64
65
  baseEndpoint,
66
+ changeIdValue,
65
67
  ...props
66
68
  }: ConnectedAutoCompleteFieldProps<TData> &
67
69
  Omit<AutocompleteFieldProps, 'options'>): JSX.Element => {
@@ -238,17 +240,24 @@ const ConnectedAutocompleteField = (
238
240
 
239
241
  const moreOptions = page > 1 ? options : [];
240
242
 
243
+ const formattedList = changeIdValue
244
+ ? newOptions.result.map((item) => ({
245
+ ...item,
246
+ id: changeIdValue(item)
247
+ }))
248
+ : newOptions.result;
249
+
241
250
  if (!isEmpty(labelKey) && !isNil(labelKey)) {
242
- const list = newOptions.result.map((item) =>
251
+ const list = formattedList.map((item) =>
243
252
  renameKey({ key: labelKey, newKey: 'name', object: item })
244
253
  );
245
254
  setOptions(moreOptions.concat(list as Array<TData>));
246
255
 
247
256
  return;
248
257
  }
249
- setOptions(moreOptions.concat(newOptions.result));
258
+ setOptions(moreOptions.concat(formattedList));
250
259
 
251
- setOptions(moreOptions.concat(newOptions.result as Array<TData>));
260
+ setOptions(moreOptions.concat(formattedList as Array<TData>));
252
261
 
253
262
  const total = prop('total', newOptions.meta) || 1;
254
263
  const limit = prop('limit', newOptions.meta) || 1;
@@ -1,10 +1,7 @@
1
- import * as React from 'react';
2
-
3
- import { isNil } from 'ramda';
4
1
  import { useTranslation } from 'react-i18next';
5
2
 
6
3
  import { FallbackPage } from '../../../FallbackPage/FallbackPage';
7
- import { PageSkeleton, useFetchQuery } from '../../..';
4
+ import { MenuSkeleton, PageSkeleton, useFetchQuery } from '../../..';
8
5
  import { getModuleLicenseCheckEndpoint } from '../api';
9
6
 
10
7
  import { licenseDecoder } from './decoder';
@@ -17,17 +14,27 @@ import {
17
14
 
18
15
  export interface LicenseCheckProps {
19
16
  children: React.ReactElement;
17
+ isFederatedComponent?: boolean;
20
18
  moduleName: string;
21
19
  }
22
20
 
23
21
  interface ContentProps {
24
22
  children: React.ReactElement;
23
+ isFederatedComponent?: boolean;
25
24
  isValid: boolean;
26
25
  }
27
26
 
28
- const Content = ({ children, isValid }: ContentProps): JSX.Element => {
27
+ const Content = ({
28
+ children,
29
+ isValid,
30
+ isFederatedComponent
31
+ }: ContentProps): JSX.Element | null => {
29
32
  const { t } = useTranslation();
30
33
 
34
+ if (isFederatedComponent && !isValid) {
35
+ return null;
36
+ }
37
+
31
38
  return isValid ? (
32
39
  children
33
40
  ) : (
@@ -40,10 +47,11 @@ const Content = ({ children, isValid }: ContentProps): JSX.Element => {
40
47
  };
41
48
 
42
49
  const LicenseCheck = ({
50
+ isFederatedComponent,
43
51
  children,
44
52
  moduleName
45
53
  }: LicenseCheckProps): JSX.Element | null => {
46
- const { isError, data } = useFetchQuery<License>({
54
+ const { isError, data, isLoading } = useFetchQuery<License>({
47
55
  decoder: licenseDecoder,
48
56
  getEndpoint: () => getModuleLicenseCheckEndpoint(moduleName),
49
57
  getQueryKey: () => ['license', moduleName]
@@ -55,10 +63,17 @@ const LicenseCheck = ({
55
63
 
56
64
  const isValid = data?.success;
57
65
 
58
- return isNil(isValid) ? (
59
- <PageSkeleton />
66
+ const skeleton = isFederatedComponent ? <MenuSkeleton /> : <PageSkeleton />;
67
+
68
+ return isLoading ? (
69
+ skeleton
60
70
  ) : (
61
- <Content isValid={isValid as boolean}>{children}</Content>
71
+ <Content
72
+ isFederatedComponent={isFederatedComponent}
73
+ isValid={isValid as boolean}
74
+ >
75
+ {children}
76
+ </Content>
62
77
  );
63
78
  };
64
79
 
@@ -5,13 +5,19 @@ import LicenseCheck, { LicenseCheckProps } from './LicenseCheck';
5
5
  type Props = ModuleProps & LicenseCheckProps;
6
6
 
7
7
  const LicensedModule = ({
8
+ isFederatedComponent,
8
9
  moduleName,
9
10
  children,
10
11
  ...props
11
12
  }: Props): JSX.Element => {
12
13
  return (
13
14
  <Module {...props}>
14
- <LicenseCheck moduleName={moduleName}>{children}</LicenseCheck>
15
+ <LicenseCheck
16
+ isFederatedComponent={isFederatedComponent}
17
+ moduleName={moduleName}
18
+ >
19
+ {children}
20
+ </LicenseCheck>
15
21
  </Module>
16
22
  );
17
23
  };
@@ -0,0 +1,55 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ import { type, equals } from 'ramda';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { Typography } from '@mui/material';
7
+ import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
8
+
9
+ import { Tooltip } from '../components';
10
+
11
+ interface Props {
12
+ children: ReactNode;
13
+ secondaryLabel?: string | Array<string>;
14
+ }
15
+
16
+ const Subtitle = ({ children, secondaryLabel }: Props): JSX.Element => {
17
+ const { t } = useTranslation();
18
+
19
+ const containsSeveralSecondaryLabels = equals(type(secondaryLabel), 'Array');
20
+
21
+ return (
22
+ <Typography variant="subtitle1">
23
+ <strong>{children}</strong>
24
+ {secondaryLabel && (
25
+ <Tooltip
26
+ aria-label={
27
+ containsSeveralSecondaryLabels
28
+ ? secondaryLabel[0]
29
+ : (secondaryLabel as string)
30
+ }
31
+ followCursor={false}
32
+ label={
33
+ containsSeveralSecondaryLabels ? (
34
+ <>
35
+ {secondaryLabel.map((label) => (
36
+ <p key={label}>{t(label)}</p>
37
+ ))}
38
+ </>
39
+ ) : (
40
+ t(secondaryLabel)
41
+ )
42
+ }
43
+ placement="right"
44
+ >
45
+ <HelpOutlineIcon
46
+ color="primary"
47
+ sx={{ ml: 1, verticalAlign: 'middle' }}
48
+ />
49
+ </Tooltip>
50
+ )}
51
+ </Typography>
52
+ );
53
+ };
54
+
55
+ export default Subtitle;
@@ -21,6 +21,7 @@ export interface UseFetchQueryProps<T> {
21
21
  catchError?: (props: CatchErrorProps) => void;
22
22
  decoder?: JsonDecoder.Decoder<T>;
23
23
  defaultFailureMessage?: string;
24
+ doNotCancelCallsOnUnmount?: boolean;
24
25
  fetchHeaders?: HeadersInit;
25
26
  getEndpoint: (params?: PrefetchEndpointParams) => string;
26
27
  getQueryKey: () => QueryKey;
@@ -56,7 +57,8 @@ const useFetchQuery = <T extends object>({
56
57
  isPaginated,
57
58
  queryOptions,
58
59
  httpCodesBypassErrorSnackbar = [],
59
- baseEndpoint
60
+ baseEndpoint,
61
+ doNotCancelCallsOnUnmount = false
60
62
  }: UseFetchQueryProps<T>): UseFetchQueryState<T> => {
61
63
  const dataRef = useRef<T | undefined>(undefined);
62
64
 
@@ -168,6 +170,10 @@ const useFetchQuery = <T extends object>({
168
170
 
169
171
  useEffect(() => {
170
172
  return (): void => {
173
+ if (doNotCancelCallsOnUnmount) {
174
+ return;
175
+ }
176
+
171
177
  queryClient.cancelQueries(getQueryKey());
172
178
  };
173
179
  }, []);
@@ -81,7 +81,14 @@ const useMutationQuery = <T extends object, TMeta>({
81
81
  {
82
82
  onError,
83
83
  onMutate,
84
- onSuccess
84
+ onSuccess: (data, variables, context) => {
85
+ if (data?.isError) {
86
+ onError?.(data, variables, context);
87
+
88
+ return;
89
+ }
90
+ onSuccess?.(data, variables, context);
91
+ }
85
92
  }
86
93
  );
87
94
 
@@ -0,0 +1,350 @@
1
+ import { Provider, createStore } from 'jotai';
2
+
3
+ import { Method, SnackbarProvider, TestQueryProvider } from '../../..';
4
+
5
+ import { AccessRights } from './AccessRights';
6
+ import {
7
+ buildResult,
8
+ emptyAccessRights,
9
+ labels,
10
+ removedAccessRights,
11
+ roles,
12
+ simpleAccessRights,
13
+ updatedAccessRights
14
+ } from './storiesData';
15
+
16
+ const initialize = ({
17
+ initialValues = simpleAccessRights,
18
+ loading = false,
19
+ link = 'link'
20
+ }): unknown => {
21
+ const cancel = cy.stub();
22
+ const save = cy.stub();
23
+
24
+ cy.interceptAPIRequest({
25
+ alias: 'getContacts',
26
+ method: Method.GET,
27
+ path: '**/contacts?**',
28
+ response: buildResult(false)
29
+ });
30
+ cy.interceptAPIRequest({
31
+ alias: 'getContactGroups',
32
+ method: Method.GET,
33
+ path: '**/contact-groups?**',
34
+ response: buildResult(true)
35
+ });
36
+
37
+ cy.mount({
38
+ Component: (
39
+ <SnackbarProvider>
40
+ <TestQueryProvider>
41
+ <Provider store={createStore()}>
42
+ <AccessRights
43
+ cancel={cancel}
44
+ endpoints={{
45
+ contact: '/contacts',
46
+ contactGroup: '/contact-groups'
47
+ }}
48
+ initialValues={initialValues}
49
+ labels={labels}
50
+ link={link}
51
+ loading={loading}
52
+ roles={roles}
53
+ submit={save}
54
+ />
55
+ </Provider>
56
+ </TestQueryProvider>
57
+ </SnackbarProvider>
58
+ )
59
+ });
60
+
61
+ return {
62
+ cancel,
63
+ save
64
+ };
65
+ };
66
+
67
+ describe('Access rights', () => {
68
+ it('displays the access rights in loading state', () => {
69
+ initialize({ loading: true });
70
+
71
+ cy.contains('Share dashboard with').should('be.visible');
72
+ cy.contains('Contact').should('be.visible');
73
+ cy.contains('Contact group').should('be.visible');
74
+ cy.findByLabelText('Add a contact').should('be.visible');
75
+ cy.findByTestId('add_role').should('be.disabled');
76
+ cy.findByTestId('add').should('be.disabled');
77
+ cy.findByLabelText('Copy link').should('be.visible');
78
+ cy.findByLabelText('Cancel').should('be.visible');
79
+ cy.findByLabelText('Save').should('be.visible');
80
+
81
+ cy.makeSnapshot();
82
+ });
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
+ it('displays the access rights with an empty list', () => {
93
+ initialize({ initialValues: emptyAccessRights });
94
+
95
+ cy.contains('The contact list is empty').should('be.visible');
96
+
97
+ cy.makeSnapshot();
98
+ });
99
+
100
+ it('displays the access rights with an empty list', () => {
101
+ initialize({});
102
+
103
+ simpleAccessRights.forEach(({ name, email, isContactGroup, role }) => {
104
+ cy.contains(name).should('be.visible');
105
+ cy.contains(email).should('be.visible');
106
+ cy.get(`[data-groupChip="${name}"]`).should(
107
+ isContactGroup ? 'be.visible' : 'not.exist'
108
+ );
109
+ cy.findByTestId(`role-${name}`).should('have.value', role);
110
+ cy.findByTestId(`remove-${name}`).should('be.visible');
111
+ });
112
+
113
+ cy.makeSnapshot();
114
+ });
115
+
116
+ it('displays a removed chip when the corresponding icon is clicked', () => {
117
+ initialize({});
118
+
119
+ cy.findByTestId(`remove-Leah McGlynn`).should(
120
+ 'have.attr',
121
+ 'data-removed',
122
+ 'false'
123
+ );
124
+
125
+ cy.findByTestId(`remove-Leah McGlynn`).click();
126
+
127
+ cy.findByTestId(`remove-Leah McGlynn`).should(
128
+ 'have.attr',
129
+ 'data-removed',
130
+ 'true'
131
+ );
132
+ cy.contains(labels.list.removed).should('be.visible');
133
+
134
+ cy.makeSnapshot();
135
+ });
136
+
137
+ it('restores the contact when the contact is removed and the corresponding icon is clicked', () => {
138
+ initialize({});
139
+
140
+ cy.findByTestId(`remove-Leah McGlynn`).click();
141
+
142
+ cy.findByTestId(`remove-Leah McGlynn`).should(
143
+ 'have.attr',
144
+ 'data-removed',
145
+ 'true'
146
+ );
147
+ cy.contains(labels.list.removed).should('be.visible');
148
+
149
+ cy.findByTestId(`remove-Leah McGlynn`).click();
150
+
151
+ cy.findByTestId(`remove-Leah McGlynn`).should(
152
+ 'have.attr',
153
+ 'data-removed',
154
+ 'false'
155
+ );
156
+ cy.contains(labels.list.removed).should('not.exist');
157
+
158
+ cy.makeSnapshot();
159
+ });
160
+
161
+ it('submits the new acces rights list without the removed contact', () => {
162
+ const { save } = initialize({});
163
+
164
+ cy.findByTestId(`remove-Leah McGlynn`).click();
165
+
166
+ cy.findByTestId(`remove-Leah McGlynn`).should(
167
+ 'have.attr',
168
+ 'data-removed',
169
+ 'true'
170
+ );
171
+ cy.contains(labels.list.removed).should('be.visible');
172
+ cy.contains('1 removed').should('be.visible');
173
+
174
+ cy.contains(labels.actions.save)
175
+ .click()
176
+ .then(() => {
177
+ expect(save).to.have.been.calledWith(removedAccessRights);
178
+ });
179
+
180
+ cy.makeSnapshot();
181
+ });
182
+
183
+ it('submits the new acces rights list with the updated contact', () => {
184
+ const { save } = initialize({});
185
+
186
+ cy.findByTestId(`role-Leah McGlynn`).parent().click();
187
+
188
+ cy.get('li[data-value="editor"]').click();
189
+ cy.contains(labels.list.updated).should('be.visible');
190
+ cy.contains('1 updated').should('be.visible');
191
+
192
+ cy.contains(labels.actions.save)
193
+ .click()
194
+ .then(() => {
195
+ expect(save).to.have.been.calledWith(updatedAccessRights);
196
+ });
197
+
198
+ cy.makeSnapshot();
199
+ });
200
+
201
+ it('removes the updated chip when the contact role is updated and its initial role is assigned back', () => {
202
+ initialize({});
203
+
204
+ cy.findByTestId(`role-Leah McGlynn`).parent().click();
205
+
206
+ cy.get('li[data-value="editor"]').click();
207
+ cy.contains(labels.list.updated).should('be.visible');
208
+
209
+ cy.get('li[data-value="viewer"]').click();
210
+ cy.contains(labels.list.updated).should('not.exist');
211
+
212
+ cy.makeSnapshot();
213
+ });
214
+
215
+ it('retrieves contacts when the contact radio is selected', () => {
216
+ initialize({});
217
+
218
+ cy.contains(labels.add.contact).click();
219
+ cy.findByLabelText(labels.add.autocompleteContact).click();
220
+
221
+ cy.waitForRequest('@getContacts');
222
+
223
+ cy.makeSnapshot();
224
+ });
225
+
226
+ it('retrieves contact groups when the contact group radio is selected', () => {
227
+ initialize({});
228
+
229
+ cy.contains(labels.add.contactGroup).click();
230
+ cy.findByLabelText(labels.add.autocompleteContactGroup).click();
231
+
232
+ cy.waitForRequest('@getContactGroups');
233
+
234
+ cy.makeSnapshot();
235
+ });
236
+
237
+ it('adds the contact as viewer when a contact is selected and the corresponding button is clicked', () => {
238
+ initialize({});
239
+
240
+ cy.contains(labels.add.contact).click();
241
+ cy.findByLabelText(labels.add.autocompleteContact).click();
242
+
243
+ cy.waitForRequest('@getContacts');
244
+
245
+ cy.contains('Entity 10').click();
246
+
247
+ cy.findByTestId('add').click();
248
+
249
+ cy.contains('Entity 10').should('be.visible');
250
+
251
+ cy.findByTestId('role-Entity 10').should('have.value', 'viewer');
252
+ cy.contains(labels.list.added).should('be.visible');
253
+
254
+ cy.makeSnapshot();
255
+ });
256
+
257
+ it('adds the contact group as editor when a contact group is selected and the corresponding button is clicked', () => {
258
+ initialize({});
259
+
260
+ cy.contains(labels.add.contactGroup).click();
261
+ cy.findByLabelText(labels.add.autocompleteContactGroup).click();
262
+
263
+ cy.waitForRequest('@getContactGroups');
264
+
265
+ cy.contains('Entity Group 10').click();
266
+ cy.findByTestId('add_role').parent().click();
267
+ cy.get('li[data-value="editor"]').click();
268
+ cy.findByTestId('add').click();
269
+
270
+ cy.contains('Entity Group 10').should('be.visible');
271
+
272
+ cy.findByTestId('role-Entity Group 10').should('have.value', 'editor');
273
+ cy.contains(labels.list.added).should('be.visible');
274
+ cy.contains('1 added').should('be.visible');
275
+
276
+ cy.makeSnapshot();
277
+ });
278
+
279
+ it('displays statistics when a contact is added, a contact is updated and a contact is removed', () => {
280
+ initialize({});
281
+
282
+ cy.contains(labels.add.contactGroup).click();
283
+ cy.findByLabelText(labels.add.autocompleteContactGroup).click();
284
+
285
+ cy.waitForRequest('@getContactGroups');
286
+
287
+ cy.contains('Entity Group 10').click();
288
+ cy.findByTestId('add_role').parent().click();
289
+ cy.get('li[data-value="editor"]').click();
290
+ cy.findByTestId('add').click();
291
+
292
+ cy.contains('Entity Group 10').should('be.visible');
293
+
294
+ cy.findByTestId(`role-Leah McGlynn`).parent().click();
295
+ cy.get('li[data-value="editor"]').click();
296
+
297
+ cy.findByTestId('remove-Jody Blanda').click();
298
+
299
+ cy.contains('1 added | 1 updated | 1 removed').should('be.visible');
300
+
301
+ cy.makeSnapshot();
302
+ });
303
+
304
+ it('clears the autocomplete when a contact is selected and the contact type is changed', () => {
305
+ initialize({});
306
+
307
+ cy.contains(labels.add.contact).click();
308
+ cy.findByLabelText(labels.add.autocompleteContact).click();
309
+
310
+ cy.waitForRequest('@getContacts');
311
+
312
+ cy.contains('Entity 10').click();
313
+ cy.findByLabelText(labels.add.autocompleteContact).should(
314
+ 'have.value',
315
+ 'Entity 10'
316
+ );
317
+
318
+ cy.contains(labels.add.contactGroup).click();
319
+ cy.findByLabelText(labels.add.autocompleteContactGroup).should(
320
+ 'have.value',
321
+ ''
322
+ );
323
+
324
+ cy.makeSnapshot();
325
+ });
326
+
327
+ it('removes the contact from the list when the contact has been to the list and the corresponding button is clicked', () => {
328
+ initialize({});
329
+
330
+ cy.contains(labels.add.contact).click();
331
+ cy.findByLabelText(labels.add.autocompleteContact).click();
332
+
333
+ cy.waitForRequest('@getContacts');
334
+
335
+ cy.contains('Entity 10').click();
336
+
337
+ cy.findByTestId('add').click();
338
+
339
+ cy.contains('Entity 10').should('be.visible');
340
+
341
+ cy.contains(labels.list.added).should('be.visible');
342
+
343
+ cy.findByTestId('remove-Entity 10').click();
344
+
345
+ cy.contains('Entity 10').should('not.exist');
346
+ cy.contains(labels.list.added).should('not.exist');
347
+
348
+ cy.makeSnapshot();
349
+ });
350
+ });