@khanacademy/wonder-blocks-modal 5.1.11 → 5.1.13

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 (33) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/package.json +10 -10
  3. package/src/components/__tests__/close-button.test.tsx +0 -37
  4. package/src/components/__tests__/focus-trap.test.tsx +0 -100
  5. package/src/components/__tests__/modal-backdrop.test.tsx +0 -241
  6. package/src/components/__tests__/modal-dialog.test.tsx +0 -87
  7. package/src/components/__tests__/modal-header.test.tsx +0 -97
  8. package/src/components/__tests__/modal-launcher.test.tsx +0 -436
  9. package/src/components/__tests__/modal-panel.test.tsx +0 -42
  10. package/src/components/__tests__/one-pane-dialog.test.tsx +0 -87
  11. package/src/components/close-button.tsx +0 -64
  12. package/src/components/focus-trap.tsx +0 -148
  13. package/src/components/modal-backdrop.tsx +0 -172
  14. package/src/components/modal-content.tsx +0 -81
  15. package/src/components/modal-context.ts +0 -16
  16. package/src/components/modal-dialog.tsx +0 -164
  17. package/src/components/modal-footer.tsx +0 -54
  18. package/src/components/modal-header.tsx +0 -194
  19. package/src/components/modal-launcher.tsx +0 -297
  20. package/src/components/modal-panel.tsx +0 -188
  21. package/src/components/one-pane-dialog.tsx +0 -244
  22. package/src/components/scroll-disabler.ts +0 -95
  23. package/src/index.ts +0 -17
  24. package/src/themes/default.ts +0 -36
  25. package/src/themes/khanmigo.ts +0 -16
  26. package/src/themes/themed-modal-dialog.tsx +0 -44
  27. package/src/util/constants.ts +0 -6
  28. package/src/util/find-focusable-nodes.ts +0 -12
  29. package/src/util/maybe-get-portal-mounted-modal-host-element.test.tsx +0 -133
  30. package/src/util/maybe-get-portal-mounted-modal-host-element.ts +0 -35
  31. package/src/util/types.ts +0 -13
  32. package/tsconfig-build.json +0 -20
  33. package/tsconfig-build.tsbuildinfo +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @khanacademy/wonder-blocks-modal
2
2
 
3
+ ## 5.1.13
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [3463bde3]
8
+ - @khanacademy/wonder-blocks-icon-button@5.5.0
9
+
10
+ ## 5.1.12
11
+
12
+ ### Patch Changes
13
+
14
+ - 02a1b298: Make sure we don't package tsconfig and tsbuildinfo files
15
+ - Updated dependencies [02a1b298]
16
+ - @khanacademy/wonder-blocks-breadcrumbs@2.2.7
17
+ - @khanacademy/wonder-blocks-core@7.0.1
18
+ - @khanacademy/wonder-blocks-icon-button@5.4.1
19
+ - @khanacademy/wonder-blocks-layout@2.2.1
20
+ - @khanacademy/wonder-blocks-theming@2.0.4
21
+ - @khanacademy/wonder-blocks-timing@5.0.2
22
+ - @khanacademy/wonder-blocks-tokens@2.0.1
23
+ - @khanacademy/wonder-blocks-typography@2.1.16
24
+
3
25
  ## 5.1.11
4
26
 
5
27
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-modal",
3
- "version": "5.1.11",
3
+ "version": "5.1.13",
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": "^2.2.6",
20
- "@khanacademy/wonder-blocks-core": "^7.0.0",
21
- "@khanacademy/wonder-blocks-icon-button": "^5.4.0",
22
- "@khanacademy/wonder-blocks-layout": "^2.2.0",
23
- "@khanacademy/wonder-blocks-theming": "^2.0.3",
24
- "@khanacademy/wonder-blocks-timing": "^5.0.1",
25
- "@khanacademy/wonder-blocks-tokens": "^2.0.0",
26
- "@khanacademy/wonder-blocks-typography": "^2.1.15"
19
+ "@khanacademy/wonder-blocks-breadcrumbs": "^2.2.7",
20
+ "@khanacademy/wonder-blocks-core": "^7.0.1",
21
+ "@khanacademy/wonder-blocks-icon-button": "^5.5.0",
22
+ "@khanacademy/wonder-blocks-layout": "^2.2.1",
23
+ "@khanacademy/wonder-blocks-theming": "^2.0.4",
24
+ "@khanacademy/wonder-blocks-timing": "^5.0.2",
25
+ "@khanacademy/wonder-blocks-tokens": "^2.0.1",
26
+ "@khanacademy/wonder-blocks-typography": "^2.1.16"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "@phosphor-icons/core": "^2.0.2",
@@ -32,7 +32,7 @@
32
32
  "react-dom": "16.14.0"
