fix(apps): make a covered elicitation dialog inert
Only the top modal owned the focus trap, but every lower `Modal.Content` remained an exposed `aria-modal` dialog with reachable controls and a live iframe — so browse navigation could land in a dialog that is visually covered and that itself claims the rest of the page is inert. Non-top contents are now `inert`: out of the accessibility tree and out of focus order, without unmounting, so each app keeps its bridge and its handshake and is live the moment it becomes top. Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
356e8eaa8a
commit
beaaad4982
2 changed files with 13 additions and 0 deletions
|
|
@ -216,6 +216,12 @@ describe("AppElicitationHost (#1854)", () => {
|
|||
await flushAsync();
|
||||
expect(screen.getAllByTestId("app-elicitation")).toHaveLength(2);
|
||||
|
||||
// The covered dialog is inert: out of the a11y tree and out of focus
|
||||
// order, but still mounted — its app keeps its bridge and handshake.
|
||||
const [lower, top] = screen.getAllByTestId("app-elicitation");
|
||||
expect(lower.hasAttribute("inert")).toBe(true);
|
||||
expect(top.hasAttribute("inert")).toBe(false);
|
||||
|
||||
await user.keyboard("{Escape}");
|
||||
// Exactly one request is dismissed — the topmost, which is the last one.
|
||||
expect(onFail).toHaveBeenCalledTimes(1);
|
||||
|
|
|
|||
|
|
@ -253,6 +253,13 @@ function AppElicitationFrame({
|
|||
only the request id tells those two dialogs apart — for
|
||||
`landmark-unique` and for anyone navigating by screen reader. */}
|
||||
<Modal.Content
|
||||
// A lower dialog is visually covered but would otherwise stay an
|
||||
// exposed `aria-modal` dialog with reachable controls and an iframe —
|
||||
// so browse navigation could land in a dialog that itself claims the
|
||||
// rest of the page is inert. `inert` takes it out of the a11y tree and
|
||||
// out of focus order WITHOUT unmounting, so its app keeps its bridge
|
||||
// and its handshake and is live the moment it becomes top.
|
||||
inert={!isTop}
|
||||
aria-label={`Elicitation ${entry.requestId} — ${entry.params.message} — rendered by ${entry.resourceUri}`}
|
||||
data-app-elicitation-status={status}
|
||||
data-testid="app-elicitation"
|
||||
|
|
|
|||
Loading…
Reference in a new issue