@khanacademy/wonder-blocks-modal 3.0.2 → 3.0.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/CHANGELOG.md +14 -0
- package/package.json +8 -8
- package/src/components/__tests__/close-button.test.js +7 -5
- package/src/components/__tests__/modal-backdrop.test.js +45 -83
- package/src/components/__tests__/modal-header.test.js +20 -21
- package/src/components/__tests__/modal-launcher.test.js +121 -137
- package/src/util/maybe-get-portal-mounted-modal-host-element.test.js +13 -14
- package/dist/index.js +0 -3013
- package/dist/index.js.flow +0 -2
- package/docs.md +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-modal
|
|
2
2
|
|
|
3
|
+
## 3.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [b561425a]
|
|
8
|
+
- Updated dependencies [a566e232]
|
|
9
|
+
- Updated dependencies [d2b21a6e]
|
|
10
|
+
- @khanacademy/wonder-blocks-core@4.6.0
|
|
11
|
+
- @khanacademy/wonder-blocks-breadcrumbs@1.0.35
|
|
12
|
+
- @khanacademy/wonder-blocks-icon@1.2.33
|
|
13
|
+
- @khanacademy/wonder-blocks-icon-button@3.4.17
|
|
14
|
+
- @khanacademy/wonder-blocks-layout@1.4.13
|
|
15
|
+
- @khanacademy/wonder-blocks-typography@1.1.35
|
|
16
|
+
|
|
3
17
|
## 3.0.2
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-modal",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"design": "v2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@babel/runtime": "^7.18.6",
|
|
19
|
-
"@khanacademy/wonder-blocks-breadcrumbs": "^1.0.
|
|
19
|
+
"@khanacademy/wonder-blocks-breadcrumbs": "^1.0.35",
|
|
20
20
|
"@khanacademy/wonder-blocks-color": "^1.2.0",
|
|
21
|
-
"@khanacademy/wonder-blocks-core": "^4.
|
|
22
|
-
"@khanacademy/wonder-blocks-icon": "^1.2.
|
|
23
|
-
"@khanacademy/wonder-blocks-icon-button": "^3.4.
|
|
24
|
-
"@khanacademy/wonder-blocks-layout": "^1.4.
|
|
21
|
+
"@khanacademy/wonder-blocks-core": "^4.6.0",
|
|
22
|
+
"@khanacademy/wonder-blocks-icon": "^1.2.33",
|
|
23
|
+
"@khanacademy/wonder-blocks-icon-button": "^3.4.17",
|
|
24
|
+
"@khanacademy/wonder-blocks-layout": "^1.4.13",
|
|
25
25
|
"@khanacademy/wonder-blocks-spacing": "^3.0.5",
|
|
26
|
-
"@khanacademy/wonder-blocks-typography": "^1.1.
|
|
26
|
+
"@khanacademy/wonder-blocks-typography": "^1.1.35"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"aphrodite": "^1.2.5",
|
|
@@ -31,6 +31,6 @@
|
|
|
31
31
|
"react-dom": "16.14.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"wb-dev-build-settings": "^0.
|
|
34
|
+
"wb-dev-build-settings": "^0.5.0"
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import {
|
|
4
|
-
import "jest-enzyme";
|
|
3
|
+
import {render, screen} from "@testing-library/react";
|
|
5
4
|
|
|
6
5
|
import expectRenderError from "../../../../../utils/testing/expect-render-error.js";
|
|
7
6
|
import CloseButton from "../close-button.js";
|
|
@@ -19,7 +18,7 @@ describe("CloseButton", () => {
|
|
|
19
18
|
|
|
20
19
|
test("testId should be set in the Icon element", () => {
|
|
21
20
|
// Arrange
|
|
22
|
-
|
|
21
|
+
render(
|
|
23
22
|
<div>
|
|
24
23
|
<ModalContext.Provider value={{closeModal: () => {}}}>
|
|
25
24
|
<CloseButton testId="modal-example-close" />,
|
|
@@ -28,9 +27,12 @@ describe("CloseButton", () => {
|
|
|
28
27
|
);
|
|
29
28
|
|
|
30
29
|
// Act
|
|
31
|
-
const closeButton =
|
|
30
|
+
const closeButton = screen.getByRole("button");
|
|
32
31
|
|
|
33
32
|
// Assert
|
|
34
|
-
expect(closeButton
|
|
33
|
+
expect(closeButton).toHaveAttribute(
|
|
34
|
+
"data-test-id",
|
|
35
|
+
"modal-example-close",
|
|
36
|
+
);
|
|
35
37
|
});
|
|
36
38
|
});
|
|
@@ -1,24 +1,16 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import {
|
|
4
|
-
import "jest-enzyme";
|
|
5
|
-
import {render, screen, fireEvent} from "@testing-library/react";
|
|
3
|
+
import {render, screen, fireEvent, waitFor} from "@testing-library/react";
|
|
6
4
|
import userEvent from "@testing-library/user-event";
|
|
7
5
|
|
|
8
6
|
import ModalBackdrop from "../modal-backdrop.js";
|
|
9
7
|
import OnePaneDialog from "../one-pane-dialog.js";
|
|
10
8
|
|
|
11
|
-
import {unmountAll} from "../../../../../utils/testing/enzyme-shim.js";
|
|
12
|
-
import {getElementAttachedToDocument} from "../../../../../utils/testing/get-element-attached-to-document.js";
|
|
13
|
-
|
|
14
|
-
const wait = (duration: number = 0) =>
|
|
15
|
-
new Promise((resolve, reject) => setTimeout(resolve, duration));
|
|
16
|
-
|
|
17
9
|
const exampleModal = (
|
|
18
10
|
<OnePaneDialog
|
|
19
|
-
content={<div data-modal-content />}
|
|
11
|
+
content={<div data-test-id="example-modal-content" />}
|
|
20
12
|
title="Title"
|
|
21
|
-
footer={<div data-modal-footer />}
|
|
13
|
+
footer={<div data-test-id="example-modal-footer" />}
|
|
22
14
|
testId="example-modal-test-id"
|
|
23
15
|
/>
|
|
24
16
|
);
|
|
@@ -27,9 +19,9 @@ const exampleModalWithButtons = (
|
|
|
27
19
|
<OnePaneDialog
|
|
28
20
|
content={
|
|
29
21
|
<div>
|
|
30
|
-
<button
|
|
31
|
-
<button
|
|
32
|
-
<button
|
|
22
|
+
<button>first focusable button</button>
|
|
23
|
+
<button />
|
|
24
|
+
<button />
|
|
33
25
|
</div>
|
|
34
26
|
}
|
|
35
27
|
title="Title"
|
|
@@ -38,13 +30,6 @@ const exampleModalWithButtons = (
|
|
|
38
30
|
);
|
|
39
31
|
|
|
40
32
|
describe("ModalBackdrop", () => {
|
|
41
|
-
afterEach(() => {
|
|
42
|
-
unmountAll();
|
|
43
|
-
if (document.body) {
|
|
44
|
-
document.body.innerHTML = "";
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
48
33
|
test("Clicking the backdrop triggers `onCloseModal`", () => {
|
|
49
34
|
// Arrange
|
|
50
35
|
const onCloseModal = jest.fn();
|
|
@@ -68,17 +53,19 @@ describe("ModalBackdrop", () => {
|
|
|
68
53
|
});
|
|
69
54
|
|
|
70
55
|
test("Clicking the modal content does not trigger `onCloseModal`", () => {
|
|
56
|
+
// Arrange
|
|
71
57
|
const onCloseModal = jest.fn();
|
|
72
58
|
|
|
73
|
-
|
|
74
|
-
// click handler expects actual DOM events.
|
|
75
|
-
const wrapper = mount(
|
|
59
|
+
render(
|
|
76
60
|
<ModalBackdrop onCloseModal={onCloseModal}>
|
|
77
61
|
{exampleModal}
|
|
78
62
|
</ModalBackdrop>,
|
|
79
63
|
);
|
|
80
64
|
|
|
81
|
-
|
|
65
|
+
// Act
|
|
66
|
+
userEvent.click(screen.getByTestId("example-modal-content"));
|
|
67
|
+
|
|
68
|
+
// Assert
|
|
82
69
|
expect(onCloseModal).not.toHaveBeenCalled();
|
|
83
70
|
});
|
|
84
71
|
|
|
@@ -139,29 +126,27 @@ describe("ModalBackdrop", () => {
|
|
|
139
126
|
});
|
|
140
127
|
|
|
141
128
|
test("Clicking the modal footer does not trigger `onCloseModal`", () => {
|
|
129
|
+
// Arrange
|
|
142
130
|
const onCloseModal = jest.fn();
|
|
143
131
|
|
|
144
|
-
|
|
145
|
-
// click handler expects actual DOM events.
|
|
146
|
-
const wrapper = mount(
|
|
132
|
+
render(
|
|
147
133
|
<ModalBackdrop onCloseModal={onCloseModal}>
|
|
148
134
|
{exampleModal}
|
|
149
135
|
</ModalBackdrop>,
|
|
150
136
|
);
|
|
151
137
|
|
|
152
|
-
|
|
138
|
+
// Act
|
|
139
|
+
userEvent.click(screen.getByTestId("example-modal-footer"));
|
|
140
|
+
|
|
141
|
+
// Assert
|
|
153
142
|
expect(onCloseModal).not.toHaveBeenCalled();
|
|
154
143
|
});
|
|
155
144
|
|
|
156
145
|
test("If initialFocusId is set and element is found, we focus that element inside the modal", async () => {
|
|
157
146
|
// Arrange
|
|
158
|
-
// We need the elements in the DOM document, it seems, for this test
|
|
159
|
-
// to work. Changing to testing-library will likely fix this.
|
|
160
|
-
// Then we can remove the lint suppression.
|
|
161
|
-
const attachElement = getElementAttachedToDocument("container");
|
|
162
147
|
const initialFocusId = "initial-focus";
|
|
163
148
|
|
|
164
|
-
|
|
149
|
+
render(
|
|
165
150
|
<ModalBackdrop
|
|
166
151
|
initialFocusId={initialFocusId}
|
|
167
152
|
onCloseModal={() => {}}
|
|
@@ -170,107 +155,84 @@ describe("ModalBackdrop", () => {
|
|
|
170
155
|
content={
|
|
171
156
|
<div data-modal-content>
|
|
172
157
|
<input type="text" />
|
|
173
|
-
<button id="initial-focus"
|
|
158
|
+
<button id="initial-focus">Initial focus</button>
|
|
174
159
|
</div>
|
|
175
160
|
}
|
|
176
161
|
title="Title"
|
|
177
162
|
footer={<div data-modal-footer />}
|
|
178
163
|
/>
|
|
179
164
|
</ModalBackdrop>,
|
|
180
|
-
{attachTo: attachElement},
|
|
181
165
|
);
|
|
182
166
|
|
|
183
167
|
// Act
|
|
184
|
-
|
|
185
|
-
|
|
168
|
+
const initialFocusElement = screen.getByRole("button", {
|
|
169
|
+
name: "Initial focus",
|
|
170
|
+
});
|
|
186
171
|
|
|
187
172
|
// Assert
|
|
188
|
-
|
|
189
|
-
expect(initialFocusElement).toHaveLength(1);
|
|
190
|
-
|
|
191
|
-
// verify the focus is set on the correct element
|
|
192
|
-
// eslint-disable-next-line testing-library/no-node-access
|
|
193
|
-
expect(document.activeElement).toBe(initialFocusElement.getDOMNode());
|
|
173
|
+
await waitFor(() => expect(initialFocusElement).toHaveFocus());
|
|
194
174
|
});
|
|
195
175
|
|
|
196
176
|
test("If initialFocusId is set but element is NOT found, we focus on the first focusable element instead", async () => {
|
|
197
177
|
// Arrange
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
// Then we can remove the lint suppression.
|
|
201
|
-
const attachElement = getElementAttachedToDocument("container");
|
|
202
|
-
const initialFocusId = "initial-focus";
|
|
203
|
-
const firstFocusableElement = "[data-first-button]";
|
|
178
|
+
// This element does not exist in the DOM
|
|
179
|
+
const initialFocusId = "unknown-node";
|
|
204
180
|
|
|
205
|
-
const
|
|
181
|
+
const {container} = render(
|
|
206
182
|
<ModalBackdrop
|
|
207
183
|
initialFocusId={initialFocusId}
|
|
208
184
|
onCloseModal={() => {}}
|
|
209
185
|
>
|
|
210
186
|
{exampleModalWithButtons}
|
|
211
187
|
</ModalBackdrop>,
|
|
212
|
-
{attachTo: attachElement},
|
|
213
188
|
);
|
|
214
189
|
|
|
215
190
|
// Act
|
|
216
|
-
|
|
217
|
-
const initialFocusElement =
|
|
191
|
+
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
|
|
192
|
+
const initialFocusElement = container.querySelector(
|
|
193
|
+
`#${initialFocusId}`,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const firstFocusableElement = screen.getByRole("button", {
|
|
197
|
+
name: "first focusable button",
|
|
198
|
+
});
|
|
218
199
|
|
|
219
200
|
// Assert
|
|
220
201
|
// first we verify the element doesn't exist in the DOM
|
|
221
|
-
expect(initialFocusElement).
|
|
202
|
+
expect(initialFocusElement).not.toBeInTheDocument();
|
|
222
203
|
// verify the focus is set on the first focusable element instead
|
|
223
|
-
|
|
224
|
-
expect(document.activeElement).toBe(
|
|
225
|
-
wrapper.find(firstFocusableElement).getDOMNode(),
|
|
226
|
-
);
|
|
204
|
+
await waitFor(() => expect(firstFocusableElement).toHaveFocus());
|
|
227
205
|
});
|
|
228
206
|
|
|
229
207
|
test("If no initialFocusId is set, we focus the first button in the modal", async () => {
|
|
230
208
|
// Arrange
|
|
231
|
-
|
|
232
|
-
// to work. Changing to testing-library will likely fix this.
|
|
233
|
-
// Then we can remove the lint suppression.
|
|
234
|
-
const attachElement = getElementAttachedToDocument("container");
|
|
235
|
-
const wrapper = mount(
|
|
209
|
+
render(
|
|
236
210
|
<ModalBackdrop onCloseModal={() => {}}>
|
|
237
211
|
{exampleModalWithButtons}
|
|
238
212
|
</ModalBackdrop>,
|
|
239
|
-
{attachTo: attachElement},
|
|
240
213
|
);
|
|
241
214
|
|
|
242
215
|
// Act
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
.getDOMNode();
|
|
216
|
+
const firstFocusableElement = screen.getByRole("button", {
|
|
217
|
+
name: "first focusable button",
|
|
218
|
+
});
|
|
247
219
|
|
|
248
220
|
// Assert
|
|
249
|
-
|
|
250
|
-
expect(document.activeElement).toBe(focusableElement);
|
|
221
|
+
await waitFor(() => expect(firstFocusableElement).toHaveFocus());
|
|
251
222
|
});
|
|
252
223
|
|
|
253
224
|
test("If there are no focusable elements, we focus the Dialog instead", async () => {
|
|
254
225
|
// Arrange
|
|
255
|
-
|
|
256
|
-
// to work. Changing to testing-library will likely fix this.
|
|
257
|
-
// Then we can remove the lint suppression.
|
|
258
|
-
const attachElement = getElementAttachedToDocument("container");
|
|
259
|
-
const wrapper = mount(
|
|
226
|
+
render(
|
|
260
227
|
<ModalBackdrop onCloseModal={() => {}}>
|
|
261
228
|
{exampleModal}
|
|
262
229
|
</ModalBackdrop>,
|
|
263
|
-
{attachTo: attachElement},
|
|
264
230
|
);
|
|
265
231
|
|
|
266
232
|
// Act
|
|
267
|
-
|
|
268
|
-
const focusableElement = wrapper
|
|
269
|
-
.find('div[role="dialog"]')
|
|
270
|
-
.getDOMNode();
|
|
233
|
+
const firstFocusableElement = screen.getByRole("dialog");
|
|
271
234
|
|
|
272
235
|
// Assert
|
|
273
|
-
|
|
274
|
-
expect(document.activeElement).toBe(focusableElement);
|
|
236
|
+
await waitFor(() => expect(firstFocusableElement).toHaveFocus());
|
|
275
237
|
});
|
|
276
238
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import {
|
|
4
|
-
import "jest-enzyme";
|
|
3
|
+
import {render, screen} from "@testing-library/react";
|
|
5
4
|
|
|
6
5
|
import {
|
|
7
6
|
Breadcrumbs,
|
|
@@ -12,24 +11,22 @@ import ModalHeader from "../modal-header.js";
|
|
|
12
11
|
|
|
13
12
|
const exampleBreadcrumbs: React.Element<typeof Breadcrumbs> = (
|
|
14
13
|
<Breadcrumbs>
|
|
15
|
-
<BreadcrumbsItem>
|
|
14
|
+
<BreadcrumbsItem>breadcrumb item</BreadcrumbsItem>
|
|
16
15
|
</Breadcrumbs>
|
|
17
16
|
);
|
|
18
17
|
|
|
19
18
|
describe("ModalHeader", () => {
|
|
20
19
|
test("renders the title by default", () => {
|
|
21
20
|
// Arrange, Act
|
|
22
|
-
|
|
23
|
-
<ModalHeader title="Title" titleId="modal-title" />,
|
|
24
|
-
);
|
|
21
|
+
render(<ModalHeader title="Title" titleId="modal-title" />);
|
|
25
22
|
|
|
26
23
|
// Assert
|
|
27
|
-
expect(
|
|
24
|
+
expect(screen.getByText("Title")).toBeInTheDocument();
|
|
28
25
|
});
|
|
29
26
|
|
|
30
27
|
test("using only `breadcrumbs` should render the header", () => {
|
|
31
28
|
// Arrange, Act
|
|
32
|
-
|
|
29
|
+
render(
|
|
33
30
|
<ModalHeader
|
|
34
31
|
title="Title"
|
|
35
32
|
breadcrumbs={exampleBreadcrumbs}
|
|
@@ -38,12 +35,12 @@ describe("ModalHeader", () => {
|
|
|
38
35
|
);
|
|
39
36
|
|
|
40
37
|
// Assert
|
|
41
|
-
expect(
|
|
38
|
+
expect(screen.getByText("breadcrumb item")).toBeInTheDocument();
|
|
42
39
|
});
|
|
43
40
|
|
|
44
41
|
test("using only `subtitle` should render the header", () => {
|
|
45
42
|
// Arrange, Act
|
|
46
|
-
|
|
43
|
+
render(
|
|
47
44
|
<ModalHeader
|
|
48
45
|
title="Title"
|
|
49
46
|
subtitle="Subtitle"
|
|
@@ -52,12 +49,12 @@ describe("ModalHeader", () => {
|
|
|
52
49
|
);
|
|
53
50
|
|
|
54
51
|
// Assert
|
|
55
|
-
expect(
|
|
52
|
+
expect(screen.getByText("Subtitle")).toBeInTheDocument();
|
|
56
53
|
});
|
|
57
54
|
|
|
58
55
|
test("testId should be added to the title", () => {
|
|
59
56
|
// Arrange
|
|
60
|
-
|
|
57
|
+
render(
|
|
61
58
|
<ModalHeader
|
|
62
59
|
title="Title"
|
|
63
60
|
subtitle="Subtitle"
|
|
@@ -67,17 +64,18 @@ describe("ModalHeader", () => {
|
|
|
67
64
|
);
|
|
68
65
|
|
|
69
66
|
// Act
|
|
70
|
-
const title =
|
|
71
|
-
`[data-test-id="test-example-header-title"]`,
|
|
72
|
-
);
|
|
67
|
+
const title = screen.getByText("Title");
|
|
73
68
|
|
|
74
69
|
// Assert
|
|
75
|
-
expect(title).
|
|
70
|
+
expect(title).toHaveAttribute(
|
|
71
|
+
"data-test-id",
|
|
72
|
+
"test-example-header-title",
|
|
73
|
+
);
|
|
76
74
|
});
|
|
77
75
|
|
|
78
76
|
test("testId should be added to the subtitle", () => {
|
|
79
77
|
// Arrange
|
|
80
|
-
|
|
78
|
+
render(
|
|
81
79
|
<ModalHeader
|
|
82
80
|
title="Title"
|
|
83
81
|
subtitle="Subtitle"
|
|
@@ -87,11 +85,12 @@ describe("ModalHeader", () => {
|
|
|
87
85
|
);
|
|
88
86
|
|
|
89
87
|
// Act
|
|
90
|
-
const subtitle =
|
|
91
|
-
`[data-test-id="test-example-header-subtitle"]`,
|
|
92
|
-
);
|
|
88
|
+
const subtitle = screen.getByText("Subtitle");
|
|
93
89
|
|
|
94
90
|
// Assert
|
|
95
|
-
expect(subtitle).
|
|
91
|
+
expect(subtitle).toHaveAttribute(
|
|
92
|
+
"data-test-id",
|
|
93
|
+
"test-example-header-subtitle",
|
|
94
|
+
);
|
|
96
95
|
});
|
|
97
96
|
});
|