33
33
  },
34
34
  "devDependencies": {
35
- "@khanacademy/wonder-blocks-breadcrumbs": "^2.2.6",
35
+ "@khanacademy/wonder-blocks-breadcrumbs": "^2.2.7",
36
36
  "@khanacademy/wb-dev-build-settings": "^1.0.1"
37
37
  }
38
38
  }
@@ -1,37 +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 CloseButton from "../close-button";
6
- import ModalContext from "../modal-context";
7
-
8
- describe("CloseButton", () => {
9
- test("ModalContext.Provider and onClose should warn", () => {
10
- expectRenderError(
11
- <ModalContext.Provider value={{closeModal: () => {}}}>
12
- <CloseButton light={false} onClick={() => {}} />,
13
- </ModalContext.Provider>,
14
- "You've specified 'onClose' on a modal when using ModalLauncher. Please specify 'onClose' on the ModalLauncher instead",
15
- );
16
- });
17
-
18
- test("testId should be set in the Icon element", () => {
19
- // Arrange
20
- render(
21
- <div>
22
- <ModalContext.Provider value={{closeModal: () => {}}}>
23
- <CloseButton testId="modal-example-close" />,
24
- </ModalContext.Provider>
25
- </div>,
26
- );
27
-
28
- // Act
29
- const closeButton = screen.getByRole("button");
30
-
31
- // Assert
32
- expect(closeButton).toHaveAttribute(
33
- "data-testid",
34
- "modal-example-close",
35
- );
36
- });
37
- });
@@ -1,100 +0,0 @@
1
- import * as React from "react";
2
- import {render, screen} from "@testing-library/react";
3
- import {userEvent} from "@testing-library/user-event";
4
-
5
- import Button from "@khanacademy/wonder-blocks-button";
6
- import {Choice, RadioGroup} from "@khanacademy/wonder-blocks-form";
7
-
8
- import FocusTrap from "../focus-trap";
9
-
10
- describe("FocusTrap", () => {
11
- it("Focus should move to the first focusable element", async () => {
12
- // Arrange
13
- render(
14
- <>
15
- <FocusTrap>
16
- <RadioGroup
17
- label="some-label"
18
- description="some-description"
19
- groupName="some-group-name"
20
- onChange={() => {}}
21
- selectedValue={""}
22
- >
23
- <Choice
24
- label="first option"
25
- value="some-choice-value"
26
- />
27
- <Choice
28
- label="second option"
29
- value="some-choice-value-2"
30
- description="Some choice description."
31
- />
32
- </RadioGroup>
33
- <Button>A button</Button>
34
- </FocusTrap>
35
- <Button>An external button</Button>
36
- </>,
37
- );
38
-
39
- // Initial focused element
40
- const firstRadioButton = await screen.findByRole("radio", {
41
- name: /first option/i,
42
- });
43
- firstRadioButton.focus();
44
-
45
- // Act
46
- // focus on the button
47
- await userEvent.tab();
48
- // focus on the last sentinel
49
- await userEvent.tab();
50
-
51
- // Assert
52
- // Redirect focus to the first radiobutton.
53
- expect(firstRadioButton).toHaveFocus();
54
- });
55
-
56
- it("Focus should move to the last focusable element", async () => {
57
- // Arrange
58
- render(
59
- <>
60
- <FocusTrap>
61
- <RadioGroup
62
- label="some-label"
63
- description="some-description"
64
- groupName="some-group-name"
65
- onChange={() => {}}
66
- selectedValue={""}
67
- >
68
- <Choice
69
- label="first option"
70
- value="some-choice-value"
71
- />
72
- <Choice
73
- label="second option"
74
- value="some-choice-value-2"
75
- description="Some choice description."
76
- />
77
- </RadioGroup>
78
- <Button>A focusable button</Button>
79
- </FocusTrap>
80
- <Button>An external button</Button>
81
- </>,
82
- );
83
-
84
- // Initial focused element
85
- const firstRadioButton = await screen.findByRole("radio", {
86
- name: /first option/i,
87
- });
88
- firstRadioButton.focus();
89
-
90
- // Act
91
- // focus on the first sentinel
92
- await userEvent.tab({shift: true});
93
-
94
- // Assert
95
- // Redirect focus to the button.
96
- expect(
97
- await screen.findByRole("button", {name: "A focusable button"}),
98
- ).toHaveFocus();
99
- });
100
- });
@@ -1,241 +0,0 @@
1
- import * as React from "react";
2
- import {render, screen, fireEvent, waitFor} from "@testing-library/react";
3
- import {userEvent} from "@testing-library/user-event";
4
-
5
- import ModalBackdrop from "../modal-backdrop";
6
- import OnePaneDialog from "../one-pane-dialog";
7
-
8
- const exampleModal = (
9
- <OnePaneDialog
10
- content={<div data-testid="example-modal-content" />}
11
- title="Title"
12
- footer={<div data-testid="example-modal-footer" />}
13
- testId="example-modal-test-id"
14
- />
15
- );
16
-
17
- const exampleModalWithButtons = (
18
- <OnePaneDialog
19
- content={
20
- <div>
21
- <button>first focusable button</button>
22
- <button />
23
- <button />
24
- </div>
25
- }
26
- title="Title"
27
- footer={<div data-modal-footer />}
28
- />
29
- );
30
-
31
- describe("ModalBackdrop", () => {
32
- test("Clicking the backdrop triggers `onCloseModal`", async () => {
33
- // Arrange
34
- const onCloseModal = jest.fn();
35
-
36
- render(
37
- <ModalBackdrop
38
- onCloseModal={onCloseModal}
39
- testId="modal-backdrop-test-id"
40
- >
41
- {exampleModal}
42
- </ModalBackdrop>,
43
- );
44
-
45
- const backdrop = await screen.findByTestId("modal-backdrop-test-id");
46
-
47
- //Act
48
- await userEvent.click(backdrop);
49
-
50
- // Assert
51
- expect(onCloseModal).toHaveBeenCalled();
52
- });
53
-
54
- test("Clicking the modal content does not trigger `onCloseModal`", async () => {
55
- // Arrange
56
- const onCloseModal = jest.fn();
57
-
58
- render(
59
- <ModalBackdrop onCloseModal={onCloseModal}>
60
- {exampleModal}
61
- </ModalBackdrop>,
62
- );
63
-
64
- // Act
65
- await userEvent.click(
66
- await screen.findByTestId("example-modal-content"),
67
- );
68
-
69
- // Assert
70
- expect(onCloseModal).not.toHaveBeenCalled();
71
- });
72
-
73
- test("Clicking and dragging into the backdrop does not close modal", async () => {
74
- // Arrange
75
- const onCloseModal = jest.fn();
76
-
77
- render(
78
- <ModalBackdrop
79
- onCloseModal={onCloseModal}
80
- testId="modal-backdrop-test-id"
81
- >
82
- {exampleModal}
83
- </ModalBackdrop>,
84
- );
85
-
86
- const panel = await screen.findByTestId("example-modal-test-id");
87
- const backdrop = await screen.findByTestId("modal-backdrop-test-id");
88
-
89
- // Act
90
-
91
- // Dragging the mouse
92
- // eslint-disable-next-line testing-library/prefer-user-event
93
- fireEvent.mouseDown(panel);
94
- // eslint-disable-next-line testing-library/prefer-user-event
95
- fireEvent.mouseUp(backdrop);
96
-
97
- // Assert
98
- expect(onCloseModal).not.toHaveBeenCalled();
99
- });
100
-
101
- test("Clicking and dragging in from the backdrop does not close modal", async () => {
102
- // Arrange
103
- const onCloseModal = jest.fn();
104
-
105
- render(
106
- <ModalBackdrop
107
- onCloseModal={onCloseModal}
108
- testId="modal-backdrop-test-id"
109
- >
110
- {exampleModal}
111
- </ModalBackdrop>,
112
- );
113
-
114
- const panel = await screen.findByTestId("example-modal-test-id");
115
- const backdrop = await screen.findByTestId("modal-backdrop-test-id");
116
-
117
- // Act
118
-
119
- // Dragging the mouse
120
- // eslint-disable-next-line testing-library/prefer-user-event
121
- fireEvent.mouseDown(backdrop);
122
- // eslint-disable-next-line testing-library/prefer-user-event
123
- fireEvent.mouseUp(panel);
124
-
125
- // Assert
126
- expect(onCloseModal).not.toHaveBeenCalled();
127
- });
128
-
129
- test("Clicking the modal footer does not trigger `onCloseModal`", async () => {
130
- // Arrange
131
- const onCloseModal = jest.fn();
132
-
133
- render(
134
- <ModalBackdrop onCloseModal={onCloseModal}>
135
- {exampleModal}
136
- </ModalBackdrop>,
137
- );
138
-
139
- // Act
140
- await userEvent.click(
141
- await screen.findByTestId("example-modal-footer"),
142
- );
143
-
144
- // Assert
145
- expect(onCloseModal).not.toHaveBeenCalled();
146
- });
147
-
148
- test("If initialFocusId is set and element is found, we focus that element inside the modal", async () => {
149
- // Arrange
150
- const initialFocusId = "initial-focus";
151
-
152
- render(
153
- <ModalBackdrop
154
- initialFocusId={initialFocusId}
155
- onCloseModal={() => {}}
156
- >
157
- <OnePaneDialog
158
- content={
159
- <div data-modal-content>
160
- <input type="text" />
161
- <button id="initial-focus">Initial focus</button>
162
- </div>
163
- }
164
- title="Title"
165
- footer={<div data-modal-footer />}
166
- />
167
- </ModalBackdrop>,
168
- );
169
-
170
- // Act
171
- const initialFocusElement = await screen.findByRole("button", {
172
- name: "Initial focus",
173
- });
174
-
175
- // Assert
176
- await waitFor(() => expect(initialFocusElement).toHaveFocus());
177
- });
178
-
179
- test("If initialFocusId is set but element is NOT found, we focus on the first focusable element instead", async () => {
180
- // Arrange
181
- // This element does not exist in the DOM
182
- const initialFocusId = "unknown-node";
183
-
184
- const {container} = render(
185
- <ModalBackdrop
186
- initialFocusId={initialFocusId}
187
- onCloseModal={() => {}}
188
- >
189
- {exampleModalWithButtons}
190
- </ModalBackdrop>,
191
- );
192
-
193
- // Act
194
- // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
195
- const initialFocusElement = container.querySelector(
196
- `#${initialFocusId}`,
197
- );
198
-
199
- const firstFocusableElement = await screen.findByRole("button", {
200
- name: "first focusable button",
201
- });
202
-
203
- // Assert
204
- // first we verify the element doesn't exist in the DOM
205
- expect(initialFocusElement).not.toBeInTheDocument();
206
- // verify the focus is set on the first focusable element instead
207
- await waitFor(() => expect(firstFocusableElement).toHaveFocus());
208
- });
209
-
210
- test("If no initialFocusId is set, we focus the first button in the modal", async () => {
211
- // Arrange
212
- render(
213
- <ModalBackdrop onCloseModal={() => {}}>
214
- {exampleModalWithButtons}
215
- </ModalBackdrop>,
216
- );
217
-
218
- // Act
219
- const firstFocusableElement = await screen.findByRole("button", {
220
- name: "first focusable button",
221
- });
222
-
223
- // Assert
224
- await waitFor(() => expect(firstFocusableElement).toHaveFocus());
225
- });
226
-
227
- test("If there are no focusable elements, we focus the Dialog instead", async () => {
228
- // Arrange
229
- render(
230
- <ModalBackdrop onCloseModal={() => {}}>
231
- {exampleModal}
232
- </ModalBackdrop>,
233
- );
234
-
235
- // Act
236
- const firstFocusableElement = await screen.findByRole("dialog");
237
-
238
- // Assert
239
- await waitFor(() => expect(firstFocusableElement).toHaveFocus());
240
- });
241
- });
@@ -1,87 +0,0 @@
1
- import * as React from "react";
2
- import {render, screen} from "@testing-library/react";
3
-
4
- import ModalDialog from "../modal-dialog";
5
-
6
- describe("ModalDialog", () => {
7
- it("should render its contents", () => {
8
- // Arrange
9
-
10
- // Act
11
- render(
12
- <ModalDialog aria-labelledby="123">
13
- <h1 id="123">A modal</h1>
14
- <p>The contents</p>
15
- </ModalDialog>,
16
- );
17
-
18
- // Assert
19
- expect(screen.getByRole("dialog")).toBeInTheDocument();
20
- });
21
-
22
- it("should announce the dialog if aria-labelledby is set", () => {
23
- // Arrange
24
-
25
- // Act
26
- render(
27
- <ModalDialog aria-labelledby="dialog-title">
28
- <h1 id="dialog-title">A modal</h1>
29
- <p>The contents</p>
30
- </ModalDialog>,
31
- );
32
-
33
- // Assert
34
- expect(
35
- screen.getByRole("dialog", {name: "A modal"}),
36
- ).toBeInTheDocument();
37
- });
38
-
39
- it("should announce the dialog's contents if aria-describedby is set", () => {
40
- // Arrange
41
-
42
- // Act
43
- render(
44
- <ModalDialog
45
- aria-labelledby="dialog-title"
46
- aria-describedby="dialog-body"
47
- >
48
- <h1 id="dialog-title">A modal</h1>
49
- <p id="dialog-body">The contents</p>
50
- </ModalDialog>,
51
- );
52
-
53
- // Assert
54
- expect(
55
- screen.getByRole("dialog", {description: "The contents"}),
56
- ).toBeInTheDocument();
57
- });
58
-
59
- it("adds testId to the dialog element", () => {
60
- // Arrange
61
-
62
- // Act
63
- render(
64
- <ModalDialog aria-labelledby="dialog-title" testId="test-id">
65
- <h1 id="dialog-title">A modal</h1>
66
- </ModalDialog>,
67
- );
68
-
69
- // Assert
70
- expect(screen.getByTestId("test-id")).toBeInTheDocument();
71
- });
72
-
73
- it("forwards the ref to the dialog element", () => {
74
- // Arrange
75
- const ref: React.RefObject<HTMLDivElement> = React.createRef();
76
-
77
- // Act
78
- render(
79
- <ModalDialog ref={ref} aria-labelledby="dialog-title">
80
- <h1 id="dialog-title">A modal</h1>
81
- </ModalDialog>,
82
- );
83
-
84
- // Assert
85
- expect(ref.current).toBeInstanceOf(HTMLDivElement);
86
- });
87
- });
@@ -1,97 +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
-
9
- import ModalHeader from "../modal-header";
10
-
11
- const exampleBreadcrumbs: React.ReactElement<
12
- React.ComponentProps<typeof Breadcrumbs>
13
- > = (
14
- <Breadcrumbs>
15
- <BreadcrumbsItem>breadcrumb item</BreadcrumbsItem>
16
- </Breadcrumbs>
17
- );
18
-
19
- describe("ModalHeader", () => {
20
- test("renders the title by default", () => {
21
- // Arrange, Act
22
- render(<ModalHeader title="Title" titleId="modal-title" />);
23
-
24
- // Assert
25
- expect(screen.getByText("Title")).toBeInTheDocument();
26
- });
27
-
28
- test("using only `breadcrumbs` should render the header", () => {
29
- // Arrange, Act
30
- render(
31
- <ModalHeader
32
- title="Title"
33
- breadcrumbs={exampleBreadcrumbs}
34
- titleId="modal-title"
35
- />,
36
- );
37
-
38
- // Assert
39
- expect(screen.getByText("breadcrumb item")).toBeInTheDocument();
40
- });
41
-
42
- test("using only `subtitle` should render the header", () => {
43
- // Arrange, Act
44
- render(
45
- <ModalHeader
46
- title="Title"
47
- subtitle="Subtitle"
48
- titleId="modal-title"
49
- />,
50
- );
51
-
52
- // Assert
53
- expect(screen.getByText("Subtitle")).toBeInTheDocument();
54
- });
55
-
56
- test("testId should be added to the title", () => {
57
- // Arrange
58
- render(
59
- <ModalHeader
60
- title="Title"
61
- subtitle="Subtitle"
62
- testId="test-example-header"
63
- titleId="modal-title"
64
- />,
65
- );
66
-
67
- // Act
68
- const title = screen.getByText("Title");
69
-
70
- // Assert
71
- expect(title).toHaveAttribute(
72
- "data-testid",
73
- "test-example-header-title",
74
- );
75
- });
76
-
77
- test("testId should be added to the subtitle", () => {
78
- // Arrange
79
- render(
80
- <ModalHeader
81
- title="Title"
82
- subtitle="Subtitle"
83
- testId="test-example-header"
84
- titleId="modal-title"
85
- />,
86
- );
87
-
88
- // Act
89
- const subtitle = screen.getByText("Subtitle");
90
-
91
- // Assert
92
- expect(subtitle).toHaveAttribute(
93
- "data-testid",
94
- "test-example-header-subtitle",
95
- );
96
- });
97
- });