@remit/ui 0.0.128 → 0.0.129
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 +2 -3
- package/src/components/brief-sections.keyboard.test.ts +8 -36
- package/src/components/compose-body.language.test.ts +8 -58
- package/src/components/compose-body.spellcheck.test.ts +8 -55
- package/src/components/event-editor.render.test.ts +2 -2
- package/src/components/filter-rule-editor.create.test.ts +10 -59
- package/src/components/folder-tree-picker.create.test.ts +18 -63
- package/src/components/message-list-pane.brief-filter.test.ts +4 -31
- package/src/components/nav-link-surface.interaction.test.ts +7 -34
- package/src/components/nav-sidebar.keyboard.test.ts +11 -40
- package/src/components/password-input.render.test.ts +8 -50
- package/src/components/plain-text-editor.mount.test.ts +11 -66
- package/src/components/rich-text-editor.mount.test.ts +9 -63
- package/src/components/rich-text-formatting.test.ts +27 -45
- package/src/components/rich-text-image-node.test.ts +60 -83
- package/src/components/rich-text-markdown.test.ts +3 -25
- package/src/components/rich-text-paste.test.ts +28 -42
- package/src/components/rich-text-spellcheck-menu.test.ts +28 -94
- package/src/components/rich-text-spellcheck.test.ts +11 -65
- package/src/components/swipeable-row.gesture.test.ts +8 -42
- package/src/components/touch-list.follows-sections.test.ts +4 -27
- package/src/lib/adopted-html.test.ts +3 -22
- package/src/lib/compose-language.test.ts +2 -7
- package/src/lib/roving-focus.test.ts +16 -43
- package/src/lib/use-list-cursor.test.ts +3 -27
- package/src/lib/use-list-keyboard.test.ts +6 -30
- package/src/lib/use-long-press.test.ts +12 -43
- package/src/lib/use-rendered-row-ids.test.ts +3 -24
- package/src/lib/use-selection.hook.test.ts +3 -26
- package/src/lib/use-suggest-list.test.ts +5 -29
- package/src/lib/use-triage-keyboard.test.ts +6 -29
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
* and snapped back, and nothing else happened.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
import "@remit/test-dom";
|
|
21
22
|
import assert from "node:assert/strict";
|
|
22
|
-
import {
|
|
23
|
-
import type { JSDOM } from "jsdom";
|
|
23
|
+
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
24
24
|
import { act, createElement } from "react";
|
|
25
25
|
import { createRoot, type Root } from "react-dom/client";
|
|
26
26
|
import type { ThreadRowData } from "./app-shell-types.js";
|
|
@@ -39,45 +39,11 @@ const thread: ThreadRowData = {
|
|
|
39
39
|
isRead: false,
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
let dom: JSDOM;
|
|
43
42
|
let container: HTMLElement;
|
|
44
43
|
let root: Root;
|
|
45
44
|
|
|
46
|
-
before(async () => {
|
|
47
|
-
const { JSDOM: JSDOMCtor } = await import("jsdom");
|
|
48
|
-
dom = new JSDOMCtor(
|
|
49
|
-
"<!doctype html><html><body><div id=root></div></body></html>",
|
|
50
|
-
{ url: "http://localhost/", pretendToBeVisual: true },
|
|
51
|
-
);
|
|
52
|
-
globalThis.window = dom.window as unknown as typeof globalThis.window;
|
|
53
|
-
globalThis.document = dom.window.document;
|
|
54
|
-
globalThis.HTMLElement = dom.window.HTMLElement;
|
|
55
|
-
globalThis.Element = dom.window.Element;
|
|
56
|
-
globalThis.SVGElement = dom.window.SVGElement;
|
|
57
|
-
globalThis.PointerEvent = dom.window.PointerEvent;
|
|
58
|
-
// jsdom does not implement the pointer-capture methods at all (not even
|
|
59
|
-
// as no-ops) — SwipeableRow calls setPointerCapture once it claims the
|
|
60
|
-
// horizontal axis, so an unpolyfilled call throws mid-gesture.
|
|
61
|
-
dom.window.Element.prototype.setPointerCapture = () => undefined;
|
|
62
|
-
dom.window.Element.prototype.releasePointerCapture = () => undefined;
|
|
63
|
-
dom.window.Element.prototype.hasPointerCapture = () => false;
|
|
64
|
-
Object.defineProperty(globalThis, "navigator", {
|
|
65
|
-
value: dom.window.navigator,
|
|
66
|
-
configurable: true,
|
|
67
|
-
});
|
|
68
|
-
(
|
|
69
|
-
globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }
|
|
70
|
-
).IS_REACT_ACT_ENVIRONMENT = true;
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
after(() => {
|
|
74
|
-
dom.window.close();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
45
|
beforeEach(() => {
|
|
78
|
-
container =
|
|
79
|
-
"root",
|
|
80
|
-
) as unknown as HTMLElement;
|
|
46
|
+
container = document.getElementById("root") as unknown as HTMLElement;
|
|
81
47
|
container.innerHTML = "";
|
|
82
48
|
root = createRoot(container);
|
|
83
49
|
});
|
|
@@ -114,7 +80,7 @@ function mount(handlers: Handlers) {
|
|
|
114
80
|
// The open affordance is the one button with no aria-label — the
|
|
115
81
|
// leading/trailing action buttons ("Mark as read" etc.) only render once
|
|
116
82
|
// peeked, but querying by absence of aria-label is stable at rest too.
|
|
117
|
-
const row = [...
|
|
83
|
+
const row = [...document.querySelectorAll("button")].find(
|
|
118
84
|
(b) => !b.hasAttribute("aria-label"),
|
|
119
85
|
);
|
|
120
86
|
assert.ok(row, "open-affordance button did not mount");
|
|
@@ -122,7 +88,7 @@ function mount(handlers: Handlers) {
|
|
|
122
88
|
}
|
|
123
89
|
|
|
124
90
|
function contextMenu(row: Element) {
|
|
125
|
-
const event = new
|
|
91
|
+
const event = new MouseEvent("contextmenu", {
|
|
126
92
|
bubbles: true,
|
|
127
93
|
cancelable: true,
|
|
128
94
|
});
|
|
@@ -139,7 +105,7 @@ function contextMenu(row: Element) {
|
|
|
139
105
|
function pointerDown(row: Element, x = 10, y = 10) {
|
|
140
106
|
act(() => {
|
|
141
107
|
row.dispatchEvent(
|
|
142
|
-
new
|
|
108
|
+
new PointerEvent("pointerdown", {
|
|
143
109
|
bubbles: true,
|
|
144
110
|
pointerType: "touch",
|
|
145
111
|
pointerId: 1,
|
|
@@ -153,7 +119,7 @@ function pointerDown(row: Element, x = 10, y = 10) {
|
|
|
153
119
|
function pointerMove(row: Element, x: number, y: number) {
|
|
154
120
|
act(() => {
|
|
155
121
|
row.dispatchEvent(
|
|
156
|
-
new
|
|
122
|
+
new PointerEvent("pointermove", {
|
|
157
123
|
bubbles: true,
|
|
158
124
|
pointerType: "touch",
|
|
159
125
|
pointerId: 1,
|
|
@@ -174,7 +140,7 @@ function pointerUp(row: Element) {
|
|
|
174
140
|
// (the horizontal-swipe case here), so this also matches real behavior.
|
|
175
141
|
act(() => {
|
|
176
142
|
row.dispatchEvent(
|
|
177
|
-
new
|
|
143
|
+
new PointerEvent("pointerup", {
|
|
178
144
|
bubbles: true,
|
|
179
145
|
pointerType: "touch",
|
|
180
146
|
pointerId: 1,
|
|
@@ -7,16 +7,15 @@
|
|
|
7
7
|
* ids over whatever the consumer passes rather than as a copy of the rows.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import "@remit/test-dom";
|
|
10
11
|
import assert from "node:assert/strict";
|
|
11
|
-
import {
|
|
12
|
-
import type { JSDOM } from "jsdom";
|
|
12
|
+
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
13
13
|
import { act, createElement } from "react";
|
|
14
14
|
import { createRoot, type Root } from "react-dom/client";
|
|
15
15
|
import type { ThreadSection } from "./app-shell-types.js";
|
|
16
16
|
import type { SwipePeek } from "./swipeable-row.js";
|
|
17
17
|
import { TouchListBody } from "./touch-list.js";
|
|
18
18
|
|
|
19
|
-
let dom: JSDOM;
|
|
20
19
|
let container: HTMLElement;
|
|
21
20
|
let root: Root;
|
|
22
21
|
|
|
@@ -66,35 +65,13 @@ const click = (label: string) => {
|
|
|
66
65
|
assert.ok(button, `no "${label}" control on the page`);
|
|
67
66
|
act(() => {
|
|
68
67
|
button.dispatchEvent(
|
|
69
|
-
new
|
|
68
|
+
new MouseEvent("click", { bubbles: true, cancelable: true }),
|
|
70
69
|
);
|
|
71
70
|
});
|
|
72
71
|
};
|
|
73
72
|
|
|
74
|
-
before(async () => {
|
|
75
|
-
const { JSDOM: JSDOMCtor } = await import("jsdom");
|
|
76
|
-
dom = new JSDOMCtor(
|
|
77
|
-
"<!doctype html><html><body><div id=root></div></body></html>",
|
|
78
|
-
{ url: "http://localhost/", pretendToBeVisual: true },
|
|
79
|
-
);
|
|
80
|
-
globalThis.window = dom.window as unknown as typeof globalThis.window;
|
|
81
|
-
globalThis.document = dom.window.document;
|
|
82
|
-
globalThis.HTMLElement = dom.window.HTMLElement;
|
|
83
|
-
globalThis.Element = dom.window.Element;
|
|
84
|
-
globalThis.SVGElement = dom.window.SVGElement;
|
|
85
|
-
(
|
|
86
|
-
globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }
|
|
87
|
-
).IS_REACT_ACT_ENVIRONMENT = true;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
after(() => {
|
|
91
|
-
dom.window.close();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
73
|
beforeEach(() => {
|
|
95
|
-
container =
|
|
96
|
-
"root",
|
|
97
|
-
) as unknown as HTMLElement;
|
|
74
|
+
container = document.getElementById("root") as unknown as HTMLElement;
|
|
98
75
|
container.innerHTML = "";
|
|
99
76
|
root = createRoot(container);
|
|
100
77
|
});
|
|
@@ -3,29 +3,10 @@
|
|
|
3
3
|
* over content nobody here wrote — a web page, another mail client, Word — so
|
|
4
4
|
* the fixtures below are the shapes those actually put on the clipboard.
|
|
5
5
|
*/
|
|
6
|
+
import "@remit/test-dom";
|
|
6
7
|
import assert from "node:assert/strict";
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import type {
|
|
10
|
-
sanitizeAdoptedHtml as SanitizeAdoptedHtml,
|
|
11
|
-
sanitizeQuotedHtml as SanitizeQuotedHtml,
|
|
12
|
-
} from "./adopted-html.js";
|
|
13
|
-
|
|
14
|
-
let sanitizeAdoptedHtml: typeof SanitizeAdoptedHtml;
|
|
15
|
-
let sanitizeQuotedHtml: typeof SanitizeQuotedHtml;
|
|
16
|
-
|
|
17
|
-
before(async () => {
|
|
18
|
-
const { JSDOM: JSDOMCtor } = await import("jsdom");
|
|
19
|
-
const dom: JSDOM = new JSDOMCtor(
|
|
20
|
-
"<!doctype html><html><body></body></html>",
|
|
21
|
-
{ url: "http://localhost/" },
|
|
22
|
-
);
|
|
23
|
-
globalThis.window = dom.window as unknown as typeof globalThis.window;
|
|
24
|
-
globalThis.document = dom.window.document;
|
|
25
|
-
({ sanitizeAdoptedHtml, sanitizeQuotedHtml } = await import(
|
|
26
|
-
"./adopted-html.js"
|
|
27
|
-
));
|
|
28
|
-
});
|
|
8
|
+
import { describe, it } from "node:test";
|
|
9
|
+
import { sanitizeAdoptedHtml, sanitizeQuotedHtml } from "./adopted-html.js";
|
|
29
10
|
|
|
30
11
|
describe("sanitizeAdoptedHtml", () => {
|
|
31
12
|
it("keeps the structure a pasted document is made of", () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import "@remit/test-dom";
|
|
1
2
|
import assert from "node:assert/strict";
|
|
2
|
-
import {
|
|
3
|
+
import { describe, it } from "node:test";
|
|
3
4
|
import { data } from "franc-min/data.js";
|
|
4
|
-
import { JSDOM } from "jsdom";
|
|
5
5
|
import {
|
|
6
6
|
browserSpellcheckHelp,
|
|
7
7
|
COMPOSE_LANGUAGE_OPTIONS,
|
|
@@ -16,11 +16,6 @@ import {
|
|
|
16
16
|
/** The dictionaries the published image stages, per `REMIT_SPELLCHECK_LANGUAGES`. */
|
|
17
17
|
const BUILT = ["en", "en-GB", "nl"];
|
|
18
18
|
|
|
19
|
-
before(() => {
|
|
20
|
-
const dom = new JSDOM("");
|
|
21
|
-
globalThis.DOMParser = dom.window.DOMParser;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
19
|
describe("detectionCodeFor", () => {
|
|
25
20
|
it("resolves a region through its language", () => {
|
|
26
21
|
assert.equal(detectionCodeFor("en-GB"), "eng");
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* real KeyboardEvents at a real focused node, asserting on the DOM side effects
|
|
5
5
|
* the hook owns (focus, tabIndex), which `renderToString` cannot reach.
|
|
6
6
|
*/
|
|
7
|
+
import "@remit/test-dom";
|
|
7
8
|
import assert from "node:assert/strict";
|
|
8
|
-
import {
|
|
9
|
-
import type { JSDOM } from "jsdom";
|
|
9
|
+
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
10
10
|
import { act, createElement, type RefObject, useRef } from "react";
|
|
11
11
|
import { createRoot, type Root } from "react-dom/client";
|
|
12
12
|
import {
|
|
@@ -51,38 +51,11 @@ describe("rovingNextIndex", () => {
|
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
let dom: JSDOM;
|
|
55
54
|
let container: HTMLElement;
|
|
56
55
|
let root: Root;
|
|
57
56
|
|
|
58
|
-
before(async () => {
|
|
59
|
-
const { JSDOM: JSDOMCtor } = await import("jsdom");
|
|
60
|
-
dom = new JSDOMCtor(
|
|
61
|
-
"<!doctype html><html><body><div id=root></div></body></html>",
|
|
62
|
-
{ url: "http://localhost/", pretendToBeVisual: true },
|
|
63
|
-
);
|
|
64
|
-
globalThis.window = dom.window as unknown as typeof globalThis.window;
|
|
65
|
-
globalThis.document = dom.window.document;
|
|
66
|
-
globalThis.HTMLElement = dom.window.HTMLElement;
|
|
67
|
-
globalThis.Element = dom.window.Element;
|
|
68
|
-
globalThis.KeyboardEvent = dom.window.KeyboardEvent;
|
|
69
|
-
Object.defineProperty(globalThis, "navigator", {
|
|
70
|
-
value: dom.window.navigator,
|
|
71
|
-
configurable: true,
|
|
72
|
-
});
|
|
73
|
-
(
|
|
74
|
-
globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }
|
|
75
|
-
).IS_REACT_ACT_ENVIRONMENT = true;
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
after(() => {
|
|
79
|
-
dom.window.close();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
57
|
beforeEach(() => {
|
|
83
|
-
container =
|
|
84
|
-
"root",
|
|
85
|
-
) as unknown as HTMLElement;
|
|
58
|
+
container = document.getElementById("root") as unknown as HTMLElement;
|
|
86
59
|
container.innerHTML = "";
|
|
87
60
|
root = createRoot(container);
|
|
88
61
|
});
|
|
@@ -95,7 +68,7 @@ afterEach(() => {
|
|
|
95
68
|
|
|
96
69
|
function pressKey(target: Element, key: string, shiftKey = false) {
|
|
97
70
|
target.dispatchEvent(
|
|
98
|
-
new
|
|
71
|
+
new KeyboardEvent("keydown", { key, bubbles: true, shiftKey }),
|
|
99
72
|
);
|
|
100
73
|
}
|
|
101
74
|
|
|
@@ -158,7 +131,7 @@ describe("useRovingFocus", () => {
|
|
|
158
131
|
act(() => items[0]?.focus());
|
|
159
132
|
act(() => pressKey(items[0] as Element, "ArrowDown"));
|
|
160
133
|
|
|
161
|
-
assert.equal(
|
|
134
|
+
assert.equal(document.activeElement, items[1]);
|
|
162
135
|
assert.equal(items[1]?.tabIndex, 0);
|
|
163
136
|
assert.equal(items[0]?.tabIndex, -1);
|
|
164
137
|
});
|
|
@@ -169,7 +142,7 @@ describe("useRovingFocus", () => {
|
|
|
169
142
|
act(() => items[0]?.focus());
|
|
170
143
|
act(() => pressKey(items[0] as Element, "ArrowUp"));
|
|
171
144
|
|
|
172
|
-
assert.equal(
|
|
145
|
+
assert.equal(document.activeElement, items[0]);
|
|
173
146
|
});
|
|
174
147
|
|
|
175
148
|
it("ArrowDown clamps at the last row", () => {
|
|
@@ -178,7 +151,7 @@ describe("useRovingFocus", () => {
|
|
|
178
151
|
act(() => items[1]?.focus());
|
|
179
152
|
act(() => pressKey(items[1] as Element, "ArrowDown"));
|
|
180
153
|
|
|
181
|
-
assert.equal(
|
|
154
|
+
assert.equal(document.activeElement, items[1]);
|
|
182
155
|
});
|
|
183
156
|
|
|
184
157
|
it("Home/End jump to the first/last row", () => {
|
|
@@ -186,10 +159,10 @@ describe("useRovingFocus", () => {
|
|
|
186
159
|
const items = rows();
|
|
187
160
|
act(() => items[2]?.focus());
|
|
188
161
|
act(() => pressKey(items[2] as Element, "End"));
|
|
189
|
-
assert.equal(
|
|
162
|
+
assert.equal(document.activeElement, items[3]);
|
|
190
163
|
|
|
191
164
|
act(() => pressKey(items[3] as Element, "Home"));
|
|
192
|
-
assert.equal(
|
|
165
|
+
assert.equal(document.activeElement, items[0]);
|
|
193
166
|
});
|
|
194
167
|
|
|
195
168
|
it("mouse-focusing a row moves the tab stop there too", () => {
|
|
@@ -207,7 +180,7 @@ describe("useRovingFocus", () => {
|
|
|
207
180
|
act(() => items[0]?.focus());
|
|
208
181
|
act(() => pressKey(items[0] as Element, "ArrowDown"));
|
|
209
182
|
|
|
210
|
-
assert.equal(
|
|
183
|
+
assert.equal(document.activeElement, items[1]);
|
|
211
184
|
assert.match(items[1]?.textContent ?? "", /row-1/);
|
|
212
185
|
});
|
|
213
186
|
|
|
@@ -219,7 +192,7 @@ describe("useRovingFocus", () => {
|
|
|
219
192
|
act(() => nested[1]?.focus());
|
|
220
193
|
act(() => pressKey(nested[1] as Element, "ArrowDown"));
|
|
221
194
|
|
|
222
|
-
assert.equal(
|
|
195
|
+
assert.equal(document.activeElement, rows()[0]);
|
|
223
196
|
});
|
|
224
197
|
|
|
225
198
|
it("leaves Shift+Arrow to the layer above instead of moving the cursor", () => {
|
|
@@ -228,14 +201,14 @@ describe("useRovingFocus", () => {
|
|
|
228
201
|
const spy = () => {
|
|
229
202
|
seen += 1;
|
|
230
203
|
};
|
|
231
|
-
|
|
204
|
+
window.addEventListener("keydown", spy);
|
|
232
205
|
const items = rows();
|
|
233
206
|
act(() => items[0]?.focus());
|
|
234
207
|
act(() => pressKey(items[0] as Element, "ArrowDown", true));
|
|
235
208
|
act(() => pressKey(items[0] as Element, "ArrowUp", true));
|
|
236
|
-
|
|
209
|
+
window.removeEventListener("keydown", spy);
|
|
237
210
|
|
|
238
|
-
assert.equal(
|
|
211
|
+
assert.equal(document.activeElement, items[0]);
|
|
239
212
|
assert.equal(seen, 2);
|
|
240
213
|
});
|
|
241
214
|
|
|
@@ -245,12 +218,12 @@ describe("useRovingFocus", () => {
|
|
|
245
218
|
const spy = () => {
|
|
246
219
|
seen += 1;
|
|
247
220
|
};
|
|
248
|
-
|
|
221
|
+
window.addEventListener("keydown", spy);
|
|
249
222
|
const items = rows();
|
|
250
223
|
act(() => items[0]?.focus());
|
|
251
224
|
act(() => pressKey(items[0] as Element, "ArrowDown"));
|
|
252
225
|
act(() => pressKey(items[1] as Element, "Enter"));
|
|
253
|
-
|
|
226
|
+
window.removeEventListener("keydown", spy);
|
|
254
227
|
|
|
255
228
|
assert.equal(seen, 1);
|
|
256
229
|
});
|
|
@@ -3,44 +3,20 @@
|
|
|
3
3
|
* every thread list drives. Mounted against jsdom rather than `renderToString`,
|
|
4
4
|
* since the state only moves in response to real calls across renders.
|
|
5
5
|
*/
|
|
6
|
+
import "@remit/test-dom";
|
|
6
7
|
import assert from "node:assert/strict";
|
|
7
|
-
import {
|
|
8
|
-
import type { JSDOM } from "jsdom";
|
|
8
|
+
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
9
9
|
import { act, createElement } from "react";
|
|
10
10
|
import { createRoot, type Root } from "react-dom/client";
|
|
11
11
|
import { type ListCursor, useListCursor } from "./use-list-cursor.js";
|
|
12
12
|
|
|
13
13
|
const IDS = ["m1", "m2", "m3", "m4"];
|
|
14
14
|
|
|
15
|
-
let dom: JSDOM;
|
|
16
15
|
let container: HTMLElement;
|
|
17
16
|
let root: Root;
|
|
18
17
|
|
|
19
|
-
before(async () => {
|
|
20
|
-
const { JSDOM: JSDOMCtor } = await import("jsdom");
|
|
21
|
-
dom = new JSDOMCtor(
|
|
22
|
-
"<!doctype html><html><body><div id=root></div></body></html>",
|
|
23
|
-
{ url: "http://localhost/", pretendToBeVisual: true },
|
|
24
|
-
);
|
|
25
|
-
globalThis.window = dom.window as unknown as typeof globalThis.window;
|
|
26
|
-
globalThis.document = dom.window.document;
|
|
27
|
-
globalThis.HTMLElement = dom.window.HTMLElement;
|
|
28
|
-
globalThis.Element = dom.window.Element;
|
|
29
|
-
Object.defineProperty(globalThis, "navigator", {
|
|
30
|
-
value: dom.window.navigator,
|
|
31
|
-
configurable: true,
|
|
32
|
-
});
|
|
33
|
-
(
|
|
34
|
-
globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }
|
|
35
|
-
).IS_REACT_ACT_ENVIRONMENT = true;
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
after(() => dom.window.close());
|
|
39
|
-
|
|
40
18
|
beforeEach(() => {
|
|
41
|
-
container =
|
|
42
|
-
"root",
|
|
43
|
-
) as unknown as HTMLElement;
|
|
19
|
+
container = document.getElementById("root") as unknown as HTMLElement;
|
|
44
20
|
container.innerHTML = "";
|
|
45
21
|
root = createRoot(container);
|
|
46
22
|
});
|
|
@@ -4,16 +4,15 @@
|
|
|
4
4
|
* path reads its modifiers the same way, the selection follows the rows on
|
|
5
5
|
* screen, and the footer offers only the actions the layer registered.
|
|
6
6
|
*/
|
|
7
|
+
import "@remit/test-dom";
|
|
7
8
|
import assert from "node:assert/strict";
|
|
8
|
-
import {
|
|
9
|
-
import type { JSDOM } from "jsdom";
|
|
9
|
+
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
10
10
|
import { act, createElement } from "react";
|
|
11
11
|
import { createRoot, type Root } from "react-dom/client";
|
|
12
12
|
import { type ListKeyboard, useListKeyboard } from "./use-list-keyboard.js";
|
|
13
13
|
|
|
14
14
|
const ALL_IDS = ["m1", "m2", "m3", "m4"];
|
|
15
15
|
|
|
16
|
-
let dom: JSDOM;
|
|
17
16
|
let container: HTMLElement;
|
|
18
17
|
let root: Root;
|
|
19
18
|
let list: ListKeyboard;
|
|
@@ -48,7 +47,7 @@ const rerender = async (rowIds: string[]) => {
|
|
|
48
47
|
};
|
|
49
48
|
|
|
50
49
|
const pane = (): HTMLElement => {
|
|
51
|
-
const element =
|
|
50
|
+
const element = document.getElementById("pane");
|
|
52
51
|
assert.ok(element, "the harness rendered no pane");
|
|
53
52
|
return element as unknown as HTMLElement;
|
|
54
53
|
};
|
|
@@ -60,7 +59,7 @@ const press = (
|
|
|
60
59
|
) => {
|
|
61
60
|
act(() => {
|
|
62
61
|
target.dispatchEvent(
|
|
63
|
-
new
|
|
62
|
+
new KeyboardEvent("keydown", {
|
|
64
63
|
key,
|
|
65
64
|
bubbles: true,
|
|
66
65
|
cancelable: true,
|
|
@@ -89,31 +88,8 @@ const click = (
|
|
|
89
88
|
|
|
90
89
|
const selected = () => Array.from(list.cursor.selection.selectedIds).sort();
|
|
91
90
|
|
|
92
|
-
before(async () => {
|
|
93
|
-
const { JSDOM: JSDOMCtor } = await import("jsdom");
|
|
94
|
-
dom = new JSDOMCtor(
|
|
95
|
-
"<!doctype html><html><body><div id=root></div></body></html>",
|
|
96
|
-
{ url: "http://localhost/", pretendToBeVisual: true },
|
|
97
|
-
);
|
|
98
|
-
globalThis.window = dom.window as unknown as typeof globalThis.window;
|
|
99
|
-
globalThis.document = dom.window.document;
|
|
100
|
-
globalThis.HTMLElement = dom.window.HTMLElement;
|
|
101
|
-
globalThis.Element = dom.window.Element;
|
|
102
|
-
globalThis.SVGElement = dom.window.SVGElement;
|
|
103
|
-
globalThis.MutationObserver = dom.window.MutationObserver;
|
|
104
|
-
(
|
|
105
|
-
globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }
|
|
106
|
-
).IS_REACT_ACT_ENVIRONMENT = true;
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
after(() => {
|
|
110
|
-
dom.window.close();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
91
|
beforeEach(() => {
|
|
114
|
-
container =
|
|
115
|
-
"root",
|
|
116
|
-
) as unknown as HTMLElement;
|
|
92
|
+
container = document.getElementById("root") as unknown as HTMLElement;
|
|
117
93
|
container.innerHTML = "";
|
|
118
94
|
root = createRoot(container);
|
|
119
95
|
mount();
|
|
@@ -158,7 +134,7 @@ describe("useListKeyboard", () => {
|
|
|
158
134
|
});
|
|
159
135
|
|
|
160
136
|
it("leaves a key pressed outside its own element alone", () => {
|
|
161
|
-
press("j", {},
|
|
137
|
+
press("j", {}, document.body);
|
|
162
138
|
assert.equal(list.keyboard.focusedId, undefined);
|
|
163
139
|
});
|
|
164
140
|
|
|
@@ -7,24 +7,22 @@
|
|
|
7
7
|
* real PointerEvents at a real mounted node and assert on the callback and
|
|
8
8
|
* the DOM side effects react-aria owns (contextmenu suppression).
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
* pointerdown → threshold timer → onLongPress path, its global
|
|
10
|
+
* react-aria's pointerdown → threshold timer → onLongPress path, its global
|
|
12
11
|
* pointerup/pointercancel listeners, and its contextmenu suppression all
|
|
13
12
|
* need a real `document`/`window`/`PointerEvent`, which `renderToString`
|
|
14
13
|
* (the pattern used elsewhere in this repo for presentational components)
|
|
15
14
|
* cannot exercise.
|
|
16
15
|
*/
|
|
17
16
|
|
|
17
|
+
import "@remit/test-dom";
|
|
18
18
|
import assert from "node:assert/strict";
|
|
19
|
-
import {
|
|
20
|
-
import type { JSDOM } from "jsdom";
|
|
19
|
+
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
21
20
|
import { act, createElement } from "react";
|
|
22
21
|
import { createRoot, type Root } from "react-dom/client";
|
|
23
22
|
import { useLongPress } from "./use-long-press.js";
|
|
24
23
|
|
|
25
24
|
const THRESHOLD = 40;
|
|
26
25
|
|
|
27
|
-
let dom: JSDOM;
|
|
28
26
|
let container: HTMLElement;
|
|
29
27
|
let root: Root;
|
|
30
28
|
|
|
@@ -54,14 +52,14 @@ function mount(props: {
|
|
|
54
52
|
act(() => {
|
|
55
53
|
root.render(createElement(Row, props));
|
|
56
54
|
});
|
|
57
|
-
const row =
|
|
55
|
+
const row = document.getElementById("row");
|
|
58
56
|
assert.ok(row, "row did not mount");
|
|
59
57
|
return row;
|
|
60
58
|
}
|
|
61
59
|
|
|
62
60
|
function pointerDown(row: Element, pointerType = "touch") {
|
|
63
61
|
row.dispatchEvent(
|
|
64
|
-
new
|
|
62
|
+
new PointerEvent("pointerdown", {
|
|
65
63
|
bubbles: true,
|
|
66
64
|
pointerType,
|
|
67
65
|
pointerId: 1,
|
|
@@ -72,7 +70,7 @@ function pointerDown(row: Element, pointerType = "touch") {
|
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
function dispatchContextMenu(row: Element) {
|
|
75
|
-
const event = new
|
|
73
|
+
const event = new MouseEvent("contextmenu", {
|
|
76
74
|
bubbles: true,
|
|
77
75
|
cancelable: true,
|
|
78
76
|
});
|
|
@@ -81,8 +79,8 @@ function dispatchContextMenu(row: Element) {
|
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
function pointerUp() {
|
|
84
|
-
|
|
85
|
-
new
|
|
82
|
+
document.dispatchEvent(
|
|
83
|
+
new PointerEvent("pointerup", {
|
|
86
84
|
bubbles: true,
|
|
87
85
|
pointerType: "touch",
|
|
88
86
|
pointerId: 1,
|
|
@@ -94,7 +92,7 @@ function pointerUp() {
|
|
|
94
92
|
|
|
95
93
|
function pointerUpOn(row: Element) {
|
|
96
94
|
row.dispatchEvent(
|
|
97
|
-
new
|
|
95
|
+
new PointerEvent("pointerup", {
|
|
98
96
|
bubbles: true,
|
|
99
97
|
pointerType: "touch",
|
|
100
98
|
pointerId: 1,
|
|
@@ -105,44 +103,15 @@ function pointerUpOn(row: Element) {
|
|
|
105
103
|
}
|
|
106
104
|
|
|
107
105
|
function pointerCancel(row: Element) {
|
|
108
|
-
row.dispatchEvent(
|
|
109
|
-
new dom.window.PointerEvent("pointercancel", { bubbles: true }),
|
|
110
|
-
);
|
|
106
|
+
row.dispatchEvent(new PointerEvent("pointercancel", { bubbles: true }));
|
|
111
107
|
}
|
|
112
108
|
|
|
113
109
|
function wait(ms: number) {
|
|
114
110
|
return act(() => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
115
111
|
}
|
|
116
112
|
|
|
117
|
-
before(async () => {
|
|
118
|
-
const { JSDOM: JSDOMCtor } = await import("jsdom");
|
|
119
|
-
dom = new JSDOMCtor(
|
|
120
|
-
"<!doctype html><html><body><div id=root></div></body></html>",
|
|
121
|
-
{ url: "http://localhost/", pretendToBeVisual: true },
|
|
122
|
-
);
|
|
123
|
-
globalThis.window = dom.window as unknown as typeof globalThis.window;
|
|
124
|
-
globalThis.document = dom.window.document;
|
|
125
|
-
globalThis.HTMLElement = dom.window.HTMLElement;
|
|
126
|
-
globalThis.Element = dom.window.Element;
|
|
127
|
-
globalThis.SVGElement = dom.window.SVGElement;
|
|
128
|
-
globalThis.PointerEvent = dom.window.PointerEvent;
|
|
129
|
-
Object.defineProperty(globalThis, "navigator", {
|
|
130
|
-
value: dom.window.navigator,
|
|
131
|
-
configurable: true,
|
|
132
|
-
});
|
|
133
|
-
(
|
|
134
|
-
globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }
|
|
135
|
-
).IS_REACT_ACT_ENVIRONMENT = true;
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
after(() => {
|
|
139
|
-
dom.window.close();
|
|
140
|
-
});
|
|
141
|
-
|
|
142
113
|
beforeEach(() => {
|
|
143
|
-
container =
|
|
144
|
-
"root",
|
|
145
|
-
) as unknown as HTMLElement;
|
|
114
|
+
container = document.getElementById("root") as unknown as HTMLElement;
|
|
146
115
|
container.innerHTML = "";
|
|
147
116
|
root = createRoot(container);
|
|
148
117
|
});
|
|
@@ -241,7 +210,7 @@ describe("useLongPress (react-aria wrapper)", () => {
|
|
|
241
210
|
|
|
242
211
|
it("does not suppress contextmenu when no pointer interaction preceded it", async () => {
|
|
243
212
|
mount({ onLongPress: () => undefined });
|
|
244
|
-
const row =
|
|
213
|
+
const row = document.getElementById("row") as Element;
|
|
245
214
|
|
|
246
215
|
assert.equal(dispatchContextMenu(row).defaultPrevented, false);
|
|
247
216
|
});
|
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
* The rendered rows are read from the DOM, so a list body that caps, collapses
|
|
3
3
|
* or filters itself below its consumer still reports what it shows.
|
|
4
4
|
*/
|
|
5
|
+
import "@remit/test-dom";
|
|
5
6
|
import assert from "node:assert/strict";
|
|
6
|
-
import {
|
|
7
|
-
import type { JSDOM } from "jsdom";
|
|
7
|
+
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
8
8
|
import { act, createElement, useState } from "react";
|
|
9
9
|
import { createRoot, type Root } from "react-dom/client";
|
|
10
10
|
import { useRenderedRowIds } from "./use-rendered-row-ids.js";
|
|
11
11
|
|
|
12
|
-
let dom: JSDOM;
|
|
13
12
|
let container: HTMLElement;
|
|
14
13
|
let root: Root;
|
|
15
14
|
let rowIds: string[] | undefined;
|
|
@@ -39,28 +38,8 @@ const settle = async () => {
|
|
|
39
38
|
});
|
|
40
39
|
};
|
|
41
40
|
|
|
42
|
-
before(async () => {
|
|
43
|
-
const { JSDOM: JSDOMCtor } = await import("jsdom");
|
|
44
|
-
dom = new JSDOMCtor(
|
|
45
|
-
"<!doctype html><html><body><div id=root></div></body></html>",
|
|
46
|
-
{ url: "http://localhost/", pretendToBeVisual: true },
|
|
47
|
-
);
|
|
48
|
-
globalThis.window = dom.window as unknown as typeof globalThis.window;
|
|
49
|
-
globalThis.document = dom.window.document;
|
|
50
|
-
globalThis.HTMLElement = dom.window.HTMLElement;
|
|
51
|
-
globalThis.Element = dom.window.Element;
|
|
52
|
-
globalThis.MutationObserver = dom.window.MutationObserver;
|
|
53
|
-
(
|
|
54
|
-
globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }
|
|
55
|
-
).IS_REACT_ACT_ENVIRONMENT = true;
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
after(() => dom.window.close());
|
|
59
|
-
|
|
60
41
|
beforeEach(() => {
|
|
61
|
-
container =
|
|
62
|
-
"root",
|
|
63
|
-
) as unknown as HTMLElement;
|
|
42
|
+
container = document.getElementById("root") as unknown as HTMLElement;
|
|
64
43
|
container.innerHTML = "";
|
|
65
44
|
root = createRoot(container);
|
|
66
45
|
});
|