fix(a11y): announce workflow / event status on timeline graph nodes (WCAG 1.4.1)#3443
Conversation
…WCAG 1.4.1) Adds aria-label to the SVG <g role="button"> wrapper on each timeline-graph node so screen readers announce the workflow id and status (or event type and classification) instead of bare "button". Previously, terminal statuses on the timeline graph (Completed, Failed, Canceled, TimedOut, Fired, Signaled) were distinguishable only by node color. The color legend lives inside a Tooltip -- keyboard- and AT-reachable since PR #3429, but still requires the user to leave the graph, decode the color, and come back to identify each node. This change makes the graph self-describing. Cross-cutting wins: - Closes the SC 4.1.2 missing-accessible-name defect on the same <g role="button"> widgets. - Closes the SC 1.3.1 matrix-row item d deferred to the Robust wave. New i18n keys: - workflows.row-accessible-name = "Workflow {{workflowId}}: {{status}}" - events.row-accessible-name = "Event {{eventType}}: {{classification}}" Co-Authored-By: Claude Opus 4.6 <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
CI's check-types caught two TS errors in the timeline-graph
aria-label additions:
1. translate(\`workflows.\${workflowStatusKey(status)}\`) -- the
helper's return type widened to plain string, which doesn't
satisfy the strongly-typed translation-key union.
2. translate(\`events.event-classification.\${classification.toLowerCase()}\`)
-- same shape; toLowerCase() returns plain string.
3. event.eventType -- doesn't exist on PendingActivity (a union
member of WorkflowEventWithPending).
Replaced both dynamic template-literal lookups with `as const`
record literals keyed by the literal-union enum values, so
TypeScript can narrow to the exact translation-key union. Added
an `in` operator narrowing for the eventType access.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
EventClassification union doesn't include 'Retrying' -- only the i18n key exists. The lookup entry was unreachable. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Previously the aria-label for a pending activity always said
"Pending" regardless of attempt count. The timeline already
communicates retry state visually (line styling), in the legend
("Retry" entry), and in the group-details row text -- but the
per-event aria-label missed it.
Check group.pendingActivity.attempt > 1 (matching the existing
group-details-row.svelte logic) and surface "Retrying" in the
aria-label when applicable. The events.event-classification.retrying
translation key already existed for this purpose.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
The status/classification -> translated-label mapping existed inline in workflow-status.svelte and was duplicated by the two new lookup tables added for the timeline-graph aria-labels (workflow-row, history-graph-row-visual). Extract the canonical map into $lib/utilities/get-status-label and reuse it in all three call sites so there is a single source of truth. No behavior change: the helper map is a verbatim copy of the canonical Record<Status, string>; the history graph normalizes its lowercase 'pending'/'retrying' values to the PascalCase Status union for lookup while leaving `classification` untouched for styling. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
timeline-graph-row's <g role="button"> contained only SVG primitives and <Text> (whose accessible-name contribution is unreliable across screen readers), so it announced as bare "button" — the same defect this PR fixes on workflow-row and history-graph-row-visual. Add an aria-label via the shared getStatusLabel helper. group-details-row's <g role="button"> is intentionally left for a separate fix: it wraps foreignObject HTML (already name-contributing) and nested interactive elements, so it needs a nested-interactive restructure rather than an aria-label that would mask its content. A11y-Audit-Ref: 1.4.1-timeline-graph-status Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- Unit test for the shared getStatusLabel helper (workflow statuses, event
classifications, pending/retrying, and the Unknown fallback).
- Integration test asserting the timeline graph's SVG <g role="button">
nodes expose accessible names: the workflow node ("Workflow <id>: Running")
and event nodes ("Event LongActivity: Scheduled", "Event customSignal:
Signaled"). Verified red — the assertions fail if the aria-label regresses.
history-graph-row-visual shares the same getStatusLabel +
events.row-accessible-name mechanism (covered by the unit test and the
identical timeline-graph-row path); its /history graph view did not render
nodes deterministically in the integration harness.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Avoid collisions between workflow statuses and event classifications that share a name (Running, Completed, Failed, Canceled, Terminated, TimedOut). The previous single combined map silently merged them and routed event nodes through the workflows.* namespace, so a future divergence between workflows.completed and events.event-classification.completed would have mislabeled event nodes. Now expose two domain-scoped resolvers, each backed by an exhaustive Record over its own union (so a new status in either domain forces a label): - getWorkflowStatusLabel -> workflows.* (workflow-row) - getEventClassificationLabel -> events.event-classification.* (history-graph-row-visual, timeline-graph-row) getStatusLabel remains as the polymorphic resolver for WorkflowStatus.svelte, which renders one badge for any status type and cannot know the domain from the value; it prefers the workflow label on shared names, preserving that component's existing behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
Auto-generated version bump from 2.51.0 to 2.51.1 Specific version: 2.51.1 Changes included: - [`c848ec54`](c848ec5) fix(a11y): announce workflow / event status on timeline graph nodes (WCAG 1.4.1) (#3443) - [`af6b12a9`](af6b12a) fix(deployments): align version/status/compute badges in worker deployments table (#3489) - [`df86587c`](df86587) Add SDK version to standalone activity details (#3447) - [`59695290`](5969529) Use shared FilterBar component for workflows (#3448) - [`ded94469`](ded9446) Set display contents in wrapper div if no afterLabel in Input component (#3513) - [`d7e5eb43`](d7e5eb4) fix(standalone-activities): decode failure encodedAttributes in activity detail (#3507) - [`81a255a9`](81a255a) fix(DT-4151): fix bottom nav scrollability and double-menu on mobile (#3514) - [`368f646e`](368f646) Fix exports to work properly with vite8 (#3519) - [`231bdaff`](231bdaf) chore(deps-dev): bump vitest from 3.2.4 to 3.2.6 (#3509) - [`1de44ea6`](1de44ea) fix(workers): soften no-workers-polling alert for serverless worker deployments (#3524) - [`689955a8`](689955a) fix(DT-4080): surface error when validate connection has no compute config, warn on set current (#3516) - [`93323db9`](93323db) feat(DT-4078): Add Set Ramping Version to Worker Deployment Versions (#3508) - [`9d3450c0`](9d3450c) fix(DT-4081): surface task queue not registered error in validate connection modal (#3517) - [`b7f56237`](b7f5623) feat(DT-4079): miscellaneous WD UI fixes (#3511) - [`83b2d4ec`](83b2d4e) feat(deployments): simplify create worker deployment UX (#3518) - [`ccb4cb73`](ccb4cb7) Standalone Activity write guards (#3534) - [`ab20ef03`](ab20ef0) Worker insights UI updates (#3520) - [`aaf63bdc`](aaf63bd) [wcag] Replace text-[12px] with text-xs in DatePicker action buttons (SC 1.4.4) (#3525) - [`743be902`](743be90) a11y(2.4.3): replace flex-wrap-reverse with flex-wrap on workflow history toolbar (#3527) - [`41b73e87`](41b73e8) a11y(2.4.2): ui-main — set distinct, descriptive titles on login/signin, task-queues list, and activity-workers sub-tab (#3535) - [`3d64cdbf`](3d64cdb) [WCAG 2.4.3] group-details-row — remove redundant outer focus stop on <g> wrapper (#3537) - [`883e5db1`](883e5db) a11y(2.4.6): datepicker — replace empty label with translate('common.start') (#3543) - [`9da915e2`](9da915e) a11y(2.4.5): import history — add back-link to Import landing page (#3542) - [`ca4c1d61`](ca4c1d6) Cache new Intl.DateTimeFormat (#3565) - [`38376e4d`](38376e4) Fix required activity timeout inputs on Start a Standalone Activity form (#3567) - [`49d7c9e4`](49d7c9e) Use separate copy for title vs menu item (#3568) - [`3c7dcdc3`](3c7dcdc) a11y(2.5.8): pagination — grow caret buttons from 12×12 to 24×24 CSS px (#3547) - [`b5171825`](b517182) a11y(2.5.8): chip — pad remove button to 24×24 CSS px minimum (#3544) - [`1fea5d5c`](1fea5d5) a11y(2.1.1): fake-button widgets — replace deprecated onkeypress with onkeydown handling Enter and Space (#3528) - [`5bf09000`](5bf0900) a11y(2.1.1): navigation primitives — replace hover-only CSS :after label tooltip with focusable Tooltip (#3529) - [`deed43f0`](deed43f) a11y(2.1.1): event summary row — add keyboard-accessible expand control alongside the mouse-only <tr onclick> (#3526) - [`d401123e`](d401123) a11y(2.5.8): checkbox — add min-h-6 min-w-6 to Label for 24×24 hit area (#3545) - [`aef4166f`](aef4166) [a11y] Expose toggled state via aria-pressed on ToggleButton (#3559) - [`797f8831`](797f883) Show single attempt in Standalone Activity details (#3572) - [`893bc58c`](893bc58) feat(DT-4183): Add Tabs (Primitive) (#3574) - [`323c1993`](323c199) Date picker input field bug/kt (#3522) - [`3ac34ce2`](3ac34ce) Various serverless UI updates (#3573) - [`f365416a`](f365416) a11y(2.1.1): ZoomSvg — add keyboard pan/zoom controls so the workflow family tree is operable without a mouse (#3530) - [`feb6897e`](feb6897) Fix gaps in slot cards on worker details page (#3580) - [`b3c80072`](b3c8007) Remove font weight on cache size (#3583) - [`0e7337ea`](0e7337e) Bump API to v1.62.14 (#3575) Co-authored-by: laurakwhit <[email protected]>
Summary
Adds
aria-labelto each SVG<g role="button">wrapper on the timeline-graph nodes so screen readers announce the workflow's id and status (or the event's type and classification) instead of bare "button".Previously, terminal statuses on the timeline graph (Completed, Failed, Canceled, TimedOut, Fired, Signaled) were distinguishable only by node color. The legend that decodes those colors lives inside a
<Tooltip>— keyboard- and AT-reachable since PR #3429, but still requires the user to leave the graph, decode the color in the legend, and come back to identify each node. This change makes the graph self-describing at the node level.Audit context
audit-output/issues/1.4.1-timeline-graph-status.md(this is Option A — the recommended fix).Cross-cutting wins
<g role="button">widgets.New i18n keys
workflows.row-accessible-name:"Workflow {{workflowId}}: {{status}}"events.row-accessible-name:"Event {{eventType}}: {{classification}}"Test plan
"Workflow {id}: {status}, button"instead of just"button"."Event {eventType}: {classification}, button".<g>has the expectedaria-labelattribute.svg-img-altrule should no longer flag the timeline graph rows.🤖 Generated with Claude Code
A11y-Audit-Ref: 1.4.1-timeline-graph-status