@centreon/ui 26.10.8 → 26.10.10
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 +6 -3
- package/public/mockServiceWorker.js +31 -8
- package/src/Dashboard/Dashboard.browser.test.tsx +107 -0
- package/src/Form/CollapsibleGroup.tsx +6 -33
- package/src/Form/Form.browser.test.tsx +553 -0
- package/src/Form/Form.stories.tsx +102 -1
- package/src/Form/Form.tsx +4 -3
- package/src/Form/Inputs/ExclusiveCheckboxGroup.browser.test.tsx +304 -0
- package/src/Form/Inputs/Grid.browser.test.tsx +186 -0
- package/src/Form/Inputs/Grid.tsx +59 -12
- package/src/Form/Inputs/PasswordInput.browser.test.tsx +80 -0
- package/src/Form/Inputs/SegmentedButtons.browser.test.tsx +197 -0
- package/src/Form/Inputs/SegmentedButtons.tsx +38 -29
- package/src/Form/Inputs/Text.tsx +13 -1
- package/src/Form/Inputs/index.tsx +10 -52
- package/src/Form/Inputs/models.ts +6 -0
- package/src/Form/Section/FormSection.browser.test.tsx +174 -0
- package/src/Form/Section/FormSection.tsx +20 -6
- package/src/Form/Section/navigateToSection.ts +26 -8
- package/src/Form/Section/useActiveSection.ts +213 -0
- package/src/InputField/Number/Number.browser.test.tsx +109 -0
- package/src/InputField/Select/Autocomplete/Connected/Multi/MultiConnectedAutocompleteField.browser.test.tsx +250 -0
- package/src/InputField/Select/Autocomplete/Connected/index.test.tsx +37 -0
- package/src/InputField/Select/Autocomplete/Connected/index.tsx +56 -45
- package/src/Listing/Cell/DataCell.tsx +0 -2
- package/src/Listing/Listing.browser.test.tsx +476 -0
- package/src/Module/Module.browser.test.tsx +145 -0
- package/src/Pagination/Pagination.browser.test.tsx +184 -0
- package/src/RichTextEditor/RichTextEditor.browser.test.tsx +567 -0
- package/src/RichTextEditor/plugins/FloatingLinkEditorPlugin.tsx +3 -1
- package/src/TimePeriods/ResolutionTimePeriod.browser.test.tsx +143 -0
- package/src/TimePeriods/TimePeriods.browser.test.tsx +468 -0
- package/src/Typography/FluidTypography/FluidTypography.browser.test.tsx +54 -0
- package/src/api/useMutationQuery/useMutationQuery.browser.test.tsx +154 -0
- package/src/components/CollapsibleItem/CollapsibleItem.browser.test.tsx +120 -0
- package/src/components/CopyCommand/CopyCommand.browser.test.tsx +287 -0
- package/src/components/CrudPage/CrudPage.browser.test.tsx +659 -0
- package/src/components/DataTable/DataTable.browser.test.tsx +257 -0
- package/src/components/Form/AccessRights/AccessRights.browser.test.tsx +527 -0
- package/src/components/Header/PageHeader/PageHeader.stories.tsx +9 -0
- package/src/components/Header/PageHeader/PageHeaderMain.tsx +4 -2
- package/src/components/Header/PageHeader/PageHeaderTitle.tsx +61 -18
- package/src/components/ItemComposition/ItemComposition.browser.test.tsx +172 -0
- package/src/components/Layout/PageLayout.browser.test.tsx +114 -0
- package/src/components/Modal/ConfirmationModal/ConfirmationModal.browser.test.tsx +237 -0
- package/src/components/SortableEntriesList/SortableEntriesList.browser.test.tsx +981 -0
- package/src/components/Tabs/Tabs.browser.test.tsx +115 -0
- package/src/components/Tabs/Tabs.tsx +7 -1
- package/src/utils/useFullscreen/Fullscreen.browser.test.tsx +176 -0
- package/src/utils/useLocale/useLocale.browser.test.tsx +49 -0
- package/src/utils/useLocaleTimezoneDate/{LocaleTimezoneDate.cypress.spec.tsx → LocaleTimezoneDate.browser.test.tsx} +24 -11
- package/src/Dashboard/Dashboard.cypress.spec.tsx +0 -71
- package/src/Form/Form.cypress.spec.tsx +0 -306
- package/src/Form/Inputs/ExclusiveCheckboxGroup.cypress.spec.tsx +0 -236
- package/src/Form/Inputs/SegmentedButtons.cypress.spec.tsx +0 -101
- package/src/InputField/Number/Number.cypress.spec.tsx +0 -89
- package/src/InputField/Select/Autocomplete/Connected/Multi/MultiConnectedAutocompleteField.cypress.spec.tsx +0 -182
- package/src/Listing/Listing.cypress.spec.tsx +0 -304
- package/src/Module/Module.cypress.spec.tsx +0 -127
- package/src/Pagination/Pagination.cypress.spec.tsx +0 -140
- package/src/RichTextEditor/RichTextEditor.cypress.spec.tsx +0 -408
- package/src/TimePeriods/ResolutionTimePeriod.cypress.spec.tsx +0 -74
- package/src/TimePeriods/TimePeriods.cypress.spec.tsx +0 -488
- package/src/Typography/FluidTypography/FluidTypography.cypress.spec.tsx +0 -27
- package/src/api/useMutationQuery/useMutationQuery.cypress.spec.tsx +0 -93
- package/src/components/CollapsibleItem/CollapsibleItem.cypress.spec.tsx +0 -76
- package/src/components/CopyCommand/CopyCommand.cypress.spec.tsx +0 -167
- package/src/components/CrudPage/CrudPage.cypress.spec.tsx +0 -561
- package/src/components/DataTable/DataTable.cypress.spec.tsx +0 -174
- package/src/components/Form/AccessRights/AccessRights.cypress.spec.tsx +0 -376
- package/src/components/ItemComposition/ItemComposition.cypress.spec.tsx +0 -112
- package/src/components/Layout/PageLayout.cypress.spec.tsx +0 -64
- package/src/components/Modal/ConfirmationModal/ConfirmationModal.cypress.spec.tsx +0 -171
- package/src/components/SortableEntriesList/SortableEntriesList.cypress.spec.tsx +0 -792
- package/src/components/Tabs/Tabs.cypress.spec.tsx +0 -69
- package/src/utils/useFullscreen/Fullscreen.cypress.spec.tsx +0 -130
- package/src/utils/useLocale/useLocale.cypress.spec.tsx +0 -40
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { rs } from '@rstest/core';
|
|
2
|
+
import { render } from '@test-setup/render';
|
|
3
|
+
import { screen } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
|
|
6
|
+
import { ItemComposition } from '.';
|
|
7
|
+
import type { Props } from './ItemComposition';
|
|
8
|
+
|
|
9
|
+
interface InitializeProps extends Partial<Props> {
|
|
10
|
+
deleteButtonHidden?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Callbacks {
|
|
14
|
+
addItem: ReturnType<typeof rs.fn>;
|
|
15
|
+
deleteItem: ReturnType<typeof rs.fn>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const initialize = ({
|
|
19
|
+
deleteButtonHidden,
|
|
20
|
+
...props
|
|
21
|
+
}: InitializeProps): Callbacks => {
|
|
22
|
+
const addItem = rs.fn();
|
|
23
|
+
const deleteItem = rs.fn();
|
|
24
|
+
|
|
25
|
+
render(
|
|
26
|
+
<ItemComposition {...props} labelAdd="Add" onAddItem={addItem}>
|
|
27
|
+
<ItemComposition.Item
|
|
28
|
+
deleteButtonHidden={deleteButtonHidden}
|
|
29
|
+
labelDelete="Delete"
|
|
30
|
+
onDeleteItem={deleteItem}
|
|
31
|
+
>
|
|
32
|
+
<div>Item 1</div>
|
|
33
|
+
</ItemComposition.Item>
|
|
34
|
+
<ItemComposition.Item
|
|
35
|
+
deleteButtonHidden={deleteButtonHidden}
|
|
36
|
+
labelDelete="Delete"
|
|
37
|
+
onDeleteItem={deleteItem}
|
|
38
|
+
>
|
|
39
|
+
<div>Item 2</div>
|
|
40
|
+
</ItemComposition.Item>
|
|
41
|
+
</ItemComposition>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
addItem,
|
|
46
|
+
deleteItem
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Both items are visible, Item 1 before Item 2.
|
|
51
|
+
const expectItemsInOrder = (): void => {
|
|
52
|
+
const item1 = screen.getByText('Item 1');
|
|
53
|
+
const item2 = screen.getByText('Item 2');
|
|
54
|
+
|
|
55
|
+
expect(item1).toBeVisible();
|
|
56
|
+
expect(item2).toBeVisible();
|
|
57
|
+
expect(
|
|
58
|
+
item1.compareDocumentPosition(item2) & Node.DOCUMENT_POSITION_FOLLOWING
|
|
59
|
+
).toBeTruthy();
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const expectAddButtonWithIcon = (): void => {
|
|
63
|
+
const addButton = screen.getByTestId('Add');
|
|
64
|
+
|
|
65
|
+
expect(addButton).toBeVisible();
|
|
66
|
+
expect(addButton).toHaveTextContent('Add');
|
|
67
|
+
expect(addButton.querySelector('[data-testid="AddIcon"]')).toBeVisible();
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
describe('ItemComposition', () => {
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
expect.hasAssertions();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('displays the component', () => {
|
|
76
|
+
initialize({});
|
|
77
|
+
|
|
78
|
+
expect(screen.getByText('Item 1')).toBeVisible();
|
|
79
|
+
expect(screen.getByText('Item 2')).toBeVisible();
|
|
80
|
+
expect(screen.getAllByTestId('Delete')).toHaveLength(2);
|
|
81
|
+
expect(screen.getByTestId('Add')).toBeEnabled();
|
|
82
|
+
|
|
83
|
+
expectItemsInOrder();
|
|
84
|
+
expectAddButtonWithIcon();
|
|
85
|
+
screen.getAllByTestId('Delete').forEach((deleteButton) => {
|
|
86
|
+
expect(deleteButton).toBeVisible();
|
|
87
|
+
});
|
|
88
|
+
expect(document.querySelector('[data-linked]')).not.toBeInTheDocument();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('displays the component with a secondary label', () => {
|
|
92
|
+
initialize({ secondaryLabel: 'Secondary label' });
|
|
93
|
+
|
|
94
|
+
expect(screen.getByText('Secondary label')).toBeVisible();
|
|
95
|
+
|
|
96
|
+
// The secondary label is on the right of the add button.
|
|
97
|
+
expectItemsInOrder();
|
|
98
|
+
expectAddButtonWithIcon();
|
|
99
|
+
expect(
|
|
100
|
+
screen.getByText('Secondary label').getBoundingClientRect().left
|
|
101
|
+
).toBeGreaterThan(screen.getByTestId('Add').getBoundingClientRect().right);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('displays add button as hidden when the prop is set to true', () => {
|
|
105
|
+
initialize({ addbuttonDisabled: true });
|
|
106
|
+
|
|
107
|
+
expect(screen.getByTestId('Add')).toBeDisabled();
|
|
108
|
+
|
|
109
|
+
expectItemsInOrder();
|
|
110
|
+
expect(screen.getByTestId('Add')).toBeVisible();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('does not display the add button when the prop is set to true', () => {
|
|
114
|
+
initialize({ addButtonHidden: true });
|
|
115
|
+
|
|
116
|
+
expect(screen.queryByTestId('Add')).not.toBeInTheDocument();
|
|
117
|
+
|
|
118
|
+
expectItemsInOrder();
|
|
119
|
+
screen.getAllByTestId('Delete').forEach((deleteButton) => {
|
|
120
|
+
expect(deleteButton).toBeVisible();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('does not display the delete button when the prop is set to true', () => {
|
|
125
|
+
initialize({ deleteButtonHidden: true });
|
|
126
|
+
|
|
127
|
+
// The delete buttons are kept in the page with `display: none`: none of
|
|
128
|
+
// them is displayed.
|
|
129
|
+
const deleteButtons = screen.queryAllByTestId('Delete');
|
|
130
|
+
|
|
131
|
+
expect(deleteButtons).toHaveLength(2);
|
|
132
|
+
deleteButtons.forEach((deleteButton) => {
|
|
133
|
+
expect(deleteButton).not.toBeVisible();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
expectItemsInOrder();
|
|
137
|
+
expectAddButtonWithIcon();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('calls the add function when the button is clicked', async () => {
|
|
141
|
+
const { addItem } = initialize({});
|
|
142
|
+
|
|
143
|
+
await userEvent.click(screen.getByTestId('Add'));
|
|
144
|
+
|
|
145
|
+
expect(addItem).toHaveBeenCalledTimes(1);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('calls the delete function when the button is clicked', async () => {
|
|
149
|
+
const { deleteItem } = initialize({});
|
|
150
|
+
|
|
151
|
+
await userEvent.click(screen.getAllByTestId('Delete')[1]);
|
|
152
|
+
|
|
153
|
+
expect(deleteItem).toHaveBeenCalledTimes(1);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('displays items as linked when the prop is set to true', () => {
|
|
157
|
+
initialize({ displayItemsAsLinked: true });
|
|
158
|
+
|
|
159
|
+
expect(document.querySelector('[data-linked]')).toBeVisible();
|
|
160
|
+
|
|
161
|
+
// The link icon is on the left of the items.
|
|
162
|
+
expectItemsInOrder();
|
|
163
|
+
expect(screen.getByTestId('LinkIcon')).toBeVisible();
|
|
164
|
+
expect(
|
|
165
|
+
(
|
|
166
|
+
document.querySelector('[data-linked]') as HTMLElement
|
|
167
|
+
).getBoundingClientRect().right
|
|
168
|
+
).toBeLessThanOrEqual(
|
|
169
|
+
screen.getByText('Item 1').getBoundingClientRect().left
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { rs } from '@rstest/core';
|
|
2
|
+
import { render } from '@test-setup/render';
|
|
3
|
+
import { screen, within } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import { T } from 'ramda';
|
|
6
|
+
|
|
7
|
+
import { PageHeader } from '..';
|
|
8
|
+
import { PageLayout } from '.';
|
|
9
|
+
import { AreaIndicator } from './AreaIndicator';
|
|
10
|
+
|
|
11
|
+
const initialize = (): void => {
|
|
12
|
+
render(
|
|
13
|
+
<PageLayout>
|
|
14
|
+
<PageLayout.Header>
|
|
15
|
+
<PageHeader>
|
|
16
|
+
<PageHeader.Menu>
|
|
17
|
+
<PageLayout.QuickAccess
|
|
18
|
+
create={rs.fn()}
|
|
19
|
+
elements={[
|
|
20
|
+
{
|
|
21
|
+
id: 1,
|
|
22
|
+
name: 'Entity'
|
|
23
|
+
}
|
|
24
|
+
]}
|
|
25
|
+
goBack={rs.fn()}
|
|
26
|
+
isActive={T}
|
|
27
|
+
labels={{
|
|
28
|
+
create: 'Create',
|
|
29
|
+
goBack: 'Go back'
|
|
30
|
+
}}
|
|
31
|
+
navigateToElement={rs.fn()}
|
|
32
|
+
/>
|
|
33
|
+
</PageHeader.Menu>
|
|
34
|
+
<PageHeader.Main>
|
|
35
|
+
<PageHeader.Title description="Description" title="Title" />
|
|
36
|
+
</PageHeader.Main>
|
|
37
|
+
<PageHeader.Actions>Actions</PageHeader.Actions>
|
|
38
|
+
</PageHeader>
|
|
39
|
+
</PageLayout.Header>
|
|
40
|
+
<PageLayout.Body>
|
|
41
|
+
<PageLayout.Actions>
|
|
42
|
+
<AreaIndicator name="Body actions" />
|
|
43
|
+
</PageLayout.Actions>
|
|
44
|
+
<h1>Content</h1>
|
|
45
|
+
</PageLayout.Body>
|
|
46
|
+
</PageLayout>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
describe('Page layout', () => {
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
expect.hasAssertions();
|
|
53
|
+
initialize();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('displays the page layout', async () => {
|
|
57
|
+
// The header (quick access button, title, description, actions) above
|
|
58
|
+
// the body (actions area, then content), and the quick access menu closed.
|
|
59
|
+
const header = document.querySelector('#header') as HTMLElement;
|
|
60
|
+
const body = document.querySelector('#page-body') as HTMLElement;
|
|
61
|
+
|
|
62
|
+
const title = await within(header).findByRole('heading', {
|
|
63
|
+
name: 'page header title'
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(title).toBeVisible();
|
|
67
|
+
expect(title).toHaveTextContent(/^Title$/);
|
|
68
|
+
expect(within(header).getByText('Description')).toBeVisible();
|
|
69
|
+
expect(within(header).getByText('Actions')).toBeVisible();
|
|
70
|
+
expect(within(header).getByTestId('quickaccess')).toBeVisible();
|
|
71
|
+
expect(within(header).getByTestId('quickaccess')).toHaveAttribute(
|
|
72
|
+
'data-is-active',
|
|
73
|
+
'false'
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const bodyActions = within(body).getByText('Body actions');
|
|
77
|
+
const content = within(body).getByRole('heading', { name: 'Content' });
|
|
78
|
+
|
|
79
|
+
expect(bodyActions).toBeVisible();
|
|
80
|
+
expect(content).toBeVisible();
|
|
81
|
+
expect(header.getBoundingClientRect().bottom).toBeLessThanOrEqual(
|
|
82
|
+
body.getBoundingClientRect().top
|
|
83
|
+
);
|
|
84
|
+
expect(bodyActions.getBoundingClientRect().bottom).toBeLessThanOrEqual(
|
|
85
|
+
content.getBoundingClientRect().top
|
|
86
|
+
);
|
|
87
|
+
expect(screen.queryByText('Entity')).not.toBeInTheDocument();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('opens the quick access poppin when the corresponding logo is displayed', async () => {
|
|
91
|
+
await userEvent.click(await screen.findByTestId('quickaccess'));
|
|
92
|
+
|
|
93
|
+
expect(await screen.findByText('Entity')).toBeVisible();
|
|
94
|
+
expect(await screen.findByText('Create')).toBeVisible();
|
|
95
|
+
expect(await screen.findByText('Go back')).toBeVisible();
|
|
96
|
+
|
|
97
|
+
// The button shown as open, the active element selected, and "Go back"
|
|
98
|
+
// before "Create".
|
|
99
|
+
expect(screen.getByTestId('quickaccess')).toHaveAttribute(
|
|
100
|
+
'data-is-active',
|
|
101
|
+
'true'
|
|
102
|
+
);
|
|
103
|
+
expect(screen.getByRole('menuitem', { name: 'Entity' })).toHaveClass(
|
|
104
|
+
'Mui-selected'
|
|
105
|
+
);
|
|
106
|
+
expect(
|
|
107
|
+
screen.getByRole('button', { name: 'Go back' }).getBoundingClientRect()
|
|
108
|
+
.right
|
|
109
|
+
).toBeLessThanOrEqual(
|
|
110
|
+
screen.getByRole('button', { name: 'Create' }).getBoundingClientRect()
|
|
111
|
+
.left
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { Button } from '@mui/material';
|
|
2
|
+
|
|
3
|
+
import { rs } from '@rstest/core';
|
|
4
|
+
import { render } from '@test-setup/render';
|
|
5
|
+
import { screen, waitFor } from '@testing-library/react';
|
|
6
|
+
import userEvent from '@testing-library/user-event';
|
|
7
|
+
import { atom, createStore, Provider, useSetAtom } from 'jotai';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
ConfirmationModal,
|
|
11
|
+
type ConfirmationModalProps
|
|
12
|
+
} from './ConfirmationModal';
|
|
13
|
+
|
|
14
|
+
const testAtom = atom<string | null>(null);
|
|
15
|
+
|
|
16
|
+
const buttonLabel = 'Click to open modal';
|
|
17
|
+
|
|
18
|
+
const TestComponent = (args: ConfirmationModalProps<string>): JSX.Element => {
|
|
19
|
+
const setAtom = useSetAtom(testAtom);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<Button onClick={() => setAtom('John')}>{buttonLabel}</Button>
|
|
24
|
+
<ConfirmationModal<string> {...args} />
|
|
25
|
+
</>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const staticLabels = {
|
|
30
|
+
cancel: 'Cancel',
|
|
31
|
+
confirm: 'Confirm',
|
|
32
|
+
description: 'Description',
|
|
33
|
+
title: 'Title'
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const dynamicLabels = {
|
|
37
|
+
cancel: 'Cancel',
|
|
38
|
+
confirm: 'Confirm',
|
|
39
|
+
description: (data: string | null): string => `Description ${data}`,
|
|
40
|
+
title: (data: string | null): string => `Hello ${data}`
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type InitializeProps = Pick<
|
|
44
|
+
ConfirmationModalProps<string>,
|
|
45
|
+
'labels' | 'disabled' | 'hasCloseButton' | 'isDanger'
|
|
46
|
+
>;
|
|
47
|
+
|
|
48
|
+
interface InitializeResult {
|
|
49
|
+
cancel: ReturnType<typeof rs.fn>;
|
|
50
|
+
confirm: ReturnType<typeof rs.fn>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const initialize = (props: InitializeProps): InitializeResult => {
|
|
54
|
+
const store = createStore();
|
|
55
|
+
|
|
56
|
+
const cancel = rs.fn();
|
|
57
|
+
const confirm = rs.fn();
|
|
58
|
+
|
|
59
|
+
render(
|
|
60
|
+
<Provider store={store}>
|
|
61
|
+
<TestComponent
|
|
62
|
+
{...props}
|
|
63
|
+
atom={testAtom}
|
|
64
|
+
onCancel={cancel}
|
|
65
|
+
onConfirm={confirm}
|
|
66
|
+
/>
|
|
67
|
+
</Provider>
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
cancel,
|
|
72
|
+
confirm
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const openModal = async (): Promise<void> => {
|
|
77
|
+
await userEvent.click(screen.getByText(buttonLabel));
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// The open modal: the title, the description, then the Cancel and Confirm
|
|
81
|
+
// buttons, in that order.
|
|
82
|
+
const expectModalContent = async ({
|
|
83
|
+
description,
|
|
84
|
+
title
|
|
85
|
+
}: {
|
|
86
|
+
description: string;
|
|
87
|
+
title: string;
|
|
88
|
+
}): Promise<void> => {
|
|
89
|
+
const dialog = await screen.findByRole('dialog');
|
|
90
|
+
|
|
91
|
+
expect(screen.getByTestId('modal-header')).toHaveTextContent(
|
|
92
|
+
new RegExp(`^${title}$`)
|
|
93
|
+
);
|
|
94
|
+
expect(dialog).toHaveTextContent(description);
|
|
95
|
+
expect(screen.getByTestId('cancel')).toBeVisible();
|
|
96
|
+
expect(screen.getByTestId('cancel')).toHaveTextContent(/^Cancel$/);
|
|
97
|
+
expect(screen.getByTestId('confirm')).toBeVisible();
|
|
98
|
+
expect(screen.getByTestId('confirm')).toHaveTextContent(/^Confirm$/);
|
|
99
|
+
expect(
|
|
100
|
+
screen
|
|
101
|
+
.getByTestId('cancel')
|
|
102
|
+
.compareDocumentPosition(screen.getByTestId('confirm'))
|
|
103
|
+
).toBe(Node.DOCUMENT_POSITION_FOLLOWING);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const expectModalClosed = async (): Promise<void> => {
|
|
107
|
+
await waitFor(() => {
|
|
108
|
+
expect(screen.queryByText('Title')).not.toBeInTheDocument();
|
|
109
|
+
});
|
|
110
|
+
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
111
|
+
expect(screen.getByText(buttonLabel)).toBeVisible();
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
describe('Confirmation modal', () => {
|
|
115
|
+
beforeEach(() => {
|
|
116
|
+
expect.hasAssertions();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('displays the modal with static labels', async () => {
|
|
120
|
+
initialize({ labels: staticLabels });
|
|
121
|
+
|
|
122
|
+
await openModal();
|
|
123
|
+
|
|
124
|
+
expect(await screen.findByText('Title')).toBeVisible();
|
|
125
|
+
expect(screen.getByText('Description')).toBeVisible();
|
|
126
|
+
expect(screen.getByLabelText('close')).toBeVisible();
|
|
127
|
+
|
|
128
|
+
await expectModalContent({ description: 'Description', title: 'Title' });
|
|
129
|
+
expect(screen.getByTestId('confirm')).toBeEnabled();
|
|
130
|
+
expect(screen.getByTestId('confirm')).toHaveAttribute(
|
|
131
|
+
'data-is-danger',
|
|
132
|
+
'false'
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('displays the modal with dynamic labels', async () => {
|
|
137
|
+
initialize({ labels: dynamicLabels });
|
|
138
|
+
|
|
139
|
+
await openModal();
|
|
140
|
+
|
|
141
|
+
expect(await screen.findByText('Hello John')).toBeVisible();
|
|
142
|
+
expect(screen.getByText('Description John')).toBeVisible();
|
|
143
|
+
|
|
144
|
+
await expectModalContent({
|
|
145
|
+
description: 'Description John',
|
|
146
|
+
title: 'Hello John'
|
|
147
|
+
});
|
|
148
|
+
expect(screen.getByLabelText('close')).toBeVisible();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('displays the confirm button as disabled when a prop is set', async () => {
|
|
152
|
+
initialize({ disabled: true, labels: staticLabels });
|
|
153
|
+
|
|
154
|
+
await openModal();
|
|
155
|
+
|
|
156
|
+
expect(await screen.findByText('Confirm')).toBeVisible();
|
|
157
|
+
expect(screen.getByTestId('confirm')).toBeDisabled();
|
|
158
|
+
|
|
159
|
+
await expectModalContent({ description: 'Description', title: 'Title' });
|
|
160
|
+
expect(screen.getByTestId('cancel')).toBeEnabled();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('displays the confirm button as danger when a prop is set', async () => {
|
|
164
|
+
initialize({ isDanger: true, labels: staticLabels });
|
|
165
|
+
|
|
166
|
+
await openModal();
|
|
167
|
+
|
|
168
|
+
expect(await screen.findByText('Confirm')).toBeVisible();
|
|
169
|
+
expect(screen.getByTestId('confirm')).toHaveAttribute(
|
|
170
|
+
'data-is-danger',
|
|
171
|
+
'true'
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
await expectModalContent({ description: 'Description', title: 'Title' });
|
|
175
|
+
|
|
176
|
+
// A red Confirm button: its background is the theme's error colour.
|
|
177
|
+
const errorColorProbe = document.createElement('div');
|
|
178
|
+
errorColorProbe.style.backgroundColor = 'var(--color-error-main)';
|
|
179
|
+
document.body.appendChild(errorColorProbe);
|
|
180
|
+
const errorColor = getComputedStyle(errorColorProbe).backgroundColor;
|
|
181
|
+
errorColorProbe.remove();
|
|
182
|
+
|
|
183
|
+
expect(errorColor).not.toBe('rgba(0, 0, 0, 0)');
|
|
184
|
+
expect(
|
|
185
|
+
getComputedStyle(screen.getByTestId('confirm')).backgroundColor
|
|
186
|
+
).toBe(errorColor);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('displays the modal without the close button when a prop is set', async () => {
|
|
190
|
+
initialize({ hasCloseButton: false, labels: staticLabels });
|
|
191
|
+
|
|
192
|
+
await openModal();
|
|
193
|
+
|
|
194
|
+
await expectModalContent({ description: 'Description', title: 'Title' });
|
|
195
|
+
expect(screen.queryByLabelText('close')).not.toBeInTheDocument();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('closes the modal when the close button is clicked', async () => {
|
|
199
|
+
initialize({ labels: staticLabels });
|
|
200
|
+
|
|
201
|
+
await openModal();
|
|
202
|
+
|
|
203
|
+
expect(await screen.findByText('Title')).toBeVisible();
|
|
204
|
+
|
|
205
|
+
await userEvent.click(screen.getByLabelText('close'));
|
|
206
|
+
|
|
207
|
+
await expectModalClosed();
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('closes the modal when the cancel button is clicked', async () => {
|
|
211
|
+
const { cancel } = initialize({ labels: staticLabels });
|
|
212
|
+
|
|
213
|
+
await openModal();
|
|
214
|
+
|
|
215
|
+
expect(await screen.findByText('Title')).toBeVisible();
|
|
216
|
+
|
|
217
|
+
await userEvent.click(screen.getByText('Cancel'));
|
|
218
|
+
|
|
219
|
+
expect(cancel).toHaveBeenCalledWith('John');
|
|
220
|
+
|
|
221
|
+
await expectModalClosed();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('closes the modal when the confirm button is clicked', async () => {
|
|
225
|
+
const { confirm } = initialize({ labels: staticLabels });
|
|
226
|
+
|
|
227
|
+
await openModal();
|
|
228
|
+
|
|
229
|
+
expect(await screen.findByText('Title')).toBeVisible();
|
|
230
|
+
|
|
231
|
+
await userEvent.click(screen.getByText('Confirm'));
|
|
232
|
+
|
|
233
|
+
expect(confirm).toHaveBeenCalledWith('John');
|
|
234
|
+
|
|
235
|
+
await expectModalClosed();
|
|
236
|
+
});
|
|
237
|
+
});
|