Skip to content

fix(accessibility): preserve keyboard focus when dismissing writing diagnostics #288

Description

@seonghobae

Current active-PR truth

Draft PR #266 / branch feat/writing-diagnostics-ui remains the canonical single writer for the writing-guidance panel. Protected shipped truth is main@3b38ead2d00f44eb578d0689087b9293b3dabe1e; the live stacked parent is now #257 feat/writing-diagnostics-controller@bc7d4fbc9ca6136c1eadc83d2bc9909ef3d7980f; current #266 exact head is 70a9c29d35ed152720686e3e3731725a739347d0. The earlier issue snapshot naming parent c2f6df89... is superseded.

The original behavior claim is also stale relative to the active implementation. WritingDiagnosticsPanel.tsx already performs a deterministic successful-dismissal focus handoff: it prefers the next diagnostic in source order, otherwise the previous one, updates the active diagnostic identity, calls focusDiagnostic() for the survivor, and focuses that card before it can be removed by the state update. If the dismissed item is the only diagnostic, focus moves to the still-mounted guidance region. A failed dismissal does not invoke the handoff.

Executable assurance added

Current test-only head 70a9c29d35ed152720686e3e3731725a739347d0 adds src/components/WritingDiagnosticsPanel.dismissalFocus.test.tsx with a stateful harness whose dismissDiagnostic() actually removes the selected diagnostic. It asserts both buyer-visible focus contracts that the former static controller test could not prove:

  • dismissing the first of two diagnostics leaves keyboard focus on the surviving second card, keeps that card at roving tabIndex=0, and synchronizes focusDiagnostic('diagnostic-two');
  • dismissing the only diagnostic leaves focus on the named guidance region after the diagnostic list becomes empty.

This test addition does not broaden semantic/model/document/transport/auth/tenancy/persistence authority; it makes already-present accessibility behavior machine-checkable.

Evidence boundary

The new exact head has not yet acquired qualifying repository PR workflow evidence. The stacked-PR CI trigger defect is owned by #298/#299; predecessor UI checks do not transfer. Until the exact current head receives and passes applicable CI/Security/SAST/coverage/package/browser/accessibility workflows, this generation remains non-passing and the issue stays open. Do not create a competing UI or CI writer merely to manufacture evidence.

Integration boundary

Keep #266 Draft and stacked while its parent stack remains Draft and #118 freezes the protected v0.6.0 release boundary. Before any lifecycle transition, refetch parent/head ancestry, live governance, formal reviews/threads, and exact-head workflows. The issue may close only after the repaired behavior is integrated under those gates, not merely because active-PR source/tests contain it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: accessibilityAccessibility and assistive-technology supportarea: authAuthentication, authorization, identity, or tenant isolationarea: ci-cdCI, GitHub Actions, checks, release, or supply chainarea: securitySecurity boundary, hardening, or vulnerability preventionpriority: mediumNormal-priority or P2 workstatus: triagedOpen issue has an organization taxonomy assignmenttype: bugDefect or incorrect behavior

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions