@khanacademy/wonder-blocks-modal 5.1.10 → 5.1.12
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 +22 -0
- package/package.json +10 -10
- package/src/components/__tests__/close-button.test.tsx +0 -37
- package/src/components/__tests__/focus-trap.test.tsx +0 -100
- package/src/components/__tests__/modal-backdrop.test.tsx +0 -241
- package/src/components/__tests__/modal-dialog.test.tsx +0 -87
- package/src/components/__tests__/modal-header.test.tsx +0 -97
- package/src/components/__tests__/modal-launcher.test.tsx +0 -436
- package/src/components/__tests__/modal-panel.test.tsx +0 -42
- package/src/components/__tests__/one-pane-dialog.test.tsx +0 -87
- package/src/components/close-button.tsx +0 -64
- package/src/components/focus-trap.tsx +0 -148
- package/src/components/modal-backdrop.tsx +0 -172
- package/src/components/modal-content.tsx +0 -81
- package/src/components/modal-context.ts +0 -16
- package/src/components/modal-dialog.tsx +0 -164
- package/src/components/modal-footer.tsx +0 -54
- package/src/components/modal-header.tsx +0 -194
- package/src/components/modal-launcher.tsx +0 -297
- package/src/components/modal-panel.tsx +0 -188
- package/src/components/one-pane-dialog.tsx +0 -244
- package/src/components/scroll-disabler.ts +0 -95
- package/src/index.ts +0 -17
- package/src/themes/default.ts +0 -36
- package/src/themes/khanmigo.ts +0 -16
- package/src/themes/themed-modal-dialog.tsx +0 -44
- package/src/util/constants.ts +0 -6
- package/src/util/find-focusable-nodes.ts +0 -12
- package/src/util/maybe-get-portal-mounted-modal-host-element.test.tsx +0 -133
- package/src/util/maybe-get-portal-mounted-modal-host-element.ts +0 -35
- package/src/util/types.ts +0 -13
- package/tsconfig-build.json +0 -20
- package/tsconfig-build.tsbuildinfo +0 -1
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {render, screen, waitFor} from "@testing-library/react";
|
|
3
|
-
import {userEvent} from "@testing-library/user-event";
|
|
4
|
-
|
|
5
|
-
import {View} from "@khanacademy/wonder-blocks-core";
|
|
6
|
-
import Button from "@khanacademy/wonder-blocks-button";
|
|
7
|
-
|
|
8
|
-
import ModalLauncher from "../modal-launcher";
|
|
9
|
-
import OnePaneDialog from "../one-pane-dialog";
|
|
10
|
-
|
|
11
|
-
const exampleModal = (
|
|
12
|
-
<OnePaneDialog
|
|
13
|
-
title="Modal launcher test"
|
|
14
|
-
content={<div data-modal-child />}
|
|
15
|
-
/>
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
describe("ModalLauncher", () => {
|
|
19
|
-
window.scrollTo = jest.fn();
|
|
20
|
-
|
|
21
|
-
test("Children can launch the modal", async () => {
|
|
22
|
-
// Arrange
|
|
23
|
-
render(
|
|
24
|
-
<ModalLauncher modal={exampleModal} testId="modal-launcher-portal">
|
|
25
|
-
{({openModal}: any) => <button onClick={openModal} />}
|
|
26
|
-
</ModalLauncher>,
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
// Act
|
|
30
|
-
await userEvent.click(await screen.findByRole("button"));
|
|
31
|
-
|
|
32
|
-
const portal = await screen.findByTestId("modal-launcher-portal");
|
|
33
|
-
|
|
34
|
-
// Assert
|
|
35
|
-
expect(portal).toBeInTheDocument();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("Modal can be manually opened and closed", async () => {
|
|
39
|
-
// Arrange
|
|
40
|
-
const UnderTest = ({opened}: {opened: boolean}) => (
|
|
41
|
-
<ModalLauncher
|
|
42
|
-
modal={exampleModal}
|
|
43
|
-
opened={opened}
|
|
44
|
-
onClose={() => {}}
|
|
45
|
-
testId="modal-launcher-portal"
|
|
46
|
-
/>
|
|
47
|
-
);
|
|
48
|
-
const {rerender} = render(<UnderTest opened={false} />);
|
|
49
|
-
|
|
50
|
-
// Act
|
|
51
|
-
expect(
|
|
52
|
-
screen.queryByTestId("modal-launcher-portal"),
|
|
53
|
-
).not.toBeInTheDocument();
|
|
54
|
-
rerender(<UnderTest opened={true} />);
|
|
55
|
-
expect(
|
|
56
|
-
await screen.findByTestId("modal-launcher-portal"),
|
|
57
|
-
).toBeInTheDocument();
|
|
58
|
-
rerender(<UnderTest opened={false} />);
|
|
59
|
-
expect(
|
|
60
|
-
screen.queryByTestId("modal-launcher-portal"),
|
|
61
|
-
).not.toBeInTheDocument();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("Modal can close itself after launching", async () => {
|
|
65
|
-
// Arrange
|
|
66
|
-
const modalFn = ({closeModal}: {closeModal: () => void}) => (
|
|
67
|
-
<OnePaneDialog
|
|
68
|
-
title="Modal launcher test"
|
|
69
|
-
content={
|
|
70
|
-
<View>
|
|
71
|
-
<Button onClick={closeModal}>Close it!</Button>
|
|
72
|
-
</View>
|
|
73
|
-
}
|
|
74
|
-
/>
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
const onCloseMock = jest.fn();
|
|
78
|
-
|
|
79
|
-
// Mount the modal launcher. This shouldn't trigger any closing yet,
|
|
80
|
-
// because we shouldn't be calling the `modal` function yet.
|
|
81
|
-
render(
|
|
82
|
-
<ModalLauncher
|
|
83
|
-
modal={modalFn}
|
|
84
|
-
onClose={onCloseMock}
|
|
85
|
-
testId="modal-launcher-portal"
|
|
86
|
-
>
|
|
87
|
-
{({openModal}: any) => <button onClick={openModal} />}
|
|
88
|
-
</ModalLauncher>,
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
await userEvent.click(await screen.findByRole("button"));
|
|
92
|
-
|
|
93
|
-
// wait until the modal is open
|
|
94
|
-
await screen.findByRole("dialog");
|
|
95
|
-
|
|
96
|
-
// Act
|
|
97
|
-
await userEvent.click(
|
|
98
|
-
await screen.findByRole("button", {name: "Close it!"}),
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
// Assert
|
|
102
|
-
expect(onCloseMock).toHaveBeenCalled();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// TODO(FEI-5533): Key press events aren't working correctly with
|
|
106
|
-
// user-event v14. We need to investigate and fix this.
|
|
107
|
-
test.skip("Pressing Escape closes the modal", async () => {
|
|
108
|
-
// Arrange
|
|
109
|
-
render(
|
|
110
|
-
<ModalLauncher modal={exampleModal}>
|
|
111
|
-
{({openModal}: any) => <button onClick={openModal} />}
|
|
112
|
-
</ModalLauncher>,
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
// Launch the modal.
|
|
116
|
-
await userEvent.click(await screen.findByRole("button"));
|
|
117
|
-
|
|
118
|
-
// wait until the modal is open
|
|
119
|
-
await screen.findByRole("dialog");
|
|
120
|
-
|
|
121
|
-
// Act
|
|
122
|
-
// Simulate an Escape keypress.
|
|
123
|
-
await userEvent.keyboard("{esc}");
|
|
124
|
-
|
|
125
|
-
// Assert
|
|
126
|
-
// Confirm that the modal is no longer mounted.
|
|
127
|
-
await waitFor(() => expect(screen.queryByRole("dialog")).toBeNull());
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test("Disable scrolling when the modal is open", async () => {
|
|
131
|
-
// Arrange
|
|
132
|
-
render(
|
|
133
|
-
<ModalLauncher modal={exampleModal}>
|
|
134
|
-
{({openModal}: any) => <button onClick={openModal} />}
|
|
135
|
-
</ModalLauncher>,
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
// Act
|
|
139
|
-
// Launch the modal.
|
|
140
|
-
await userEvent.click(await screen.findByRole("button"));
|
|
141
|
-
|
|
142
|
-
// wait until the modal is open
|
|
143
|
-
await screen.findByRole("dialog");
|
|
144
|
-
|
|
145
|
-
// Assert
|
|
146
|
-
// Now that the modal is open, there should be a ScrollDisabler.
|
|
147
|
-
expect(document.body).toHaveStyle("overflow: hidden");
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
test("re-enable scrolling after the modal is closed", async () => {
|
|
151
|
-
// Arrange
|
|
152
|
-
render(
|
|
153
|
-
<ModalLauncher modal={exampleModal}>
|
|
154
|
-
{({openModal}: any) => <button onClick={openModal} />}
|
|
155
|
-
</ModalLauncher>,
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
// Launch the modal.
|
|
159
|
-
await userEvent.click(await screen.findByRole("button"));
|
|
160
|
-
|
|
161
|
-
await screen.findByRole("dialog");
|
|
162
|
-
|
|
163
|
-
// Close the modal.
|
|
164
|
-
await userEvent.click(
|
|
165
|
-
await screen.findByRole("button", {name: "Close modal"}),
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
// Assert
|
|
169
|
-
// Now that the modal is closed, there should be no ScrollDisabler.
|
|
170
|
-
expect(document.body).not.toHaveStyle("overflow: hidden");
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test("using `opened` and `children` should warn", async () => {
|
|
174
|
-
// Arrange
|
|
175
|
-
jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
176
|
-
|
|
177
|
-
// Act
|
|
178
|
-
render(
|
|
179
|
-
<ModalLauncher
|
|
180
|
-
modal={exampleModal}
|
|
181
|
-
opened={false}
|
|
182
|
-
onClose={() => {}}
|
|
183
|
-
>
|
|
184
|
-
{({openModal}: any) => <button onClick={openModal} />}
|
|
185
|
-
</ModalLauncher>,
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
// Assert
|
|
189
|
-
// eslint-disable-next-line no-console
|
|
190
|
-
expect(console.warn).toHaveBeenCalledWith(
|
|
191
|
-
"'children' and 'opened' can't be used together",
|
|
192
|
-
);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
test("using `opened` without `onClose` should throw", async () => {
|
|
196
|
-
// Arrange
|
|
197
|
-
jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
198
|
-
|
|
199
|
-
// Act
|
|
200
|
-
render(<ModalLauncher modal={exampleModal} opened={false} />);
|
|
201
|
-
|
|
202
|
-
// Assert
|
|
203
|
-
// eslint-disable-next-line no-console
|
|
204
|
-
expect(console.warn).toHaveBeenCalledWith(
|
|
205
|
-
"'onClose' should be used with 'opened'",
|
|
206
|
-
);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test("using neither `opened` nor `children` should throw", async () => {
|
|
210
|
-
// Arrange
|
|
211
|
-
jest.spyOn(console, "warn").mockImplementation(() => {});
|
|
212
|
-
|
|
213
|
-
// Act
|
|
214
|
-
render(<ModalLauncher modal={exampleModal} />);
|
|
215
|
-
|
|
216
|
-
// Assert
|
|
217
|
-
// eslint-disable-next-line no-console
|
|
218
|
-
expect(console.warn).toHaveBeenCalledWith(
|
|
219
|
-
"either 'children' or 'opened' must be set",
|
|
220
|
-
);
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
test("If backdropDismissEnabled set to false, clicking the backdrop does not trigger `onClose`", async () => {
|
|
224
|
-
// Arrange
|
|
225
|
-
const onClose = jest.fn();
|
|
226
|
-
|
|
227
|
-
render(
|
|
228
|
-
<ModalLauncher
|
|
229
|
-
onClose={onClose}
|
|
230
|
-
modal={exampleModal}
|
|
231
|
-
opened={true}
|
|
232
|
-
backdropDismissEnabled={false}
|
|
233
|
-
testId="modal-launcher-backdrop"
|
|
234
|
-
/>,
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
// Act
|
|
238
|
-
const backdrop = await screen.findByTestId("modal-launcher-backdrop");
|
|
239
|
-
await userEvent.click(backdrop);
|
|
240
|
-
|
|
241
|
-
// Assert
|
|
242
|
-
expect(onClose).not.toHaveBeenCalled();
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
test("if modal is launched, move focus inside the modal", async () => {
|
|
246
|
-
// Arrange
|
|
247
|
-
render(
|
|
248
|
-
<ModalLauncher
|
|
249
|
-
modal={
|
|
250
|
-
<OnePaneDialog
|
|
251
|
-
title="Modal launcher test"
|
|
252
|
-
content={
|
|
253
|
-
<View>
|
|
254
|
-
<Button>Button in modal</Button>
|
|
255
|
-
</View>
|
|
256
|
-
}
|
|
257
|
-
/>
|
|
258
|
-
}
|
|
259
|
-
>
|
|
260
|
-
{({openModal}: any) => (
|
|
261
|
-
<button onClick={openModal}>Open modal</button>
|
|
262
|
-
)}
|
|
263
|
-
</ModalLauncher>,
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
const modalOpener = await screen.findByRole("button", {
|
|
267
|
-
name: "Open modal",
|
|
268
|
-
});
|
|
269
|
-
// force focus
|
|
270
|
-
modalOpener.focus();
|
|
271
|
-
|
|
272
|
-
// Act
|
|
273
|
-
// Launch the modal.
|
|
274
|
-
await userEvent.type(modalOpener, "{enter}");
|
|
275
|
-
|
|
276
|
-
// wait until the modal is open
|
|
277
|
-
await screen.findByRole("dialog");
|
|
278
|
-
|
|
279
|
-
// Assert
|
|
280
|
-
await waitFor(async () =>
|
|
281
|
-
expect(
|
|
282
|
-
await screen.findByRole("button", {name: "Button in modal"}),
|
|
283
|
-
).toHaveFocus(),
|
|
284
|
-
);
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
test("if modal is closed, return focus to the last element focused outside the modal", async () => {
|
|
288
|
-
// Arrange
|
|
289
|
-
const ModalLauncherWrapper = () => {
|
|
290
|
-
const [opened, setOpened] = React.useState(false);
|
|
291
|
-
|
|
292
|
-
const handleOpen = () => {
|
|
293
|
-
setOpened(true);
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
const handleClose = () => {
|
|
297
|
-
setOpened(false);
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
return (
|
|
301
|
-
<View>
|
|
302
|
-
<Button>Top of page (should not receive focus)</Button>
|
|
303
|
-
<Button
|
|
304
|
-
testId="launcher-button"
|
|
305
|
-
onClick={() => handleOpen()}
|
|
306
|
-
>
|
|
307
|
-
Open modal
|
|
308
|
-
</Button>
|
|
309
|
-
<ModalLauncher
|
|
310
|
-
onClose={() => handleClose()}
|
|
311
|
-
opened={opened}
|
|
312
|
-
modal={({closeModal}: any) => (
|
|
313
|
-
<OnePaneDialog
|
|
314
|
-
title="Regular modal"
|
|
315
|
-
content={<View>Hello World</View>}
|
|
316
|
-
footer={
|
|
317
|
-
<Button
|
|
318
|
-
testId="modal-close-button"
|
|
319
|
-
onClick={closeModal}
|
|
320
|
-
>
|
|
321
|
-
Close Modal
|
|
322
|
-
</Button>
|
|
323
|
-
}
|
|
324
|
-
/>
|
|
325
|
-
)}
|
|
326
|
-
/>
|
|
327
|
-
</View>
|
|
328
|
-
);
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
render(<ModalLauncherWrapper />);
|
|
332
|
-
|
|
333
|
-
const lastButton = await screen.findByTestId("launcher-button");
|
|
334
|
-
|
|
335
|
-
// Launch the modal.
|
|
336
|
-
await userEvent.click(lastButton);
|
|
337
|
-
|
|
338
|
-
// Act
|
|
339
|
-
// Close modal
|
|
340
|
-
const modalCloseButton = await screen.findByTestId(
|
|
341
|
-
"modal-close-button",
|
|
342
|
-
);
|
|
343
|
-
await userEvent.click(modalCloseButton);
|
|
344
|
-
|
|
345
|
-
// Assert
|
|
346
|
-
await waitFor(() => {
|
|
347
|
-
expect(lastButton).toHaveFocus();
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
test("if `closedFocusId` is passed, shift focus to specified element after the modal closes", async () => {
|
|
352
|
-
// Arrange
|
|
353
|
-
const ModalLauncherWrapper = () => {
|
|
354
|
-
const [opened, setOpened] = React.useState(false);
|
|
355
|
-
|
|
356
|
-
const handleOpen = () => {
|
|
357
|
-
setOpened(true);
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
const handleClose = () => {
|
|
361
|
-
setOpened(false);
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
return (
|
|
365
|
-
<View>
|
|
366
|
-
<Button>Top of page (should not receive focus)</Button>
|
|
367
|
-
<Button id="button-to-focus-on" testId="focused-button">
|
|
368
|
-
Focus here after close
|
|
369
|
-
</Button>
|
|
370
|
-
<Button
|
|
371
|
-
testId="launcher-button"
|
|
372
|
-
onClick={() => handleOpen()}
|
|
373
|
-
>
|
|
374
|
-
Open modal
|
|
375
|
-
</Button>
|
|
376
|
-
<ModalLauncher
|
|
377
|
-
onClose={() => handleClose()}
|
|
378
|
-
opened={opened}
|
|
379
|
-
closedFocusId="button-to-focus-on"
|
|
380
|
-
modal={({closeModal}: any) => (
|
|
381
|
-
<OnePaneDialog
|
|
382
|
-
title="Triggered from action menu"
|
|
383
|
-
content={<View>Hello World</View>}
|
|
384
|
-
footer={
|
|
385
|
-
<Button
|
|
386
|
-
testId="modal-close-button"
|
|
387
|
-
onClick={closeModal}
|
|
388
|
-
>
|
|
389
|
-
Close Modal
|
|
390
|
-
</Button>
|
|
391
|
-
}
|
|
392
|
-
/>
|
|
393
|
-
)}
|
|
394
|
-
/>
|
|
395
|
-
</View>
|
|
396
|
-
);
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
render(<ModalLauncherWrapper />);
|
|
400
|
-
|
|
401
|
-
// Launch modal
|
|
402
|
-
const launcherButton = await screen.findByTestId("launcher-button");
|
|
403
|
-
await userEvent.click(launcherButton);
|
|
404
|
-
|
|
405
|
-
// Act
|
|
406
|
-
// Close modal
|
|
407
|
-
const modalCloseButton = await screen.findByTestId(
|
|
408
|
-
"modal-close-button",
|
|
409
|
-
);
|
|
410
|
-
await userEvent.click(modalCloseButton);
|
|
411
|
-
|
|
412
|
-
// Assert
|
|
413
|
-
const focusedButton = await screen.findByTestId("focused-button");
|
|
414
|
-
await waitFor(() => {
|
|
415
|
-
expect(focusedButton).toHaveFocus();
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
test("testId should be added to the Backdrop", async () => {
|
|
420
|
-
// Arrange
|
|
421
|
-
render(
|
|
422
|
-
<ModalLauncher
|
|
423
|
-
opened={true}
|
|
424
|
-
onClose={jest.fn()}
|
|
425
|
-
modal={<div role="dialog">dialog</div>}
|
|
426
|
-
testId="test-id-example"
|
|
427
|
-
/>,
|
|
428
|
-
);
|
|
429
|
-
|
|
430
|
-
// Act
|
|
431
|
-
const backdrop = await screen.findByTestId("test-id-example");
|
|
432
|
-
|
|
433
|
-
// Assert
|
|
434
|
-
expect(backdrop).toBeInTheDocument();
|
|
435
|
-
});
|
|
436
|
-
});
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {render, screen} from "@testing-library/react";
|
|
3
|
-
|
|
4
|
-
import expectRenderError from "../../../../../utils/testing/expect-render-error";
|
|
5
|
-
import ModalPanel from "../modal-panel";
|
|
6
|
-
import ModalContext from "../modal-context";
|
|
7
|
-
|
|
8
|
-
describe("ModalPanel", () => {
|
|
9
|
-
test("ModalContext.Provider and onClose should warn", () => {
|
|
10
|
-
expectRenderError(
|
|
11
|
-
<ModalContext.Provider value={{closeModal: () => {}}}>
|
|
12
|
-
<ModalPanel
|
|
13
|
-
content="Hello, world"
|
|
14
|
-
onClose={() => {}}
|
|
15
|
-
closeButtonVisible={true}
|
|
16
|
-
/>
|
|
17
|
-
</ModalContext.Provider>,
|
|
18
|
-
"You've specified 'onClose' on a modal when using ModalLauncher. Please specify 'onClose' on the ModalLauncher instead",
|
|
19
|
-
);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test("testId should be added to the panel wrapper", () => {
|
|
23
|
-
// Arrange
|
|
24
|
-
render(<ModalPanel content="dummy content" testId="test-id" />);
|
|
25
|
-
|
|
26
|
-
// Act
|
|
27
|
-
|
|
28
|
-
// Assert
|
|
29
|
-
expect(screen.getByTestId("test-id-panel")).toBeInTheDocument();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("testId should be added to the CloseButton element", () => {
|
|
33
|
-
// Arrange
|
|
34
|
-
render(<ModalPanel content="dummy content" testId="test-id" />);
|
|
35
|
-
|
|
36
|
-
// Act
|
|
37
|
-
const closeButton = screen.getByLabelText("Close modal");
|
|
38
|
-
|
|
39
|
-
// Assert
|
|
40
|
-
expect(closeButton).toHaveAttribute("data-testid", "test-id-close");
|
|
41
|
-
});
|
|
42
|
-
});
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {render, screen} from "@testing-library/react";
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
Breadcrumbs,
|
|
6
|
-
BreadcrumbsItem,
|
|
7
|
-
} from "@khanacademy/wonder-blocks-breadcrumbs";
|
|
8
|
-
import OnePaneDialog from "../one-pane-dialog";
|
|
9
|
-
|
|
10
|
-
describe("OnePaneDialog", () => {
|
|
11
|
-
test("testId should be set in the Dialog element", () => {
|
|
12
|
-
// Arrange
|
|
13
|
-
render(
|
|
14
|
-
<OnePaneDialog
|
|
15
|
-
title="Dialog with multi-step footer"
|
|
16
|
-
content="dummy content"
|
|
17
|
-
testId="one-pane-dialog-example"
|
|
18
|
-
/>,
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
// Act
|
|
22
|
-
const dialog = screen.getByRole("dialog");
|
|
23
|
-
|
|
24
|
-
// Assert
|
|
25
|
-
expect(dialog).toHaveAttribute(
|
|
26
|
-
"data-testid",
|
|
27
|
-
"one-pane-dialog-example",
|
|
28
|
-
);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test("role can be overriden to alertdialog", () => {
|
|
32
|
-
// Arrange
|
|
33
|
-
render(
|
|
34
|
-
<OnePaneDialog
|
|
35
|
-
title="Dialog with multi-step footer"
|
|
36
|
-
subtitle="Dialog subtitle"
|
|
37
|
-
content="dummy content"
|
|
38
|
-
testId="one-pane-dialog-example"
|
|
39
|
-
role="alertdialog"
|
|
40
|
-
/>,
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
// Act
|
|
44
|
-
const dialog = screen.getByRole("alertdialog");
|
|
45
|
-
|
|
46
|
-
// Assert
|
|
47
|
-
expect(dialog).toBeInTheDocument();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("should include breadcrumbs", () => {
|
|
51
|
-
// Arrange
|
|
52
|
-
render(
|
|
53
|
-
<OnePaneDialog
|
|
54
|
-
title="Dialog with multi-step footer"
|
|
55
|
-
breadcrumbs={
|
|
56
|
-
<Breadcrumbs>
|
|
57
|
-
<BreadcrumbsItem>test</BreadcrumbsItem>
|
|
58
|
-
</Breadcrumbs>
|
|
59
|
-
}
|
|
60
|
-
content="dummy content"
|
|
61
|
-
testId="one-pane-dialog-example"
|
|
62
|
-
/>,
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
// Act
|
|
66
|
-
|
|
67
|
-
// Assert
|
|
68
|
-
expect(screen.getByLabelText("Breadcrumbs")).toBeInTheDocument();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("applies aria-describedby to the modal", () => {
|
|
72
|
-
// Arrange
|
|
73
|
-
render(
|
|
74
|
-
<OnePaneDialog
|
|
75
|
-
title="unused"
|
|
76
|
-
content={<p id="description">cool dialog</p>}
|
|
77
|
-
aria-describedby="description"
|
|
78
|
-
/>,
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
// Act
|
|
82
|
-
const modal = screen.getByRole("dialog");
|
|
83
|
-
|
|
84
|
-
// Assert
|
|
85
|
-
expect(modal).toHaveDescription(/cool dialog/i);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import xIcon from "@phosphor-icons/core/regular/x.svg";
|
|
3
|
-
import IconButton from "@khanacademy/wonder-blocks-icon-button";
|
|
4
|
-
import type {StyleType} from "@khanacademy/wonder-blocks-core";
|
|
5
|
-
|
|
6
|
-
import ModalContext from "./modal-context";
|
|
7
|
-
|
|
8
|
-
type Props = {
|
|
9
|
-
/**
|
|
10
|
-
* Whether the button is on a dark/colored background.
|
|
11
|
-
*
|
|
12
|
-
* Sets primary button background color to white, and secondary and
|
|
13
|
-
* tertiary button title to color.
|
|
14
|
-
*/
|
|
15
|
-
light?: boolean;
|
|
16
|
-
/** Optional click handler */
|
|
17
|
-
onClick?: () => unknown;
|
|
18
|
-
/** Optional custom styles. */
|
|
19
|
-
style?: StyleType;
|
|
20
|
-
/**
|
|
21
|
-
* Test ID used for e2e testing.
|
|
22
|
-
*
|
|
23
|
-
* In this case, this component is internal, so `testId` is composed with
|
|
24
|
-
* the `testId` passed down from the Dialog variant + a suffix to scope it
|
|
25
|
-
* to this component.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* For testId="some-random-id"
|
|
29
|
-
* The result will be: `some-random-id-modal-panel`
|
|
30
|
-
*/
|
|
31
|
-
testId?: string;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export default class CloseButton extends React.Component<Props> {
|
|
35
|
-
render(): React.ReactNode {
|
|
36
|
-
const {light, onClick, style, testId} = this.props;
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<ModalContext.Consumer>
|
|
40
|
-
{({closeModal}) => {
|
|
41
|
-
if (closeModal && onClick) {
|
|
42
|
-
throw new Error(
|
|
43
|
-
"You've specified 'onClose' on a modal when using ModalLauncher. Please specify 'onClose' on the ModalLauncher instead",
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<IconButton
|
|
49
|
-
icon={xIcon}
|
|
50
|
-
// TODO(mdr): Translate this string for i18n.
|
|
51
|
-
// TODO(kevinb): provide a way to set this label
|
|
52
|
-
aria-label="Close modal"
|
|
53
|
-
onClick={onClick || closeModal}
|
|
54
|
-
kind={light ? "primary" : "tertiary"}
|
|
55
|
-
light={light}
|
|
56
|
-
style={style}
|
|
57
|
-
testId={testId}
|
|
58
|
-
/>
|
|
59
|
-
);
|
|
60
|
-
}}
|
|
61
|
-
</ModalContext.Consumer>
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
}
|