Skip to content

fix(playground): add accessible names to icon-only controls and fix invalid ARIA on animated/status elements - #14110

Open
olayinkaadelakun wants to merge 3 commits into
release-1.12.0from
LE-1756
Open

fix(playground): add accessible names to icon-only controls and fix invalid ARIA on animated/status elements#14110
olayinkaadelakun wants to merge 3 commits into
release-1.12.0from
LE-1756

Conversation

@olayinkaadelakun

@olayinkaadelakun olayinkaadelakun commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes several accessibility violations on /playground/:id (found via a Lighthouse a11y audit and manual Chrome DevTools accessibility-tree inspection) and the shared IOModal/playground-modal component underlying it — the same code renders whether the playground is opened as its own route or as an overlay from the flow canvas.

Changes

Unlabeled icon-only controls (Lighthouse button-name / manual "no associated label" findings):

  • playground-modal.tsx — sidebar-collapse toggle button had no aria-label; both "Built with Langflow" button variants (sidebar-open and sidebar-collapsed) either lacked a label or had an unhidden decorative SVG child exposed to assistive tech
  • session-selector.tsx — the rename cancel/confirm buttons, the row's "options" combobox trigger, and the rename <Input> itself all had no accessible name
  • ThemeButtons/index.tsx (rendered in the playground's publish-options sidebar panel) — Light/Dark/System theme buttons were icon-only with no aria-label

Invalid ARIA usage (aria-label on a generic-role element):

  • textAnimation.tsx — the "New chat" empty-state text is rendered via per-character animated <span>s, with aria-label set on the wrapping element to expose the full string as one accessible name. That wrapper defaults to a plain <span>/<div> (implicit role generic), and aria-label isn't valid on generic-role elements per the ARIA-in-HTML spec. Fixed with role="img" — the standard technique for collapsing animated child nodes into one accessible text alternative.

Sidebar-toggle label bug:

  • playground-modal.tsx — the sidebar-toggle's aria-label/tooltip content was hardcoded to "Hide sidebar" and never flipped to "Show sidebar" after collapsing, even though the icon on the same button correctly ternaries. Both strings now track sidebarOpen state.

Inert aria-hidden prop:

  • ThemeButtons/index.tsx, session-selector.tsx, playground-modal.tsxaria-hidden="true" had been added to 6 IconComponent/ForwardedIconComponent usages, but that's not a prop the component recognizes (it only destructures camelCase ariaHidden); the kebab-case attribute was silently dropped. Harmless today only because the component already defaults unlabeled icons to hidden. Replaced with ariaHidden.

Test coverage added

Four new *.a11y.test.tsx files (axe scans + accessible-name/role assertions), all using the repo's shared @/utils/a11y-test helper (disables the jsdom-unreliable color-contrast rule) for consistency with the existing ~18 other *.a11y.test.tsx files:

  • ThemeButtons/__tests__/ThemeButtons.a11y.test.tsx
  • components/ui/__tests__/textAnimation.a11y.test.tsx
  • modals/IOModal/__tests__/playground-modal.a11y.test.tsx — includes a regression test locking in the sidebar-toggle label flip
  • IOFieldView/components/__tests__/session-selector.a11y.test.tsx

Summary by CodeRabbit

  • Accessibility

    • Improved screen-reader labels for theme selection, chat renaming, session controls, sidebar actions, and branding buttons.
    • Decorative icons are now hidden from assistive technologies.
    • Animated text exposes a clear accessible name where applicable.
  • Localization

    • Added translated labels for chat renaming and light, dark, and system theme options across supported languages.
  • Tests

    • Added accessibility coverage for theme buttons, animated text, playground modal states, and session renaming workflows.

Olayinka Adelakun and others added 2 commits July 15, 2026 16:02
…props, and use shared axe helper in a11y tests
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 14194733-bb3b-4019-a991-3c1142a34fc9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Accessibility labels, decorative-icon handling, conditional animated-text semantics, localization keys, and Jest axe coverage were added for theme controls, session renaming, and the playground modal.

Changes

Frontend accessibility and localization

Layer / File(s) Summary
Localized theme controls
src/frontend/src/components/core/appHeaderComponent/components/ThemeButtons/*, src/frontend/src/locales/*.json
Theme buttons now use localized accessible names and hide decorative icons; theme translations and accessibility tests were added.
Animated text semantics
src/frontend/src/components/ui/textAnimation.tsx, src/frontend/src/components/ui/__tests__/textAnimation.a11y.test.tsx
Animated text conditionally exposes role="img" and aria-label, with tests covering axe results and the full accessible name.
Session rename accessibility
src/frontend/src/modals/IOModal/components/IOFieldView/components/*, src/frontend/src/locales/*.json
Session controls, rename inputs, and rename actions receive localized accessible names; tests cover roles, axe results, cancel behavior, and rename submission.
Playground modal accessibility
src/frontend/src/modals/IOModal/playground-modal.tsx, src/frontend/src/modals/IOModal/__tests__/playground-modal.a11y.test.tsx
Sidebar and branding controls now have accessible names with decorative logos hidden; tests cover open and collapsed states.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested labels: lgtm

Suggested reviewers: cristhianzl


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 3 warnings)

Check name Status Explanation Resolution
Test Coverage For New Implementations ❌ Error SessionSelector’s a11y test covers the buttons and mutation flow, but not the rename textbox’s new accessible name, so one changed control lacks regression coverage. Add an assertion like screen.getByRole('textbox', { name: /session name/i }) in session-selector.a11y.test.tsx so the new label is regression-tested.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Quality And Coverage ⚠️ Warning Most suites validate the new ARIA behavior, but session-selector.a11y.test.tsx still omits the rename textbox’s accessible-name assertion, so coverage of the new input label is incomplete. Add an assertion for the rename textbox’s accessible name (e.g. Rename session) in session-selector.a11y.test.tsx, alongside the existing button checks.
Excessive Mock Usage Warning ⚠️ Warning playground-modal.a11y.test.tsx uses 21 jest.mock calls, so the suite is heavily isolated and obscures the real behavior under test. Reduce the mock layer by rendering more real providers/subcomponents, or split out a smaller integration test for the labeled controls.
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Test File Naming And Structure ✅ Passed PASS: The added frontend test files use accepted *.test.tsx naming, live in __tests__, and have clear describe/it blocks with setup plus positive/negative coverage.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main accessibility fixes: adding names to icon-only controls and correcting invalid ARIA on animated text.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch LE-1756

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the bug Something isn't working label Jul 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Test Coverage Advisor

No source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉

Advisory check only — never blocks merge.

@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 16, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/frontend/src/components/ui/__tests__/textAnimation.a11y.test.tsx (1)

5-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a negative test case to verify omitted accessibility attributes.

As per coding guidelines, frontend test files must include coverage for positive, negative, edge, and error cases. This test file currently only covers the positive case (where per="char" appropriately applies role="img"). Please add a negative test case for the underlying TextEffect component with per="line" to verify that the role="img" attribute is intentionally omitted.

🧪 Proposed test addition
-import { TextEffectPerChar } from "../textAnimation";
+import { TextEffect, TextEffectPerChar } from "../textAnimation";

-describe("TextEffectPerChar accessibility", () => {
+describe("TextEffect accessibility", () => {
   it("has no detectable axe violations", async () => {
     const { container } = render(
       <TextEffectPerChar>Test your flow with a chat prompt</TextEffectPerChar>,
     );

     const results = await axe(container);

     expect(results).toHaveNoViolations();
   });

   it("exposes the full string as one accessible name via role=img", () => {
     render(
       <TextEffectPerChar>Test your flow with a chat prompt</TextEffectPerChar>,
     );

     expect(
       screen.getByRole("img", { name: "Test your flow with a chat prompt" }),
     ).toBeInTheDocument();
   });
+
+  it("does not apply role=img when per is 'line'", () => {
+    render(
+      <TextEffect per="line">Test your flow</TextEffect>,
+    );
+
+    expect(screen.queryByRole("img")).not.toBeInTheDocument();
+  });
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/src/components/ui/__tests__/textAnimation.a11y.test.tsx` around
lines 5 - 25, Add a negative accessibility test in the “TextEffectPerChar
accessibility” suite that renders the underlying TextEffect component with
per="line" and verifies no role="img" element is exposed. Keep the existing
positive and axe tests unchanged.

Source: Coding guidelines

src/frontend/src/components/core/appHeaderComponent/components/ThemeButtons/__tests__/ThemeButtons.a11y.test.tsx (1)

14-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the decorative-icon semantics.

The test verifies button names but not the other changed behavior: each icon should remain hidden from the accessibility tree. Add an assertion for the rendered icon’s aria-hidden="true" attribute so this regression is detected.
As per coding guidelines, frontend tests should verify meaningful behavior for new functionality rather than only smoke-testing it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/frontend/src/components/core/appHeaderComponent/components/ThemeButtons/__tests__/ThemeButtons.a11y.test.tsx`
around lines 14 - 25, Add an accessibility assertion to the existing “names the
light, dark, and system theme buttons” test for the rendered decorative icons,
verifying each has aria-hidden="true". Use the existing ThemeButtons render and
accessible queries, targeting the icon elements without changing the button-name
assertions.

Source: Coding guidelines

src/frontend/src/modals/IOModal/__tests__/playground-modal.a11y.test.tsx (1)

38-64: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Run the axe scans against the real modal structure.

Mocking BaseModal as plain <div> elements removes dialog semantics, focus handling, and ARIA relationships from both axe scans. Keep the focused button tests, but add integration-level axe coverage using the real modal primitive.

As per coding guidelines, warn when excessive mocks obscure what is actually being tested and prefer integration tests when unit tests are overly mocked.

Also applies to: 211-217, 253-260

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/src/modals/IOModal/__tests__/playground-modal.a11y.test.tsx`
around lines 38 - 64, Reduce the mocking in the accessibility tests around the
BaseModal mock so axe scans exercise the real modal primitive and preserve its
dialog semantics, focus handling, and ARIA relationships. Keep the existing
focused-button tests and add integration-level axe coverage using the actual
BaseModal, while retaining child-component mocks only where they do not obscure
modal behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/frontend/src/modals/IOModal/components/IOFieldView/components/__tests__/session-selector.a11y.test.tsx`:
- Around line 85-100: Update the “switches to editing mode and exposes named
cancel/confirm buttons” test to also query the rename textbox by its expected
accessible name and assert that it is present, alongside the existing named
button assertions.

In `@src/frontend/src/modals/IOModal/playground-modal.tsx`:
- Line 405: Update the sidebar toggle button in the IOModal playground to
conditionally use “Show sidebar” when collapsed and “Hide sidebar” when expanded
for both its tooltip and aria-label. Extend the relevant test to click the
toggle and assert the resulting “Show sidebar” accessible label.

---

Nitpick comments:
In
`@src/frontend/src/components/core/appHeaderComponent/components/ThemeButtons/__tests__/ThemeButtons.a11y.test.tsx`:
- Around line 14-25: Add an accessibility assertion to the existing “names the
light, dark, and system theme buttons” test for the rendered decorative icons,
verifying each has aria-hidden="true". Use the existing ThemeButtons render and
accessible queries, targeting the icon elements without changing the button-name
assertions.

In `@src/frontend/src/components/ui/__tests__/textAnimation.a11y.test.tsx`:
- Around line 5-25: Add a negative accessibility test in the “TextEffectPerChar
accessibility” suite that renders the underlying TextEffect component with
per="line" and verifies no role="img" element is exposed. Keep the existing
positive and axe tests unchanged.

In `@src/frontend/src/modals/IOModal/__tests__/playground-modal.a11y.test.tsx`:
- Around line 38-64: Reduce the mocking in the accessibility tests around the
BaseModal mock so axe scans exercise the real modal primitive and preserve its
dialog semantics, focus handling, and ARIA relationships. Keep the existing
focused-button tests and add integration-level axe coverage using the actual
BaseModal, while retaining child-component mocks only where they do not obscure
modal behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 31c32499-427a-4eac-880f-d40a61071442

📥 Commits

Reviewing files that changed from the base of the PR and between d89c8f8 and a9ba745.

📒 Files selected for processing (15)
  • src/frontend/src/components/core/appHeaderComponent/components/ThemeButtons/__tests__/ThemeButtons.a11y.test.tsx
  • src/frontend/src/components/core/appHeaderComponent/components/ThemeButtons/index.tsx
  • src/frontend/src/components/ui/__tests__/textAnimation.a11y.test.tsx
  • src/frontend/src/components/ui/textAnimation.tsx
  • src/frontend/src/locales/de.json
  • src/frontend/src/locales/en.json
  • src/frontend/src/locales/es.json
  • src/frontend/src/locales/fr.json
  • src/frontend/src/locales/ja.json
  • src/frontend/src/locales/pt.json
  • src/frontend/src/locales/zh-Hans.json
  • src/frontend/src/modals/IOModal/__tests__/playground-modal.a11y.test.tsx
  • src/frontend/src/modals/IOModal/components/IOFieldView/components/__tests__/session-selector.a11y.test.tsx
  • src/frontend/src/modals/IOModal/components/IOFieldView/components/session-selector.tsx
  • src/frontend/src/modals/IOModal/playground-modal.tsx

Comment on lines +85 to +100
it("switches to editing mode and exposes named cancel/confirm buttons", async () => {
const user = userEvent.setup();
renderSessionSelector();

await user.click(screen.getByRole("combobox", { name: "Options" }));
await user.click(screen.getByRole("option", { name: /rename/i }));

const cancelButton = screen.getByRole("button", {
name: "Cancel rename",
});
const confirmButton = screen.getByRole("button", {
name: "Confirm rename",
});
expect(cancelButton).toBeInTheDocument();
expect(confirmButton).toBeInTheDocument();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Assert the rename input’s accessible name.

The test verifies the new button labels but queries the changed textbox without its name elsewhere. Add an assertion such as:

+    expect(
+      screen.getByRole("textbox", { name: "Rename session" }),
+    ).toBeInTheDocument();

As per coding guidelines, frontend tests must verify the new or changed behavior rather than act as placeholders.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("switches to editing mode and exposes named cancel/confirm buttons", async () => {
const user = userEvent.setup();
renderSessionSelector();
await user.click(screen.getByRole("combobox", { name: "Options" }));
await user.click(screen.getByRole("option", { name: /rename/i }));
const cancelButton = screen.getByRole("button", {
name: "Cancel rename",
});
const confirmButton = screen.getByRole("button", {
name: "Confirm rename",
});
expect(cancelButton).toBeInTheDocument();
expect(confirmButton).toBeInTheDocument();
});
it("switches to editing mode and exposes named cancel/confirm buttons", async () => {
const user = userEvent.setup();
renderSessionSelector();
await user.click(screen.getByRole("combobox", { name: "Options" }));
await user.click(screen.getByRole("option", { name: /rename/i }));
expect(
screen.getByRole("textbox", { name: "Rename session" }),
).toBeInTheDocument();
const cancelButton = screen.getByRole("button", {
name: "Cancel rename",
});
const confirmButton = screen.getByRole("button", {
name: "Confirm rename",
});
expect(cancelButton).toBeInTheDocument();
expect(confirmButton).toBeInTheDocument();
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/frontend/src/modals/IOModal/components/IOFieldView/components/__tests__/session-selector.a11y.test.tsx`
around lines 85 - 100, Update the “switches to editing mode and exposes named
cancel/confirm buttons” test to also query the rename textbox by its expected
accessible name and assert that it is present, alongside the existing named
button assertions.

Source: Coding guidelines

variant="ghost"
className="flex h-8 w-8 items-center justify-center !p-0"
onClick={() => setSidebarOpen(!sidebarOpen)}
aria-label={t("modal.io.hideSidebar")}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Switch the label when the sidebar is collapsed.

After the first click, this button opens the sidebar but remains named “Hide sidebar.” Make both its tooltip and accessible label conditional, and assert the resulting “Show sidebar” state in the test.

Proposed fix
-                    content={t("modal.io.hideSidebar")}
+                    content={t(
+                      sidebarOpen
+                        ? "modal.io.hideSidebar"
+                        : "modal.io.showSidebar",
+                    )}
...
-                      aria-label={t("modal.io.hideSidebar")}
+                      aria-label={t(
+                        sidebarOpen
+                          ? "modal.io.hideSidebar"
+                          : "modal.io.showSidebar",
+                      )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
aria-label={t("modal.io.hideSidebar")}
content={t(
sidebarOpen
? "modal.io.hideSidebar"
: "modal.io.showSidebar",
)}
...
aria-label={t(
sidebarOpen
? "modal.io.hideSidebar"
: "modal.io.showSidebar",
)}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/src/modals/IOModal/playground-modal.tsx` at line 405, Update the
sidebar toggle button in the IOModal playground to conditionally use “Show
sidebar” when collapsed and “Hide sidebar” when expanded for both its tooltip
and aria-label. Extend the relevant test to click the toggle and assert the
resulting “Show sidebar” accessible label.

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 46%
46.78% (66856/142891) 70.12% (9386/13385) 45.28% (1538/3396)

Unit Test Results

Tests Skipped Failures Errors Time
5343 0 💤 0 ❌ 0 🔥 17m 49s ⏱️

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 4.25532% with 45 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.51%. Comparing base (f8c5f9c) to head (2922ca1).
⚠️ Report is 38 commits behind head on release-1.12.0.

Files with missing lines Patch % Lines
...c/frontend/src/modals/IOModal/playground-modal.tsx 0.00% 17 Missing ⚠️
...pHeaderComponent/components/ThemeButtons/index.tsx 11.11% 16 Missing ⚠️
...onents/IOFieldView/components/session-selector.tsx 0.00% 11 Missing ⚠️
src/frontend/src/components/ui/textAnimation.tsx 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##           release-1.12.0   #14110      +/-   ##
==================================================
- Coverage           61.30%   60.51%   -0.79%     
==================================================
  Files                2342     2333       -9     
  Lines              237114   234413    -2701     
  Branches            33340    33041     -299     
==================================================
- Hits               145354   141866    -3488     
- Misses              89963    90834     +871     
+ Partials             1797     1713      -84     
Flag Coverage Δ
frontend 59.42% <4.25%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/frontend/src/components/ui/textAnimation.tsx 0.00% <0.00%> (ø)
...onents/IOFieldView/components/session-selector.tsx 0.00% <0.00%> (ø)
...pHeaderComponent/components/ThemeButtons/index.tsx 35.55% <11.11%> (-3.95%) ⬇️
...c/frontend/src/modals/IOModal/playground-modal.tsx 0.00% <0.00%> (ø)

... and 227 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot added the lgtm This PR has been approved by a maintainer label Jul 20, 2026
@olayinkaadelakun
olayinkaadelakun changed the base branch from release-1.11.0 to release-1.12.0 July 22, 2026 20:31
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working lgtm This PR has been approved by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants