@khanacademy/wonder-blocks-popover 3.2.15 → 3.2.16
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 +13 -0
- package/package.json +7 -7
- package/src/components/__tests__/focus-manager.test.tsx +0 -180
- package/src/components/__tests__/initial-focus.test.tsx +0 -73
- package/src/components/__tests__/popover-anchor.test.tsx +0 -61
- package/src/components/__tests__/popover-content.test.tsx +0 -76
- package/src/components/__tests__/popover-content.typestest.tsx +0 -38
- package/src/components/__tests__/popover-dialog.test.tsx +0 -98
- package/src/components/__tests__/popover-event-listener.test.tsx +0 -98
- package/src/components/__tests__/popover.test.tsx +0 -932
- package/src/components/close-button.tsx +0 -61
- package/src/components/focus-manager.tsx +0 -344
- package/src/components/initial-focus.ts +0 -87
- package/src/components/popover-anchor.ts +0 -93
- package/src/components/popover-content-core.tsx +0 -143
- package/src/components/popover-content.tsx +0 -319
- package/src/components/popover-context.ts +0 -40
- package/src/components/popover-dialog.tsx +0 -150
- package/src/components/popover-event-listener.ts +0 -96
- package/src/components/popover.tsx +0 -410
- package/src/index.ts +0 -5
- package/src/util/__tests__/util.test.tsx +0 -38
- package/src/util/util.ts +0 -20
- package/tsconfig-build.json +0 -17
- package/tsconfig-build.tsbuildinfo +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-popover
|
|
2
2
|
|
|
3
|
+
## 3.2.16
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 02a1b298: Make sure we don't package tsconfig and tsbuildinfo files
|
|
8
|
+
- Updated dependencies [02a1b298]
|
|
9
|
+
- @khanacademy/wonder-blocks-core@7.0.1
|
|
10
|
+
- @khanacademy/wonder-blocks-icon-button@5.4.1
|
|
11
|
+
- @khanacademy/wonder-blocks-modal@5.1.12
|
|
12
|
+
- @khanacademy/wonder-blocks-tokens@2.0.1
|
|
13
|
+
- @khanacademy/wonder-blocks-tooltip@2.4.3
|
|
14
|
+
- @khanacademy/wonder-blocks-typography@2.1.16
|
|
15
|
+
|
|
3
16
|
## 3.2.15
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-popover",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.16",
|
|
4
4
|
"design": "v1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@babel/runtime": "^7.18.6",
|
|
19
|
-
"@khanacademy/wonder-blocks-core": "^7.0.
|
|
20
|
-
"@khanacademy/wonder-blocks-icon-button": "^5.4.
|
|
21
|
-
"@khanacademy/wonder-blocks-modal": "^5.1.
|
|
22
|
-
"@khanacademy/wonder-blocks-tokens": "^2.0.
|
|
23
|
-
"@khanacademy/wonder-blocks-tooltip": "^2.4.
|
|
24
|
-
"@khanacademy/wonder-blocks-typography": "^2.1.
|
|
19
|
+
"@khanacademy/wonder-blocks-core": "^7.0.1",
|
|
20
|
+
"@khanacademy/wonder-blocks-icon-button": "^5.4.1",
|
|
21
|
+
"@khanacademy/wonder-blocks-modal": "^5.1.12",
|
|
22
|
+
"@khanacademy/wonder-blocks-tokens": "^2.0.1",
|
|
23
|
+
"@khanacademy/wonder-blocks-tooltip": "^2.4.3",
|
|
24
|
+
"@khanacademy/wonder-blocks-typography": "^2.1.16"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"@phosphor-icons/core": "^2.0.2",
|
|
@@ -1,180 +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 FocusManager from "../focus-manager";
|
|
6
|
-
|
|
7
|
-
describe("FocusManager", () => {
|
|
8
|
-
it("should focus on the first focusable element inside the popover", async () => {
|
|
9
|
-
// Arrange
|
|
10
|
-
const externalNodes = (
|
|
11
|
-
<div>
|
|
12
|
-
<button>Open popover</button>
|
|
13
|
-
<button>Next focusable element outside</button>
|
|
14
|
-
</div>
|
|
15
|
-
);
|
|
16
|
-
render(externalNodes);
|
|
17
|
-
|
|
18
|
-
// get the anchor reference to be able pass it to the FocusManager
|
|
19
|
-
const anchorElementNode = await screen.findByRole("button", {
|
|
20
|
-
name: "Open popover",
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
render(
|
|
24
|
-
<FocusManager anchorElement={anchorElementNode}>
|
|
25
|
-
<div>
|
|
26
|
-
<button>first focusable element inside</button>
|
|
27
|
-
<button>second focusable element inside</button>
|
|
28
|
-
<button>third focusable element inside</button>
|
|
29
|
-
</div>
|
|
30
|
-
</FocusManager>,
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
// Act
|
|
34
|
-
// focus on the previous element before the popover (anchor element)
|
|
35
|
-
anchorElementNode.focus();
|
|
36
|
-
// focus gets automatically moved to be on the first focusable element
|
|
37
|
-
|
|
38
|
-
const firstFocusableElementInside = await screen.findByText(
|
|
39
|
-
"first focusable element inside",
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
// Assert
|
|
43
|
-
expect(firstFocusableElementInside).toHaveFocus();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("should focus on the last focusable element inside the popover", async () => {
|
|
47
|
-
// Arrange
|
|
48
|
-
const externalNodes = (
|
|
49
|
-
<div>
|
|
50
|
-
<button>Open popover</button>
|
|
51
|
-
<button>Next focusable element outside</button>
|
|
52
|
-
</div>
|
|
53
|
-
);
|
|
54
|
-
render(externalNodes);
|
|
55
|
-
|
|
56
|
-
// get the anchor reference to be able pass it to the FocusManager
|
|
57
|
-
const anchorElementNode = await screen.findByRole("button", {
|
|
58
|
-
name: "Open popover",
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
render(
|
|
62
|
-
<FocusManager anchorElement={anchorElementNode}>
|
|
63
|
-
<div>
|
|
64
|
-
<button>first focusable element inside</button>
|
|
65
|
-
<button>second focusable element inside</button>
|
|
66
|
-
<button>third focusable element inside</button>
|
|
67
|
-
</div>
|
|
68
|
-
</FocusManager>,
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
// Act
|
|
72
|
-
|
|
73
|
-
// find previous focusable element outside the popover
|
|
74
|
-
const nextFocusableElementOutside = await screen.findByRole("button", {
|
|
75
|
-
name: "Next focusable element outside",
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// focus on the next element after the popover
|
|
79
|
-
nextFocusableElementOutside.focus();
|
|
80
|
-
await userEvent.tab({shift: true});
|
|
81
|
-
|
|
82
|
-
const lastFocusableElementInside = await screen.findByText(
|
|
83
|
-
"third focusable element inside",
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
// Assert
|
|
87
|
-
expect(lastFocusableElementInside).toHaveFocus();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("should allow flowing the focus correctly", async () => {
|
|
91
|
-
// Arrange
|
|
92
|
-
const externalNodes = (
|
|
93
|
-
<div>
|
|
94
|
-
<button>Prev focusable element outside</button>
|
|
95
|
-
<button>Open popover</button>
|
|
96
|
-
<button>Next focusable element outside</button>
|
|
97
|
-
</div>
|
|
98
|
-
);
|
|
99
|
-
render(externalNodes);
|
|
100
|
-
|
|
101
|
-
// get the anchor reference to be able pass it to the FocusManager
|
|
102
|
-
const anchorElementNode = await screen.findByRole("button", {
|
|
103
|
-
name: "Open popover",
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
render(
|
|
107
|
-
<FocusManager anchorElement={anchorElementNode}>
|
|
108
|
-
<div>
|
|
109
|
-
<button>first focusable element inside</button>
|
|
110
|
-
</div>
|
|
111
|
-
</FocusManager>,
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
// Act
|
|
115
|
-
// 1. focus on the Open popover button, this opens the focus manager
|
|
116
|
-
// and focuses the button inside the popover
|
|
117
|
-
await userEvent.tab();
|
|
118
|
-
|
|
119
|
-
// 2. we advance to the next focusable element outside the popover
|
|
120
|
-
await userEvent.tab();
|
|
121
|
-
|
|
122
|
-
// 3. we loop back around to the first focusable element outside the popover
|
|
123
|
-
await userEvent.tab();
|
|
124
|
-
|
|
125
|
-
// find previous focusable element outside the popover
|
|
126
|
-
const prevFocusableElementOutside = await screen.findByRole("button", {
|
|
127
|
-
name: "Prev focusable element outside",
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
// Assert
|
|
131
|
-
expect(prevFocusableElementOutside).toHaveFocus();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("should disallow focusability on internal elements if the user focus out of the focus manager", async () => {
|
|
135
|
-
// Arrange
|
|
136
|
-
const externalNodes = (
|
|
137
|
-
<div>
|
|
138
|
-
<button>Prev focusable element outside</button>
|
|
139
|
-
<button>Open popover</button>
|
|
140
|
-
<button>Next focusable element outside</button>
|
|
141
|
-
</div>
|
|
142
|
-
);
|
|
143
|
-
render(externalNodes);
|
|
144
|
-
|
|
145
|
-
// get the anchor reference to be able pass it to the FocusManager
|
|
146
|
-
const anchorElementNode = await screen.findByRole("button", {
|
|
147
|
-
name: "Open popover",
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
render(
|
|
151
|
-
<FocusManager anchorElement={anchorElementNode}>
|
|
152
|
-
<div>
|
|
153
|
-
<button>first focusable element inside</button>
|
|
154
|
-
</div>
|
|
155
|
-
</FocusManager>,
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
// Act
|
|
159
|
-
// 1. focus on the previous element before the popover
|
|
160
|
-
await userEvent.tab();
|
|
161
|
-
|
|
162
|
-
// 2. focus on the anchor element
|
|
163
|
-
await userEvent.tab();
|
|
164
|
-
|
|
165
|
-
// 3. focus on focusable element inside the popover
|
|
166
|
-
await userEvent.tab();
|
|
167
|
-
|
|
168
|
-
// 4. focus on the next focusable element outside the popover (this will
|
|
169
|
-
// be the first focusable element outside the popover)
|
|
170
|
-
await userEvent.tab();
|
|
171
|
-
|
|
172
|
-
// The elements inside the focus manager should not be focusable anymore.
|
|
173
|
-
const focusableElementInside = await screen.findByRole("button", {
|
|
174
|
-
name: "first focusable element inside",
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
// Assert
|
|
178
|
-
expect(focusableElementInside).toHaveAttribute("tabIndex", "-1");
|
|
179
|
-
});
|
|
180
|
-
});
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {render, screen} from "@testing-library/react";
|
|
3
|
-
|
|
4
|
-
import InitialFocus from "../initial-focus";
|
|
5
|
-
|
|
6
|
-
describe("InitialFocus", () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
jest.useFakeTimers();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it("should try to focus on a given element by id", () => {
|
|
12
|
-
// Arrange
|
|
13
|
-
render(
|
|
14
|
-
<InitialFocus initialFocusId="initial-focus-id">
|
|
15
|
-
<div data-testid="container">
|
|
16
|
-
<button data-testid="item-0" />
|
|
17
|
-
<button data-testid="item-1" id="initial-focus-id" />
|
|
18
|
-
<button data-testid="item-2" />
|
|
19
|
-
</div>
|
|
20
|
-
</InitialFocus>,
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
// Act
|
|
24
|
-
const firstFocusableElement = screen.getByTestId("item-1");
|
|
25
|
-
// Fast-forward until all timers have been executed
|
|
26
|
-
jest.runAllTimers();
|
|
27
|
-
|
|
28
|
-
// Assert
|
|
29
|
-
expect(firstFocusableElement).toHaveFocus();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("should try to focus on the first focusable element", () => {
|
|
33
|
-
// Arrange
|
|
34
|
-
render(
|
|
35
|
-
<InitialFocus>
|
|
36
|
-
<div data-testid="container">
|
|
37
|
-
<button data-testid="item-0" />
|
|
38
|
-
<button data-testid="item-1" id="initial-focus-id" />
|
|
39
|
-
<button data-testid="item-2" />
|
|
40
|
-
</div>
|
|
41
|
-
</InitialFocus>,
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
// Act
|
|
45
|
-
const firstFocusableElement = screen.getByTestId("item-0");
|
|
46
|
-
|
|
47
|
-
// Fast-forward until all timers have been executed
|
|
48
|
-
jest.runAllTimers();
|
|
49
|
-
|
|
50
|
-
// Assert
|
|
51
|
-
expect(firstFocusableElement).toHaveFocus();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("should try to focus on the container if no focusable elements are found", () => {
|
|
55
|
-
// Arrange
|
|
56
|
-
render(
|
|
57
|
-
<InitialFocus>
|
|
58
|
-
<div data-testid="container">
|
|
59
|
-
<p>no focusable elements here</p>
|
|
60
|
-
</div>
|
|
61
|
-
</InitialFocus>,
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
// Act
|
|
65
|
-
const firstFocusableElement = screen.getByTestId("container");
|
|
66
|
-
|
|
67
|
-
// Fast-forward until all timers have been executed
|
|
68
|
-
jest.runAllTimers();
|
|
69
|
-
|
|
70
|
-
// Assert
|
|
71
|
-
expect(firstFocusableElement).toHaveFocus();
|
|
72
|
-
});
|
|
73
|
-
});
|
|
@@ -1,61 +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 PopoverAnchor from "../popover-anchor";
|
|
6
|
-
|
|
7
|
-
describe("PopoverAnchor", () => {
|
|
8
|
-
it("should set child node as ref", async () => {
|
|
9
|
-
// Arrange
|
|
10
|
-
const updateRef = jest.fn();
|
|
11
|
-
|
|
12
|
-
render(
|
|
13
|
-
<PopoverAnchor anchorRef={updateRef} onClick={jest.fn()}>
|
|
14
|
-
<button>test</button>
|
|
15
|
-
</PopoverAnchor>,
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
// Act
|
|
19
|
-
const triggerElement = await screen.findByRole("button");
|
|
20
|
-
|
|
21
|
-
// Assert
|
|
22
|
-
expect(updateRef).toBeCalledWith(triggerElement);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("should add onClick handler if child is a function", async () => {
|
|
26
|
-
// Arrange
|
|
27
|
-
const onClickMock = jest.fn();
|
|
28
|
-
|
|
29
|
-
render(
|
|
30
|
-
<PopoverAnchor anchorRef={jest.fn()} onClick={onClickMock}>
|
|
31
|
-
{({open}: any) => <button onClick={open}>open</button>}
|
|
32
|
-
</PopoverAnchor>,
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
// Act
|
|
36
|
-
await userEvent.click(await screen.findByRole("button"));
|
|
37
|
-
|
|
38
|
-
// Assert
|
|
39
|
-
expect(onClickMock).toBeCalled();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("should add onClick handler if child is a Node", async () => {
|
|
43
|
-
// Arrange
|
|
44
|
-
const onClickMock = jest.fn();
|
|
45
|
-
const onClickInnerMock = jest.fn();
|
|
46
|
-
|
|
47
|
-
render(
|
|
48
|
-
<PopoverAnchor anchorRef={jest.fn()} onClick={onClickMock}>
|
|
49
|
-
<button onClick={onClickInnerMock}>test</button>
|
|
50
|
-
</PopoverAnchor>,
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
// Act
|
|
54
|
-
await userEvent.click(await screen.findByRole("button"));
|
|
55
|
-
|
|
56
|
-
// Assert
|
|
57
|
-
// both custom and internal click should be called
|
|
58
|
-
expect(onClickInnerMock).toBeCalled();
|
|
59
|
-
expect(onClickMock).toBeCalled();
|
|
60
|
-
});
|
|
61
|
-
});
|
|
@@ -1,76 +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 PopoverContent from "../popover-content";
|
|
6
|
-
import PopoverContext from "../popover-context";
|
|
7
|
-
|
|
8
|
-
describe("PopoverContent", () => {
|
|
9
|
-
it("should close the popover from the actions", async () => {
|
|
10
|
-
// Arrange
|
|
11
|
-
const onCloseMock = jest.fn();
|
|
12
|
-
|
|
13
|
-
render(
|
|
14
|
-
<PopoverContext.Provider
|
|
15
|
-
value={{close: onCloseMock, placement: "left"}}
|
|
16
|
-
>
|
|
17
|
-
<PopoverContent
|
|
18
|
-
title="Title"
|
|
19
|
-
content="content"
|
|
20
|
-
actions={({close}: any) => (
|
|
21
|
-
<button onClick={close}>close popover</button>
|
|
22
|
-
)}
|
|
23
|
-
/>
|
|
24
|
-
</PopoverContext.Provider>,
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
// Act
|
|
28
|
-
await userEvent.click(await screen.findByRole("button"));
|
|
29
|
-
|
|
30
|
-
// Assert
|
|
31
|
-
expect(onCloseMock).toBeCalled();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("should warn when setting a image and icon at the same time", async () => {
|
|
35
|
-
// Arrange
|
|
36
|
-
const nodes = (
|
|
37
|
-
<PopoverContent
|
|
38
|
-
title="illustration"
|
|
39
|
-
content="content"
|
|
40
|
-
image={<img src="/dummy-image.png" alt="popover image" />}
|
|
41
|
-
icon={<img src="/dummy-icon.png" alt="popover icon" />}
|
|
42
|
-
/>
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
// Act
|
|
46
|
-
const underTest = () => render(nodes);
|
|
47
|
-
|
|
48
|
-
// Assert
|
|
49
|
-
expect(underTest).toThrowErrorMatchingInlineSnapshot(
|
|
50
|
-
`"'image' and 'icon' cannot be used at the same time. You can fix this by either removing 'image' or 'icon' from your instance."`,
|
|
51
|
-
);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("should warn when setting a horizontal placement with an Illustration popover", async () => {
|
|
55
|
-
// Arrange
|
|
56
|
-
const nodes = (
|
|
57
|
-
<PopoverContext.Provider
|
|
58
|
-
value={{close: () => {}, placement: "left"}}
|
|
59
|
-
>
|
|
60
|
-
<PopoverContent
|
|
61
|
-
title="illustration"
|
|
62
|
-
content="content"
|
|
63
|
-
image={<img src="/dummy-image.png" alt="dummy" />}
|
|
64
|
-
/>
|
|
65
|
-
</PopoverContext.Provider>
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
// Act
|
|
69
|
-
const underTest = () => render(nodes);
|
|
70
|
-
|
|
71
|
-
// Assert
|
|
72
|
-
expect(underTest).toThrowErrorMatchingInlineSnapshot(
|
|
73
|
-
`"'image' can only be vertically placed. You can fix this by either changing \`placement\` to \`top\` or \`bottom\` or removing the \`image\` prop inside \`content\`."`,
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import PopoverContent from "../popover-content";
|
|
4
|
-
|
|
5
|
-
<PopoverContent title="Title" content="Content" />;
|
|
6
|
-
|
|
7
|
-
<PopoverContent title="Title" content="Content" icon="close" />;
|
|
8
|
-
|
|
9
|
-
<PopoverContent
|
|
10
|
-
title="Title"
|
|
11
|
-
content="Content"
|
|
12
|
-
image={<img src="domokun.jpg" alt="domokun" />}
|
|
13
|
-
/>;
|
|
14
|
-
|
|
15
|
-
<PopoverContent
|
|
16
|
-
title="Title"
|
|
17
|
-
content="Content"
|
|
18
|
-
icon="close"
|
|
19
|
-
image={<img src="domokun.jpg" alt="domokun" />}
|
|
20
|
-
/>;
|
|
21
|
-
|
|
22
|
-
<PopoverContent title="Title" content="Content" emphasized={true} />;
|
|
23
|
-
|
|
24
|
-
// @ts-expect-error `emphasized` cannot be used with `icon`
|
|
25
|
-
<PopoverContent
|
|
26
|
-
title="Title"
|
|
27
|
-
content="Content"
|
|
28
|
-
icon="close"
|
|
29
|
-
emphasized={true}
|
|
30
|
-
/>;
|
|
31
|
-
|
|
32
|
-
// @ts-expect-error `emphasized` cannot be used with `img`
|
|
33
|
-
<PopoverContent
|
|
34
|
-
title="Title"
|
|
35
|
-
content="Content"
|
|
36
|
-
image={<img src="domokun.jpg" alt="domokun" />}
|
|
37
|
-
emphasized={true}
|
|
38
|
-
/>;
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {render} from "@testing-library/react";
|
|
3
|
-
import * as Tooltip from "@khanacademy/wonder-blocks-tooltip";
|
|
4
|
-
|
|
5
|
-
import type {Placement} from "@khanacademy/wonder-blocks-tooltip";
|
|
6
|
-
import PopoverDialog from "../popover-dialog";
|
|
7
|
-
import PopoverContentCore from "../popover-content-core";
|
|
8
|
-
|
|
9
|
-
jest.mock("@khanacademy/wonder-blocks-tooltip");
|
|
10
|
-
|
|
11
|
-
describe("PopoverDialog", () => {
|
|
12
|
-
it("should update the tail color to match the content's color", () => {
|
|
13
|
-
// Arrange
|
|
14
|
-
const tooltipTailSpy = jest.spyOn(Tooltip, "TooltipTail");
|
|
15
|
-
|
|
16
|
-
// Act
|
|
17
|
-
render(
|
|
18
|
-
<PopoverDialog showTail={true} placement="top" onUpdate={jest.fn()}>
|
|
19
|
-
<PopoverContentCore color="darkBlue">
|
|
20
|
-
popover content
|
|
21
|
-
</PopoverContentCore>
|
|
22
|
-
</PopoverDialog>,
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
// Assert
|
|
26
|
-
expect(tooltipTailSpy).toHaveBeenCalledWith(
|
|
27
|
-
expect.objectContaining({color: "darkBlue"}),
|
|
28
|
-
{},
|
|
29
|
-
);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("should call onUpdate if placement is changed", () => {
|
|
33
|
-
// Arrange
|
|
34
|
-
const onUpdateMock = jest.fn();
|
|
35
|
-
const UnderTest = ({placement}: {placement: Placement}) => (
|
|
36
|
-
<PopoverDialog
|
|
37
|
-
showTail={true}
|
|
38
|
-
placement={placement}
|
|
39
|
-
onUpdate={onUpdateMock}
|
|
40
|
-
>
|
|
41
|
-
<PopoverContentCore>popover content</PopoverContentCore>
|
|
42
|
-
</PopoverDialog>
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
const {rerender} = render(<UnderTest placement="top" />);
|
|
46
|
-
|
|
47
|
-
// Act
|
|
48
|
-
rerender(<UnderTest placement="bottom" />);
|
|
49
|
-
|
|
50
|
-
// Assert
|
|
51
|
-
expect(onUpdateMock).toBeCalledWith("bottom");
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("should not call onUpdate if placement remains the same", () => {
|
|
55
|
-
// Arrange
|
|
56
|
-
const onUpdateMock = jest.fn();
|
|
57
|
-
|
|
58
|
-
const UnderTest = ({placement}: {placement: Placement}) => (
|
|
59
|
-
<PopoverDialog
|
|
60
|
-
showTail={true}
|
|
61
|
-
placement={placement}
|
|
62
|
-
onUpdate={onUpdateMock}
|
|
63
|
-
>
|
|
64
|
-
<PopoverContentCore>popover content</PopoverContentCore>
|
|
65
|
-
</PopoverDialog>
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
const {rerender} = render(<UnderTest placement="top" />);
|
|
69
|
-
|
|
70
|
-
// Act
|
|
71
|
-
rerender(<UnderTest placement="top" />);
|
|
72
|
-
|
|
73
|
-
// Assert
|
|
74
|
-
expect(onUpdateMock).not.toBeCalled();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("should not render a tail if showTail is false", () => {
|
|
78
|
-
// Arrange
|
|
79
|
-
const tooltipTailSpy = jest.spyOn(Tooltip, "TooltipTail");
|
|
80
|
-
|
|
81
|
-
// Act
|
|
82
|
-
render(
|
|
83
|
-
<PopoverDialog
|
|
84
|
-
showTail={false}
|
|
85
|
-
placement="top"
|
|
86
|
-
onUpdate={jest.fn()}
|
|
87
|
-
>
|
|
88
|
-
<PopoverContentCore>popover content</PopoverContentCore>
|
|
89
|
-
</PopoverDialog>,
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
// Assert
|
|
93
|
-
expect(tooltipTailSpy).toHaveBeenCalledWith(
|
|
94
|
-
expect.objectContaining({show: false}),
|
|
95
|
-
{},
|
|
96
|
-
);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
@@ -1,98 +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 {View} from "@khanacademy/wonder-blocks-core";
|
|
6
|
-
import PopoverEventListener from "../popover-event-listener";
|
|
7
|
-
import PopoverContent from "../popover-content";
|
|
8
|
-
|
|
9
|
-
describe("PopoverKeypressListener", () => {
|
|
10
|
-
// TODO(FEI-5533): Key press events aren't working correctly with
|
|
11
|
-
// user-event v14. We need to investigate and fix this.
|
|
12
|
-
it.skip("should call onClose if Escape is pressed", async () => {
|
|
13
|
-
// Arrange
|
|
14
|
-
const onCloseMock = jest.fn();
|
|
15
|
-
|
|
16
|
-
render(<PopoverEventListener onClose={onCloseMock} />);
|
|
17
|
-
|
|
18
|
-
// Act
|
|
19
|
-
await userEvent.keyboard("{esc}");
|
|
20
|
-
|
|
21
|
-
// Assert
|
|
22
|
-
expect(onCloseMock).toHaveBeenCalled();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("should call onClose if clicked outside content ref", async () => {
|
|
26
|
-
// Arrange
|
|
27
|
-
const onCloseMock = jest.fn();
|
|
28
|
-
const contentRef: React.RefObject<PopoverContent> = React.createRef();
|
|
29
|
-
|
|
30
|
-
render(
|
|
31
|
-
<View>
|
|
32
|
-
<PopoverContent
|
|
33
|
-
ref={contentRef}
|
|
34
|
-
title="Title"
|
|
35
|
-
content="Content"
|
|
36
|
-
/>
|
|
37
|
-
<PopoverEventListener
|
|
38
|
-
onClose={onCloseMock}
|
|
39
|
-
contentRef={contentRef}
|
|
40
|
-
/>
|
|
41
|
-
</View>,
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
// Act
|
|
45
|
-
const event = new MouseEvent("click", {view: window, bubbles: true});
|
|
46
|
-
const node = document.body;
|
|
47
|
-
if (node) {
|
|
48
|
-
// First click is ignored by PopoverEventListener
|
|
49
|
-
// because it is triggered when opening the popover.
|
|
50
|
-
node.dispatchEvent(event);
|
|
51
|
-
node.dispatchEvent(event);
|
|
52
|
-
} else {
|
|
53
|
-
// Signal that body was never found
|
|
54
|
-
expect(node).not.toBe(null);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Assert
|
|
58
|
-
expect(onCloseMock).toHaveBeenCalled();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("should not call onClose if clicked inside content ref", async () => {
|
|
62
|
-
// Arrange
|
|
63
|
-
const onCloseMock = jest.fn();
|
|
64
|
-
const contentRef: React.RefObject<PopoverContent> = React.createRef();
|
|
65
|
-
|
|
66
|
-
render(
|
|
67
|
-
<View>
|
|
68
|
-
<PopoverContent
|
|
69
|
-
ref={contentRef}
|
|
70
|
-
title="Title"
|
|
71
|
-
content="Content"
|
|
72
|
-
testId="popover-content"
|
|
73
|
-
/>
|
|
74
|
-
<PopoverEventListener
|
|
75
|
-
onClose={onCloseMock}
|
|
76
|
-
contentRef={contentRef}
|
|
77
|
-
/>
|
|
78
|
-
</View>,
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
// Act
|
|
82
|
-
const event = new MouseEvent("click", {view: window, bubbles: true});
|
|
83
|
-
const node = await screen.findByTestId("popover-content");
|
|
84
|
-
|
|
85
|
-
if (node) {
|
|
86
|
-
// First click is ignored by PopoverEventListener
|
|
87
|
-
// because it is triggered when opening the popover.
|
|
88
|
-
node.dispatchEvent(event);
|
|
89
|
-
node.dispatchEvent(event);
|
|
90
|
-
} else {
|
|
91
|
-
// Signal that PopoverContent was never found
|
|
92
|
-
expect(node).not.toBe(null);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Assert
|
|
96
|
-
expect(onCloseMock).not.toHaveBeenCalled();
|
|
97
|
-
});
|
|
98
|
-
});
|