mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 11:07:10 +00:00
Compose for wasm never focuses the semantics node carrying the testTag. It proxies keystrokes through a hidden 1px backing input that is a sibling of the a11y tree, so the node the runner tapped never held focus and confirmFocus refused to type into every Compose text field. Focus is re-attributed to the smallest editable whose box holds the caret's centre. Centre-point rather than full containment because the caret's height comes from the text style and the field's from its layout box, so a taller font would silently drop back to refusing.
81 lines
3.0 KiB
HTML
81 lines
3.0 KiB
HTML
<!doctype html>
|
|
<html id="page">
|
|
<head id="page-head">
|
|
<meta id="page-charset" charset="utf-8" />
|
|
<title id="page-title">compose backing input</title>
|
|
</head>
|
|
<body id="page-body">
|
|
<!-- Shaped like Compose for Web: keystrokes go through a 1px transparent
|
|
input pinned to the caret, a sibling of the accessibility tree rather
|
|
than a node in it, so DOM focus never lands on the semantics element
|
|
carrying the test tag. The custom properties are declared on the
|
|
container and read by the input, exactly as Compose writes them. -->
|
|
<div id="app"></div>
|
|
<script id="page-script">
|
|
const root = document.getElementById("app").attachShadow({ mode: "open" });
|
|
root.innerHTML = `
|
|
<div
|
|
id="caret-holder"
|
|
style="
|
|
position: absolute;
|
|
top: 0px;
|
|
left: 0px;
|
|
--compose-internal-web-backing-input-left: 0;
|
|
--compose-internal-web-backing-input-top: 0;
|
|
--compose-internal-web-backing-input-width: 1;
|
|
--compose-internal-web-backing-input-height: 17.578125;
|
|
"
|
|
>
|
|
<input
|
|
id="caret-input"
|
|
style="
|
|
position: absolute;
|
|
top: calc(var(--compose-internal-web-backing-input-top) * 1px);
|
|
left: calc(var(--compose-internal-web-backing-input-left) * 1px);
|
|
width: calc(var(--compose-internal-web-backing-input-width) * 1px);
|
|
height: calc(var(--compose-internal-web-backing-input-height) * 1px);
|
|
padding: 0;
|
|
color: transparent;
|
|
background: transparent;
|
|
caret-color: transparent;
|
|
border: none;
|
|
outline: none;
|
|
z-index: -1;
|
|
"
|
|
/>
|
|
</div>
|
|
<div id="a11y-root" role="presentation">
|
|
<div
|
|
id="EmailField"
|
|
role="textbox"
|
|
contenteditable="true"
|
|
style="position: absolute; left: 34px; top: 78px; width: 688px; height: 18px;"
|
|
></div>
|
|
<div
|
|
id="PasswordField"
|
|
role="textbox"
|
|
contenteditable="true"
|
|
style="position: absolute; left: 34px; top: 158px; width: 688px; height: 18px;"
|
|
></div>
|
|
</div>`;
|
|
const holder = root.getElementById("caret-holder");
|
|
const caret = root.getElementById("caret-input");
|
|
for (const field of root.querySelectorAll('[role="textbox"]')) {
|
|
field.addEventListener("mousedown", (event) => {
|
|
event.preventDefault();
|
|
const box = field.getBoundingClientRect();
|
|
holder.style.setProperty(
|
|
"--compose-internal-web-backing-input-left",
|
|
String(Math.round(event.clientX)),
|
|
);
|
|
holder.style.setProperty(
|
|
"--compose-internal-web-backing-input-top",
|
|
String(Math.round(box.top)),
|
|
);
|
|
caret.focus();
|
|
});
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|