@prosopo/procaptcha-common 2.13.2 → 2.14.0
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/.turbo/turbo-build$colon$cjs.log +4 -4
- package/.turbo/turbo-build$colon$tsc.log +18 -18
- package/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +85 -0
- package/dist/cjs/reactComponents/ChallengeSurface.cjs +55 -30
- package/dist/cjs/reactComponents/Checkbox.cjs +29 -3
- package/dist/reactComponents/ChallengeSurface.d.ts +1 -1
- package/dist/reactComponents/ChallengeSurface.d.ts.map +1 -1
- package/dist/reactComponents/ChallengeSurface.js +55 -30
- package/dist/reactComponents/ChallengeSurface.js.map +1 -1
- package/dist/reactComponents/Checkbox.d.ts +1 -0
- package/dist/reactComponents/Checkbox.d.ts.map +1 -1
- package/dist/reactComponents/Checkbox.js +30 -4
- package/dist/reactComponents/Checkbox.js.map +1 -1
- package/dist/tests/challengeSurfaceRender.unit.test.js +83 -3
- package/dist/tests/challengeSurfaceRender.unit.test.js.map +1 -1
- package/package.json +5 -5
- package/src/reactComponents/ChallengeSurface.tsx +91 -41
- package/src/reactComponents/Checkbox.tsx +51 -2
- package/src/tests/challengeSurfaceRender.unit.test.tsx +134 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -35,6 +35,8 @@ interface RenderArgs {
|
|
|
35
35
|
withAnchor?: boolean;
|
|
36
36
|
onDismiss?: () => void;
|
|
37
37
|
show?: boolean;
|
|
38
|
+
dialogLabel?: string;
|
|
39
|
+
focusableChildren?: number;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
const render = ({
|
|
@@ -42,6 +44,8 @@ const render = ({
|
|
|
42
44
|
withAnchor = true,
|
|
43
45
|
onDismiss,
|
|
44
46
|
show = true,
|
|
47
|
+
dialogLabel,
|
|
48
|
+
focusableChildren = 0,
|
|
45
49
|
}: RenderArgs): void => {
|
|
46
50
|
act(() => {
|
|
47
51
|
root.render(
|
|
@@ -50,13 +54,34 @@ const render = ({
|
|
|
50
54
|
placement={placement}
|
|
51
55
|
anchor={withAnchor ? anchor : null}
|
|
52
56
|
onDismiss={onDismiss}
|
|
57
|
+
dialogLabel={dialogLabel}
|
|
53
58
|
>
|
|
54
59
|
<div data-testid="challenge">challenge</div>
|
|
60
|
+
{Array.from(
|
|
61
|
+
{ length: focusableChildren },
|
|
62
|
+
(_, index) => `button-${index}`,
|
|
63
|
+
).map((id) => (
|
|
64
|
+
<button key={id} type="button">
|
|
65
|
+
{id}
|
|
66
|
+
</button>
|
|
67
|
+
))}
|
|
55
68
|
</ChallengeSurface>,
|
|
56
69
|
);
|
|
57
70
|
});
|
|
58
71
|
};
|
|
59
72
|
|
|
73
|
+
/** Scoped to the panel: tests put buttons on the page around it too. */
|
|
74
|
+
const buttons = (): HTMLButtonElement[] =>
|
|
75
|
+
Array.from(content()?.querySelectorAll("button") ?? []);
|
|
76
|
+
|
|
77
|
+
const pressTab = (shiftKey = false): void => {
|
|
78
|
+
act(() => {
|
|
79
|
+
document.dispatchEvent(
|
|
80
|
+
new KeyboardEvent("keydown", { key: "Tab", bubbles: true, shiftKey }),
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
60
85
|
beforeEach(() => {
|
|
61
86
|
container = document.createElement("div");
|
|
62
87
|
anchor = document.createElement("div");
|
|
@@ -100,6 +125,29 @@ describe("popup", () => {
|
|
|
100
125
|
expect(layer()?.style.pointerEvents).toBe("");
|
|
101
126
|
});
|
|
102
127
|
|
|
128
|
+
it("keeps a tall challenge inside the viewport rather than clipping its top", () => {
|
|
129
|
+
// A panel taller than the viewport used to be translated out of flow by
|
|
130
|
+
// its own full height, putting the instruction ("Select all containing
|
|
131
|
+
// ...") and the first row of images above the top of the screen with no
|
|
132
|
+
// way to scroll back to them. In flow it is bounded and scrolls instead.
|
|
133
|
+
render({ placement: PlacementEnum.popup });
|
|
134
|
+
|
|
135
|
+
const style = content()?.style;
|
|
136
|
+
expect(style?.position).not.toBe("absolute");
|
|
137
|
+
expect(style?.transform).toBe("");
|
|
138
|
+
expect(style?.maxHeight).toBe("100%");
|
|
139
|
+
expect(style?.overflowY).toBe("auto");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("sizes the layer to the visible viewport, not the retracted-toolbar one", () => {
|
|
143
|
+
// `100vh` on iOS Safari is the toolbar-retracted height. Forcing the
|
|
144
|
+
// centring box to it put the challenge's lower half under the bottom bar,
|
|
145
|
+
// which is what the removed translate hack was compensating for.
|
|
146
|
+
render({ placement: PlacementEnum.popup });
|
|
147
|
+
|
|
148
|
+
expect(layer()?.style.minHeight).toBe("100dvh");
|
|
149
|
+
});
|
|
150
|
+
|
|
103
151
|
it("ignores an outside click", () => {
|
|
104
152
|
const onDismiss = vi.fn();
|
|
105
153
|
render({ placement: PlacementEnum.popup, onDismiss });
|
|
@@ -205,3 +253,89 @@ describe("dismissing with the keyboard", () => {
|
|
|
205
253
|
expect(onDismiss).not.toHaveBeenCalled();
|
|
206
254
|
});
|
|
207
255
|
});
|
|
256
|
+
|
|
257
|
+
describe("as a dialog", () => {
|
|
258
|
+
it("stays inert for a challenge that does not name itself", () => {
|
|
259
|
+
render({});
|
|
260
|
+
|
|
261
|
+
expect(content()?.getAttribute("role")).toBeNull();
|
|
262
|
+
expect(content()?.getAttribute("aria-modal")).toBeNull();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("names itself to assistive tech once a label is given", () => {
|
|
266
|
+
render({ dialogLabel: "Puzzle challenge" });
|
|
267
|
+
|
|
268
|
+
expect(content()?.getAttribute("role")).toBe("dialog");
|
|
269
|
+
expect(content()?.getAttribute("aria-modal")).toBe("true");
|
|
270
|
+
expect(content()?.getAttribute("aria-label")).toBe("Puzzle challenge");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("takes focus so a screen reader lands on the challenge", () => {
|
|
274
|
+
render({ dialogLabel: "Puzzle challenge", focusableChildren: 2 });
|
|
275
|
+
|
|
276
|
+
expect(document.activeElement).toBe(buttons()[0]);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("falls back to the panel when it holds nothing focusable", () => {
|
|
280
|
+
render({ dialogLabel: "Puzzle challenge" });
|
|
281
|
+
|
|
282
|
+
expect(document.activeElement).toBe(content());
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("gives focus back to whatever opened it", () => {
|
|
286
|
+
const opener = document.createElement("button");
|
|
287
|
+
document.body.appendChild(opener);
|
|
288
|
+
opener.focus();
|
|
289
|
+
|
|
290
|
+
render({ dialogLabel: "Puzzle challenge", focusableChildren: 1 });
|
|
291
|
+
expect(document.activeElement).not.toBe(opener);
|
|
292
|
+
|
|
293
|
+
act(() => root.unmount());
|
|
294
|
+
expect(document.activeElement).toBe(opener);
|
|
295
|
+
|
|
296
|
+
opener.remove();
|
|
297
|
+
root = createRoot(container);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("wraps tab from the last control back to the first", () => {
|
|
301
|
+
render({ dialogLabel: "Puzzle challenge", focusableChildren: 2 });
|
|
302
|
+
const [first, last] = buttons();
|
|
303
|
+
last?.focus();
|
|
304
|
+
|
|
305
|
+
pressTab();
|
|
306
|
+
|
|
307
|
+
expect(document.activeElement).toBe(first);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("wraps shift-tab from the first control back to the last", () => {
|
|
311
|
+
render({ dialogLabel: "Puzzle challenge", focusableChildren: 2 });
|
|
312
|
+
const [first, last] = buttons();
|
|
313
|
+
first?.focus();
|
|
314
|
+
|
|
315
|
+
pressTab(true);
|
|
316
|
+
|
|
317
|
+
expect(document.activeElement).toBe(last);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("pulls focus back in if it has escaped to the page", () => {
|
|
321
|
+
const outside = document.createElement("button");
|
|
322
|
+
document.body.appendChild(outside);
|
|
323
|
+
render({ dialogLabel: "Puzzle challenge", focusableChildren: 2 });
|
|
324
|
+
outside.focus();
|
|
325
|
+
|
|
326
|
+
pressTab();
|
|
327
|
+
|
|
328
|
+
expect(document.activeElement).toBe(buttons()[0]);
|
|
329
|
+
outside.remove();
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it("leaves tab alone for a challenge that is not a dialog", () => {
|
|
333
|
+
render({ focusableChildren: 2 });
|
|
334
|
+
const [, last] = buttons();
|
|
335
|
+
last?.focus();
|
|
336
|
+
|
|
337
|
+
pressTab();
|
|
338
|
+
|
|
339
|
+
expect(document.activeElement).toBe(last);
|
|
340
|
+
});
|
|
341
|
+
});
|