Skip to content

fix(a11y): give the scheduler's anchored popovers a role, a name and focus - #350

Merged
maksimzinchuk merged 1 commit into
mainfrom
fix/VCST-5802-popover-semantics
Sep 2, 2026
Merged

fix(a11y): give the scheduler's anchored popovers a role, a name and focus#350
maksimzinchuk merged 1 commit into
mainfrom
fix/VCST-5802-popover-semantics

Conversation

@maksimzinchuk

Copy link
Copy Markdown
Collaborator

Problem

The quick-info popover — opened by clicking an event chip — rendered as a bare div.vc-popover with role: null and aria-label: null, and focus never entered it. Its Edit/Delete controls were reachable, so nothing was unreachable; the cost was an unannounced, unnamed surface and a long tab path, since the panel is teleported to the end of the DOM.

VCST-5671 fixed exactly this for the "+N more" overflow popover, but at that call site rather than in the shared component, so the other two anchored popovers were left behind. QA flagged quick-create as an uninspected third case; it was affected too.

Fix

All three anchored popovers now carry role="dialog" and an accessible name — the formatted date, the event title (falling back to a localized "Event details"), and "New event".

Quick-info also moves focus to its close control on open and hands it back to the chip on close. Quick-create already focused its title field.

The shared part

The focus entry/restore logic moves out of MonthMorePopover into useAnchoredPanelFocus, now used by two call sites. It hinges on reading document.activeElement before the DOM patch, while the panel still exists — a tick later the node is gone, "is focus inside?" is false for every close, and focus gets yanked back even when the user deliberately moved on. That is the detail a second hand-written copy loses, and there is a test for it.

VcPopover now exposes panelEl alongside close(). A call site managing focus needs the teleported panel to answer "is focus still inside?", and cannot reach it any other way.

Dialog semantics stay opt-in props on VcPopover, as #304 intended — this is three call sites opting in, not a change of default.

Docs

The scheduler's accessibility section still said the overflow popover does not apply role="dialog", which stopped being true with #304. QA flagged that drift under VCST-5671; it is corrected here along with the new behaviour.

Tests

  • Quick-info: dialog role, name from the event title, generic name when the title is empty, focus in on open, focus back to the chip on close, and focus left alone when the user had moved it out. Reverting the fix fails four of the six.
  • Quick-create: dialog role and name.
  • useAnchoredPanelFocus: five cases, including mounting already-open (these panels usually do) and the "user moved focus out first" case that the pre-DOM-patch check exists for.

The VcPopover stub in the quick-info tests had to grow a panelEl: the real component exposes one, and a stub without it would make the focus tests pass vacuously.

Verification

vue-tsc clean · vitest run 4119 passed, exit 0 · lint:check, prettier, check:locales, madge and docs:lint clean.

Committed with --no-verify: the pre-commit hook lints only the staged files, and that narrow invocation reports a false import/no-unresolved the full lint:check does not.

Closes VCST-5802

…focus

The quick-info popover rendered as a bare div: no role, no accessible name,
and focus never entered it. The overflow popover got that treatment under
VCST-5671, but it was applied at that call site rather than shared, so the
other two anchored popovers were left behind.

All three now carry role="dialog" and a name — the formatted date, the event
title, and "New event". Quick-info also moves focus to its close control on
open and hands it back to the chip on close; quick-create already focused its
title field.

The focus entry/restore logic moves out of MonthMorePopover into
useAnchoredPanelFocus, now shared by two call sites. It hinges on reading
document.activeElement before the DOM patch, while the panel still exists —
a second hand-written copy is where that detail gets lost.

VcPopover exposes panelEl alongside close(). A call site managing focus needs
the teleported panel to answer "is focus still inside?" and cannot reach it
any other way.

The scheduler's accessibility docs still said no popover applies role=dialog,
which stopped being true with #304. Corrected.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

📦 Preview published for commit bce5a99

Install the preview with dist-tag:

npm install @vc-shell/framework@pr-350

Or pin to the exact commit:

npm install @vc-shell/framework@2.5.0-pr350.bce5a99

Published packages (dist-tag pr-350, version 2.5.0-pr350.bce5a99):

  • @vc-shell/framework
  • @vc-shell/api-client-generator
  • @vc-shell/create-vc-app
  • @vc-shell/config-generator
  • @vc-shell/migrate
  • @vc-shell/ts-config
  • @vc-shell/mf-config
  • @vc-shell/mf-host
  • @vc-shell/mf-module
  • @vc-shell/vc-app-skill

@maksimzinchuk
maksimzinchuk merged commit d1a02a5 into main Sep 2, 2026
12 checks passed
@maksimzinchuk
maksimzinchuk deleted the fix/VCST-5802-popover-semantics branch September 2, 2026 10:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant