@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isEventTrusted } from "../events/trust.js";
|
|
2
2
|
import { WIDGET_CHECKBOX_SPINNER_CSS_CLASS, withAlpha } from "@prosopo/widget-skeleton";
|
|
3
|
-
import { useMemo, useState } from "react";
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { jsx, jsxs } from "@emotion/react/jsx-runtime";
|
|
5
5
|
import { css } from "@emotion/react";
|
|
6
6
|
import styled from "@emotion/styled";
|
|
@@ -35,12 +35,29 @@ var baseStyle = {
|
|
|
35
35
|
borderWidth: "2px"
|
|
36
36
|
};
|
|
37
37
|
var ID_LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
38
|
+
/**
|
|
39
|
+
* Whether nothing on the page holds focus, which is where the browser leaves it
|
|
40
|
+
* when the focused element is removed.
|
|
41
|
+
*/
|
|
42
|
+
var focusIsStranded = (element) => {
|
|
43
|
+
const { activeElement, body } = element.ownerDocument;
|
|
44
|
+
return activeElement === null || activeElement === body;
|
|
45
|
+
};
|
|
38
46
|
var FAQ_LINK = process.env.PROSOPO_DOCS_URL ? `${new URL(`${process.env.PROSOPO_DOCS_URL}/en/basics/faq/`).href}/` : "https://docs.prosopo.io/en/basics/faq/";
|
|
39
47
|
var generateRandomId = () => {
|
|
40
48
|
return Array.from({ length: 8 }, () => ID_LETTERS[Math.floor(Math.random() * ID_LETTERS.length)]).join("");
|
|
41
49
|
};
|
|
42
|
-
var Checkbox = ({ theme, onChange, checked, labelText, error, loading }) => {
|
|
50
|
+
var Checkbox = ({ theme, onChange, checked, labelText, error, loading, loadingText = "Checking that you are human" }) => {
|
|
43
51
|
const [hover, setHover] = useState(false);
|
|
52
|
+
const inputRef = useRef(null);
|
|
53
|
+
const spinnerRef = useRef(null);
|
|
54
|
+
const hadFocus = useRef(false);
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!hadFocus.current) return;
|
|
57
|
+
const target = loading ? spinnerRef.current : inputRef.current;
|
|
58
|
+
if (!target || !focusIsStranded(target)) return;
|
|
59
|
+
target.focus();
|
|
60
|
+
}, [loading]);
|
|
44
61
|
const checkboxFocus = useMemo(() => css`
|
|
45
62
|
&:focus-visible {
|
|
46
63
|
outline: 3px solid ${theme.palette.primary.main};
|
|
@@ -102,20 +119,29 @@ var Checkbox = ({ theme, onChange, checked, labelText, error, loading }) => {
|
|
|
102
119
|
minHeight: "58px"
|
|
103
120
|
},
|
|
104
121
|
children: [loading ? /* @__PURE__ */ jsx("div", {
|
|
122
|
+
ref: spinnerRef,
|
|
105
123
|
className: WIDGET_CHECKBOX_SPINNER_CSS_CLASS,
|
|
106
|
-
|
|
124
|
+
role: "status",
|
|
125
|
+
tabIndex: -1,
|
|
126
|
+
"aria-label": loadingText
|
|
107
127
|
}) : /* @__PURE__ */ jsx("input", {
|
|
128
|
+
ref: inputRef,
|
|
108
129
|
name: id,
|
|
109
130
|
id,
|
|
110
131
|
onMouseEnter: () => setHover(true),
|
|
111
132
|
onMouseLeave: () => setHover(false),
|
|
133
|
+
onFocus: () => {
|
|
134
|
+
hadFocus.current = true;
|
|
135
|
+
},
|
|
136
|
+
onBlur: () => {
|
|
137
|
+
hadFocus.current = false;
|
|
138
|
+
},
|
|
112
139
|
css: [
|
|
113
140
|
checkboxBefore,
|
|
114
141
|
checkboxForcedColors,
|
|
115
142
|
checkboxFocus
|
|
116
143
|
],
|
|
117
144
|
type: "checkbox",
|
|
118
|
-
"aria-live": "assertive",
|
|
119
145
|
"aria-label": labelText,
|
|
120
146
|
onKeyDown: (e) => {
|
|
121
147
|
if (!isEventTrusted(e)) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../src/reactComponents/Checkbox.tsx"],"names":[],"mappings":";AAcA,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,MAAM,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAEN,iCAAiC,EACjC,SAAS,GACT,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAIN,OAAO,EACP,QAAQ,GACR,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"Checkbox.js","sourceRoot":"","sources":["../../src/reactComponents/Checkbox.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "@emotion/react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
2
|
import { PlacementEnum } from "@prosopo/types";
|
|
3
3
|
import { createRoot } from "react-dom/client";
|
|
4
4
|
import { act } from "react-dom/test-utils";
|
|
@@ -9,9 +9,15 @@ let root;
|
|
|
9
9
|
let anchor;
|
|
10
10
|
const layer = () => document.querySelector(".prosopo-challenge-surface");
|
|
11
11
|
const content = () => document.querySelector(".prosopo-challenge-content");
|
|
12
|
-
const render = ({ placement, withAnchor = true, onDismiss, show = true, }) => {
|
|
12
|
+
const render = ({ placement, withAnchor = true, onDismiss, show = true, dialogLabel, focusableChildren = 0, }) => {
|
|
13
13
|
act(() => {
|
|
14
|
-
root.render(
|
|
14
|
+
root.render(_jsxs(ChallengeSurface, { show: show, placement: placement, anchor: withAnchor ? anchor : null, onDismiss: onDismiss, dialogLabel: dialogLabel, children: [_jsx("div", { "data-testid": "challenge", children: "challenge" }), Array.from({ length: focusableChildren }, (_, index) => `button-${index}`).map((id) => (_jsx("button", { type: "button", children: id }, id)))] }));
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
const buttons = () => Array.from(content()?.querySelectorAll("button") ?? []);
|
|
18
|
+
const pressTab = (shiftKey = false) => {
|
|
19
|
+
act(() => {
|
|
20
|
+
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Tab", bubbles: true, shiftKey }));
|
|
15
21
|
});
|
|
16
22
|
};
|
|
17
23
|
beforeEach(() => {
|
|
@@ -47,6 +53,18 @@ describe("popup", () => {
|
|
|
47
53
|
expect(layer()?.style.display).toBe("flex");
|
|
48
54
|
expect(layer()?.style.pointerEvents).toBe("");
|
|
49
55
|
});
|
|
56
|
+
it("keeps a tall challenge inside the viewport rather than clipping its top", () => {
|
|
57
|
+
render({ placement: PlacementEnum.popup });
|
|
58
|
+
const style = content()?.style;
|
|
59
|
+
expect(style?.position).not.toBe("absolute");
|
|
60
|
+
expect(style?.transform).toBe("");
|
|
61
|
+
expect(style?.maxHeight).toBe("100%");
|
|
62
|
+
expect(style?.overflowY).toBe("auto");
|
|
63
|
+
});
|
|
64
|
+
it("sizes the layer to the visible viewport, not the retracted-toolbar one", () => {
|
|
65
|
+
render({ placement: PlacementEnum.popup });
|
|
66
|
+
expect(layer()?.style.minHeight).toBe("100dvh");
|
|
67
|
+
});
|
|
50
68
|
it("ignores an outside click", () => {
|
|
51
69
|
const onDismiss = vi.fn();
|
|
52
70
|
render({ placement: PlacementEnum.popup, onDismiss });
|
|
@@ -120,4 +138,66 @@ describe("dismissing with the keyboard", () => {
|
|
|
120
138
|
expect(onDismiss).not.toHaveBeenCalled();
|
|
121
139
|
});
|
|
122
140
|
});
|
|
141
|
+
describe("as a dialog", () => {
|
|
142
|
+
it("stays inert for a challenge that does not name itself", () => {
|
|
143
|
+
render({});
|
|
144
|
+
expect(content()?.getAttribute("role")).toBeNull();
|
|
145
|
+
expect(content()?.getAttribute("aria-modal")).toBeNull();
|
|
146
|
+
});
|
|
147
|
+
it("names itself to assistive tech once a label is given", () => {
|
|
148
|
+
render({ dialogLabel: "Puzzle challenge" });
|
|
149
|
+
expect(content()?.getAttribute("role")).toBe("dialog");
|
|
150
|
+
expect(content()?.getAttribute("aria-modal")).toBe("true");
|
|
151
|
+
expect(content()?.getAttribute("aria-label")).toBe("Puzzle challenge");
|
|
152
|
+
});
|
|
153
|
+
it("takes focus so a screen reader lands on the challenge", () => {
|
|
154
|
+
render({ dialogLabel: "Puzzle challenge", focusableChildren: 2 });
|
|
155
|
+
expect(document.activeElement).toBe(buttons()[0]);
|
|
156
|
+
});
|
|
157
|
+
it("falls back to the panel when it holds nothing focusable", () => {
|
|
158
|
+
render({ dialogLabel: "Puzzle challenge" });
|
|
159
|
+
expect(document.activeElement).toBe(content());
|
|
160
|
+
});
|
|
161
|
+
it("gives focus back to whatever opened it", () => {
|
|
162
|
+
const opener = document.createElement("button");
|
|
163
|
+
document.body.appendChild(opener);
|
|
164
|
+
opener.focus();
|
|
165
|
+
render({ dialogLabel: "Puzzle challenge", focusableChildren: 1 });
|
|
166
|
+
expect(document.activeElement).not.toBe(opener);
|
|
167
|
+
act(() => root.unmount());
|
|
168
|
+
expect(document.activeElement).toBe(opener);
|
|
169
|
+
opener.remove();
|
|
170
|
+
root = createRoot(container);
|
|
171
|
+
});
|
|
172
|
+
it("wraps tab from the last control back to the first", () => {
|
|
173
|
+
render({ dialogLabel: "Puzzle challenge", focusableChildren: 2 });
|
|
174
|
+
const [first, last] = buttons();
|
|
175
|
+
last?.focus();
|
|
176
|
+
pressTab();
|
|
177
|
+
expect(document.activeElement).toBe(first);
|
|
178
|
+
});
|
|
179
|
+
it("wraps shift-tab from the first control back to the last", () => {
|
|
180
|
+
render({ dialogLabel: "Puzzle challenge", focusableChildren: 2 });
|
|
181
|
+
const [first, last] = buttons();
|
|
182
|
+
first?.focus();
|
|
183
|
+
pressTab(true);
|
|
184
|
+
expect(document.activeElement).toBe(last);
|
|
185
|
+
});
|
|
186
|
+
it("pulls focus back in if it has escaped to the page", () => {
|
|
187
|
+
const outside = document.createElement("button");
|
|
188
|
+
document.body.appendChild(outside);
|
|
189
|
+
render({ dialogLabel: "Puzzle challenge", focusableChildren: 2 });
|
|
190
|
+
outside.focus();
|
|
191
|
+
pressTab();
|
|
192
|
+
expect(document.activeElement).toBe(buttons()[0]);
|
|
193
|
+
outside.remove();
|
|
194
|
+
});
|
|
195
|
+
it("leaves tab alone for a challenge that is not a dialog", () => {
|
|
196
|
+
render({ focusableChildren: 2 });
|
|
197
|
+
const [, last] = buttons();
|
|
198
|
+
last?.focus();
|
|
199
|
+
pressTab();
|
|
200
|
+
expect(document.activeElement).toBe(last);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
123
203
|
//# sourceMappingURL=challengeSurfaceRender.unit.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"challengeSurfaceRender.unit.test.js","sourceRoot":"","sources":["../../src/tests/challengeSurfaceRender.unit.test.tsx"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"challengeSurfaceRender.unit.test.js","sourceRoot":"","sources":["../../src/tests/challengeSurfaceRender.unit.test.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prosopo/procaptcha-common",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"author": "PROSOPO LIMITED <info@prosopo.io>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -34,15 +34,15 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@emotion/react": "11.14.0",
|
|
36
36
|
"@emotion/styled": "11.14.1",
|
|
37
|
-
"@prosopo/account": "2.8.
|
|
38
|
-
"@prosopo/load-balancer": "2.10.
|
|
39
|
-
"@prosopo/types": "5.8.
|
|
37
|
+
"@prosopo/account": "2.8.92",
|
|
38
|
+
"@prosopo/load-balancer": "2.10.44",
|
|
39
|
+
"@prosopo/types": "5.8.3",
|
|
40
40
|
"@prosopo/widget-skeleton": "2.8.7",
|
|
41
41
|
"react": "19.2.8",
|
|
42
42
|
"react-dom": "19.2.8"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@prosopo/config": "3.3.
|
|
45
|
+
"@prosopo/config": "3.3.16",
|
|
46
46
|
"@types/jsdom": "30.0.0",
|
|
47
47
|
"@types/node": "22.10.2",
|
|
48
48
|
"@types/react": "19.2.18",
|
|
@@ -42,37 +42,32 @@ interface ChallengeSurfaceProps {
|
|
|
42
42
|
scrim?: SurfaceScrim;
|
|
43
43
|
/** Called on Escape, and on an outside click when floating. */
|
|
44
44
|
onDismiss?: () => void;
|
|
45
|
-
/** Lifts the popup content on iOS, where Safari's bottom bar overlaps a centred dialog. */
|
|
46
|
-
popupIosLift?: boolean;
|
|
47
45
|
className?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Accessible name for the panel. Supplying one turns the panel into a modal
|
|
48
|
+
* dialog: it takes focus when it opens, keeps Tab inside itself while it is
|
|
49
|
+
* open, and hands focus back to whatever opened it on close. Challenges
|
|
50
|
+
* that have nothing focusable to offer leave it unset and stay inert.
|
|
51
|
+
*/
|
|
52
|
+
dialogLabel?: string;
|
|
48
53
|
}
|
|
49
54
|
|
|
50
|
-
const
|
|
51
|
-
|
|
55
|
+
const FOCUSABLE_SELECTOR = [
|
|
56
|
+
"a[href]",
|
|
57
|
+
"button:not([disabled])",
|
|
58
|
+
"input:not([disabled])",
|
|
59
|
+
"select:not([disabled])",
|
|
60
|
+
"textarea:not([disabled])",
|
|
61
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
62
|
+
].join(",");
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
|
|
64
|
+
const focusableWithin = (root: HTMLElement): HTMLElement[] =>
|
|
65
|
+
Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));
|
|
55
66
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
transform: translate(-50%, -50%);
|
|
59
|
-
}
|
|
60
|
-
@supports (-webkit-touch-callout: none) {
|
|
61
|
-
.prosopo-challenge-content--ios-lift {
|
|
62
|
-
transform: translate(-50%, -100%);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
`;
|
|
66
|
-
|
|
67
|
-
const ensureIosLiftStyles = (): void => {
|
|
68
|
-
if (typeof document === "undefined") return;
|
|
69
|
-
if (document.getElementById(IOS_LIFT_STYLE_ID)) return;
|
|
67
|
+
const SURFACE_Z_INDEX = 2147483646;
|
|
68
|
+
const CONTENT_Z_INDEX = 2147483647;
|
|
70
69
|
|
|
71
|
-
|
|
72
|
-
style.id = IOS_LIFT_STYLE_ID;
|
|
73
|
-
style.textContent = IOS_LIFT_CSS;
|
|
74
|
-
document.head.appendChild(style);
|
|
75
|
-
};
|
|
70
|
+
const POPUP_EDGE_GAP_PX = 8;
|
|
76
71
|
|
|
77
72
|
const FLOAT_GAP_PX = 8;
|
|
78
73
|
|
|
@@ -113,8 +108,8 @@ const ChallengeSurface = React.memo((props: ChallengeSurfaceProps) => {
|
|
|
113
108
|
anchor,
|
|
114
109
|
scrim = "none",
|
|
115
110
|
onDismiss,
|
|
116
|
-
popupIosLift = false,
|
|
117
111
|
className,
|
|
112
|
+
dialogLabel,
|
|
118
113
|
} = props;
|
|
119
114
|
|
|
120
115
|
const contentRef = useRef<HTMLDivElement>(null);
|
|
@@ -183,6 +178,53 @@ const ChallengeSurface = React.memo((props: ChallengeSurfaceProps) => {
|
|
|
183
178
|
return () => document.removeEventListener("keydown", onKeyDown);
|
|
184
179
|
}, [show, onDismiss]);
|
|
185
180
|
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
if (!show || !dialogLabel) return;
|
|
183
|
+
|
|
184
|
+
const content = contentRef.current;
|
|
185
|
+
if (!content) return;
|
|
186
|
+
|
|
187
|
+
const opener =
|
|
188
|
+
document.activeElement instanceof HTMLElement
|
|
189
|
+
? document.activeElement
|
|
190
|
+
: null;
|
|
191
|
+
// The panel itself is the fallback so the dialog's name is still
|
|
192
|
+
// announced when it holds nothing focusable.
|
|
193
|
+
(focusableWithin(content)[0] ?? content).focus();
|
|
194
|
+
|
|
195
|
+
return () => opener?.focus();
|
|
196
|
+
}, [show, dialogLabel]);
|
|
197
|
+
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
if (!show || !dialogLabel) return;
|
|
200
|
+
|
|
201
|
+
// Tabbing off either end wraps back inside. A modal hides the rest of
|
|
202
|
+
// the page from assistive tech, so focus landing out there leaves the
|
|
203
|
+
// user somewhere they have no way to perceive or get back from.
|
|
204
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
205
|
+
if (event.key !== "Tab") return;
|
|
206
|
+
const content = contentRef.current;
|
|
207
|
+
if (!content) return;
|
|
208
|
+
|
|
209
|
+
const focusable = focusableWithin(content);
|
|
210
|
+
const first = focusable[0] ?? content;
|
|
211
|
+
const last = focusable[focusable.length - 1] ?? content;
|
|
212
|
+
const active = document.activeElement;
|
|
213
|
+
const outside = !content.contains(active);
|
|
214
|
+
|
|
215
|
+
if (event.shiftKey && (outside || active === first)) {
|
|
216
|
+
event.preventDefault();
|
|
217
|
+
last.focus();
|
|
218
|
+
} else if (!event.shiftKey && (outside || active === last)) {
|
|
219
|
+
event.preventDefault();
|
|
220
|
+
first.focus();
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
document.addEventListener("keydown", onKeyDown);
|
|
225
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
226
|
+
}, [show, dialogLabel]);
|
|
227
|
+
|
|
186
228
|
useEffect(() => {
|
|
187
229
|
if (!show || !isFloating || !onDismiss) return;
|
|
188
230
|
|
|
@@ -198,10 +240,6 @@ const ChallengeSurface = React.memo((props: ChallengeSurfaceProps) => {
|
|
|
198
240
|
return () => document.removeEventListener("pointerdown", onPointerDown);
|
|
199
241
|
}, [show, isFloating, anchor, onDismiss]);
|
|
200
242
|
|
|
201
|
-
useEffect(() => {
|
|
202
|
-
if (popupIosLift && !isFloating) ensureIosLiftStyles();
|
|
203
|
-
}, [popupIosLift, isFloating]);
|
|
204
|
-
|
|
205
243
|
if (typeof document === "undefined") return null;
|
|
206
244
|
|
|
207
245
|
const layerStyle: CSSProperties = isFloating
|
|
@@ -227,7 +265,12 @@ const ChallengeSurface = React.memo((props: ChallengeSurfaceProps) => {
|
|
|
227
265
|
display: show ? "flex" : "none",
|
|
228
266
|
alignItems: "center",
|
|
229
267
|
justifyContent: "center",
|
|
230
|
-
|
|
268
|
+
// `dvh`, not `vh`: on iOS Safari `100vh` is the toolbar-retracted
|
|
269
|
+
// height, which would make this box taller than the visible area and
|
|
270
|
+
// centre the challenge underneath the bottom bar.
|
|
271
|
+
minHeight: "100dvh",
|
|
272
|
+
padding: `${POPUP_EDGE_GAP_PX}px`,
|
|
273
|
+
boxSizing: "border-box",
|
|
231
274
|
backgroundColor:
|
|
232
275
|
scrim === "dim" && show ? "rgba(0, 0, 0, 0.4)" : "transparent",
|
|
233
276
|
transition: "background-color 0.3s ease",
|
|
@@ -244,13 +287,17 @@ const ChallengeSurface = React.memo((props: ChallengeSurfaceProps) => {
|
|
|
244
287
|
visibility: floatPosition ? "visible" : "hidden",
|
|
245
288
|
}
|
|
246
289
|
: {
|
|
247
|
-
|
|
248
|
-
top:
|
|
249
|
-
|
|
250
|
-
//
|
|
251
|
-
|
|
290
|
+
// Centred as a flex item by the layer rather than by
|
|
291
|
+
// `top/left: 50%` and a translate. An out-of-flow panel taller than
|
|
292
|
+
// the viewport overflows off both edges and its top is unreachable;
|
|
293
|
+
// in flow it is bounded by `maxHeight` and scrolls instead.
|
|
294
|
+
position: "relative",
|
|
252
295
|
zIndex: CONTENT_Z_INDEX,
|
|
253
296
|
boxSizing: "border-box",
|
|
297
|
+
maxWidth: "100%",
|
|
298
|
+
maxHeight: "100%",
|
|
299
|
+
overflowY: "auto",
|
|
300
|
+
overscrollBehavior: "contain",
|
|
254
301
|
};
|
|
255
302
|
|
|
256
303
|
return createPortal(
|
|
@@ -264,14 +311,17 @@ const ChallengeSurface = React.memo((props: ChallengeSurfaceProps) => {
|
|
|
264
311
|
.join(" ")}
|
|
265
312
|
style={layerStyle}
|
|
266
313
|
>
|
|
314
|
+
{/* biome-ignore lint/a11y/useSemanticElements: <dialog> brings its own
|
|
315
|
+
top-layer and backdrop, which would fight the portal's own
|
|
316
|
+
stacking and the float placement computed above. */}
|
|
267
317
|
<div
|
|
268
318
|
ref={contentRef}
|
|
269
|
-
className=
|
|
270
|
-
popupIosLift && !isFloating
|
|
271
|
-
? "prosopo-challenge-content prosopo-challenge-content--ios-lift"
|
|
272
|
-
: "prosopo-challenge-content"
|
|
273
|
-
}
|
|
319
|
+
className="prosopo-challenge-content"
|
|
274
320
|
style={contentStyle}
|
|
321
|
+
role={dialogLabel ? "dialog" : undefined}
|
|
322
|
+
aria-modal={dialogLabel ? true : undefined}
|
|
323
|
+
aria-label={dialogLabel}
|
|
324
|
+
tabIndex={dialogLabel ? -1 : undefined}
|
|
275
325
|
>
|
|
276
326
|
{children}
|
|
277
327
|
</div>
|
|
@@ -23,7 +23,9 @@ import {
|
|
|
23
23
|
type ButtonHTMLAttributes,
|
|
24
24
|
type CSSProperties,
|
|
25
25
|
type FC,
|
|
26
|
+
useEffect,
|
|
26
27
|
useMemo,
|
|
28
|
+
useRef,
|
|
27
29
|
useState,
|
|
28
30
|
} from "react";
|
|
29
31
|
import { isEventTrusted } from "../events/trust.js";
|
|
@@ -36,6 +38,8 @@ interface CheckboxProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
36
38
|
labelText: string;
|
|
37
39
|
error?: string;
|
|
38
40
|
loading: boolean;
|
|
41
|
+
/** Name for the spinner that stands in for the box while it is working. */
|
|
42
|
+
loadingText?: string;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
const checkboxBefore = css`{
|
|
@@ -79,6 +83,15 @@ const baseStyle: CSSProperties = {
|
|
|
79
83
|
|
|
80
84
|
const ID_LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
81
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Whether nothing on the page holds focus, which is where the browser leaves it
|
|
88
|
+
* when the focused element is removed.
|
|
89
|
+
*/
|
|
90
|
+
const focusIsStranded = (element: HTMLElement): boolean => {
|
|
91
|
+
const { activeElement, body } = element.ownerDocument;
|
|
92
|
+
return activeElement === null || activeElement === body;
|
|
93
|
+
};
|
|
94
|
+
|
|
82
95
|
const FAQ_LINK = process.env.PROSOPO_DOCS_URL
|
|
83
96
|
? `${new URL(`${process.env.PROSOPO_DOCS_URL}/en/basics/faq/`).href}/`
|
|
84
97
|
: "https://docs.prosopo.io/en/basics/faq/";
|
|
@@ -101,8 +114,32 @@ export const Checkbox: FC<CheckboxProps> = ({
|
|
|
101
114
|
labelText,
|
|
102
115
|
error,
|
|
103
116
|
loading,
|
|
117
|
+
loadingText = "Checking that you are human",
|
|
104
118
|
}: CheckboxProps) => {
|
|
105
119
|
const [hover, setHover] = useState(false);
|
|
120
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
121
|
+
const spinnerRef = useRef<HTMLDivElement>(null);
|
|
122
|
+
// Removing a focused element drops focus to the body without firing blur,
|
|
123
|
+
// so this survives the swap and says whether the swap cost the user their
|
|
124
|
+
// place on the page.
|
|
125
|
+
const hadFocus = useRef(false);
|
|
126
|
+
|
|
127
|
+
// The spinner replaces the box in the DOM rather than covering it, which
|
|
128
|
+
// strands a keyboard user at the top of the page for as long as the check
|
|
129
|
+
// runs, with nothing said about why the box vanished. Handing focus across
|
|
130
|
+
// the swap and back keeps them where they were and gets each side's name
|
|
131
|
+
// read out as it arrives.
|
|
132
|
+
//
|
|
133
|
+
// Only focus the swap itself stranded is claimed back. The check lasts as
|
|
134
|
+
// long as the network does, and a user who spent that time moving on to the
|
|
135
|
+
// host page's own fields would otherwise be dragged out of them, mid
|
|
136
|
+
// keystroke, by a widget they had finished with.
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
if (!hadFocus.current) return;
|
|
139
|
+
const target = loading ? spinnerRef.current : inputRef.current;
|
|
140
|
+
if (!target || !focusIsStranded(target)) return;
|
|
141
|
+
target.focus();
|
|
142
|
+
}, [loading]);
|
|
106
143
|
|
|
107
144
|
// M3 focus indicator: a 3dp outline offset by 2dp, drawn only for keyboard
|
|
108
145
|
// focus. The control previously had no focus affordance at all.
|
|
@@ -188,18 +225,30 @@ export const Checkbox: FC<CheckboxProps> = ({
|
|
|
188
225
|
>
|
|
189
226
|
{loading ? (
|
|
190
227
|
<div
|
|
228
|
+
ref={spinnerRef}
|
|
191
229
|
className={WIDGET_CHECKBOX_SPINNER_CSS_CLASS}
|
|
192
|
-
|
|
230
|
+
role="status"
|
|
231
|
+
// Focusable only programmatically: it stands in for the input it
|
|
232
|
+
// replaced, so the name below is what a screen reader reads when
|
|
233
|
+
// focus is handed over.
|
|
234
|
+
tabIndex={-1}
|
|
235
|
+
aria-label={loadingText}
|
|
193
236
|
/>
|
|
194
237
|
) : (
|
|
195
238
|
<input
|
|
239
|
+
ref={inputRef}
|
|
196
240
|
name={id}
|
|
197
241
|
id={id}
|
|
198
242
|
onMouseEnter={() => setHover(true)}
|
|
199
243
|
onMouseLeave={() => setHover(false)}
|
|
244
|
+
onFocus={() => {
|
|
245
|
+
hadFocus.current = true;
|
|
246
|
+
}}
|
|
247
|
+
onBlur={() => {
|
|
248
|
+
hadFocus.current = false;
|
|
249
|
+
}}
|
|
200
250
|
css={[checkboxBefore, checkboxForcedColors, checkboxFocus]}
|
|
201
251
|
type={"checkbox"}
|
|
202
|
-
aria-live={"assertive"}
|
|
203
252
|
aria-label={labelText}
|
|
204
253
|
onKeyDown={(e) => {
|
|
205
254
|
if (!isEventTrusted(e)) {
|