Skip to content

fix(apollo-react): expose stage pill tooltips to keyboard and screen readers [MST-12262] [MST-12339] - #1581

Open
venkateshcherukuri wants to merge 2 commits into
mainfrom
fix/stage-status-badge-tooltip-a11y
Open

venkateshcherukuri wants to merge 2 commits into
mainfrom
fix/stage-status-badge-tooltip-a11y

Conversation

@venkateshcherukuri

Copy link
Copy Markdown
Contributor

Summary

A stage status pill ("Optional", "Ends case") that has a tooltip now keeps that tooltip available to every user: keyboard users can reach the pill and open the tooltip, and screen readers get the tooltip text as the pill's description whether or not the tooltip is open.

Issue

Without an onClick, StageStatusBadge rendered the pill as a plain <span> inside CanvasTooltip. The tooltip opened only on mouse hover, the pill was not in the Tab order, and the tooltip text existed in the DOM only while the tooltip was open, in a portal at the end of the document. Read-only canvases, such as the case runtime views, render the pills this way.

  • MST-12262 (2.1.1): the "Optional" tooltip could not be opened with the keyboard.
  • MST-12339 (4.1.2): the "Optional" tooltip text was not available to screen readers.

Changes

  • A pill with a tooltip and no onClick is a tab stop with a visible focus ring. Focus opens the tooltip; blur and Escape close it; hover works as before. The pill has no action, so it is exposed as an image named by its label (role="img", aria-label) rather than a button.
  • The tooltip text is rendered as visually hidden text directly after the pill and is the pill's aria-describedby target, so it is read in reading order and announced on focus without the tooltip being open.
  • Clickable pills keep their button and behavior and gain the same description.
  • A pill without a tooltip stays a plain, non-focusable label.
  • The StageNode "Execution Status" story now gives its runtime pills tooltips.

Behavior changes for consumers

  • Read-only "Optional" and "Ends case" pills that have a tooltip add a Tab stop inside the stage node, after the stage status and before the tasks.
  • Clicking such a pill now focuses the pill instead of the stage node. The stage is still selected, and the node's keyboard handling (Enter, arrow keys, Delete) still applies because key events bubble to the node.

…readers [MST-12262] [MST-12339]

A status pill ("Optional", "Ends case") without an onClick rendered as a
plain span inside CanvasTooltip, so its tooltip only opened on mouse
hover and its text never reached assistive technology. Read-only
canvases, such as case runtime views, render the pills this way.

A pill with a tooltip is now a tab stop: it opens the tooltip on focus
and closes it on blur and Escape. It has no action, so it is exposed as
a named image rather than a button. The tooltip text is also rendered
as visually hidden text right after the pill and is the pill's
aria-describedby target, so screen readers read it in reading order and
on focus without the tooltip being open. Clickable pills keep their
button and gain the same description.
@github-actions

Copy link
Copy Markdown
Contributor

Dependency License Review

  • ✅ 1958 package(s) scanned
  • ✅ No license issues found
  • ⚠️ 2 package(s) excluded (see details below)
License distribution
License Packages
MIT 1722
ISC 91
Apache-2.0 57
BSD-3-Clause 28
BSD-2-Clause 23
BlueOak-1.0.0 8
MPL-2.0 4
CC0-1.0 4
MIT-0 3
MIT OR Apache-2.0 2
(MIT OR Apache-2.0) 2
Unlicense 2
LGPL-3.0-or-later 1
Python-2.0 1
CC-BY-4.0 1
(MPL-2.0 OR Apache-2.0) 1
Unknown 1
Artistic-2.0 1
(WTFPL OR MIT) 1
(BSD-2-Clause OR MIT OR Apache-2.0) 1
CC-BY-3.0 1
0BSD 1
(MIT OR CC0-1.0) 1
MIT AND ISC 1
Excluded packages
Package Version License Reason
@img/sharp-libvips-linux-x64 1.3.4 LGPL-3.0-or-later LGPL pre-built binary, not linked
khroma 2.1.0 Unknown MIT per GitHub repo, missing license field in package.json

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Outstanding accessibility issues include focusable chips with empty tooltips, duplicated interactive tooltip content, and low-contrast keyboard focus indicators.

3 open issues
What changed in this PR

This PR improves stage status pill accessibility in Apollo React’s canvas, addressing keyboard access and persistent screen-reader descriptions for tooltips.

Changes:

  • Makes non-clickable pills with tooltips focusable and adds persistent accessible descriptions to tooltip-bearing pills.
  • Adds tests for keyboard tooltip behavior and accessible descriptions.
  • Adds runtime pill tooltips to the Execution Status story.
File Description
packages/​apollo-react/​src/​canvas/​components/​StageNode/​StageNodeHeader.integration.test.tsx Tests tooltip opening on focus and closing on blur or Escape.
packages/​apollo-react/​src/​canvas/​components/​StageNode/​StageNode.stories.tsx Adds tooltip content to runtime status pills.
packages/​apollo-react/​src/​canvas/​components/​StageNode/​StageHeaderChips.tsx Adds focusability, focus styling, and persistent tooltip descriptions.
packages/​apollo-react/​src/​canvas/​components/​StageNode/​StageHeaderChips.test.tsx Tests tab order and descriptions for clickable and non-clickable pills.

Review effort: Balanced

</span>
);
const descriptionId = useId();
const describedBy = tooltip ? descriptionId : undefined;
aria-describedby={describedBy}
// biome-ignore lint/a11y/noNoninteractiveTabindex: Focusable so keyboard users can open the tooltip. It has no action, so it stays an image rather than a button.
tabIndex={0}
className={`${STATUS_BADGE_BASE_CLASS} ${className} focus-visible:outline-2 focus-visible:outline-offset-2`}
Comment on lines +136 to +137
<span id={descriptionId} className="sr-only">
{tooltip}
@github-actions

Copy link
Copy Markdown
Contributor

📊 Coverage + size by package

Per-package coverage and bundle size on this PR. New-line coverage = of the source lines this PR adds or changes, the % hit by tests.

Package Coverage New-line coverage Packed (gzip) Unpacked vs main
@uipath/apollo-core — — 42.02 MB 50.17 MB ±0
@uipath/apollo-react 61.1% 100.0% (12/12) 2.57 MB 14.41 MB +350 B
@uipath/apollo-ui-icons — — 2.86 MB 6.97 MB ±0
@uipath/apollo-vertex — — — — —
@uipath/apollo-wind — — 909.2 KB 5.12 MB +23 B
@uipath/ap-chat 85.8% — 40.48 MB 46.10 MB ±0

"Coverage" is each package's own coverage.include scope (e.g. apollo-core instruments only scripts/). "Packed"/"Unpacked" come from npm pack --dry-run and only cover built packages — "—" means not measured this run (package not affected / not built). "vs main" is the packed (gzipped) delta against the last successful main build (the package-sizes artifact from the Release workflow); "—" there means no main baseline was available this run. The baseline is main's latest build, not this PR's exact merge-base, so it includes any drift since the branch diverged. Packages with no vitest config are omitted.

@github-actions

Copy link
Copy Markdown
Contributor

Storybook visual diff

⚠️ Visual changes detected: 10 changed (of 406 compared, 396 unchanged). View report

Baseline is the deployed main Storybook, so changes merged to main after this branch was last updated can also appear here. Logs

Updated (PT): Nov 13, 2026, 04:42:31 PM

This branch was successfully deployed

4 active deployments
preview/apollo-landing — 8c1ea0eb Deployed Nov 13, 2026 by github-actions[bot]
preview/apollo-design — 8c1ea0eb Deployed Nov 13, 2026 by github-actions[bot]
preview/apollo-vertex — 8c1ea0eb Deployed Nov 13, 2026 by github-actions[bot]
preview/apollo-docs — 8c1ea0eb Deployed Nov 13, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg:apollo-react size:L 100-499 changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants