Skip to content

fix(a11y): name command palette, dialogs, and crash screen (LE-2043) - #14313

Merged
erichare merged 5 commits into
release-1.12.0from
LE-2043
Aug 5, 2026
Merged

fix(a11y): name command palette, dialogs, and crash screen (LE-2043)#14313
erichare merged 5 commits into
release-1.12.0from
LE-2043

Conversation

@viktoravelino

@viktoravelino viktoravelino commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Fixes the pre-existing accessibility bugs that the round-2 test coverage (#14302) documented with FINDING (documented, not fixed) comments. Each fix flips its documenting assertion, so the tests now pin the correct behaviour instead of the bug.

Ticket: LE-2043 — parent epic LE-1505.

What changed

components/ui/command.tsx

cmdk 1.1.1 hardcodes aria-labelledby on the input and role="separator" on the separator after spreading caller props, so neither could be fixed by passing a prop — the primitives needed reworking.

  • CommandInput drops cmdk's aria-labelledby when the caller passes aria-label. Previously that reference always won the accessible-name computation, and when the wrapping <Command> had no label it resolved to an empty element, leaving the combobox with no accessible name at all (WCAG 4.1.2). With no aria-label, cmdk's <Command label> naming is untouched.
  • CommandSeparator renders as a presentational divider instead of role="separator", which is not an allowed child of CommandList's listbox (axe aria-required-children). It keeps cmdk's hide-while-searching behaviour via useCommandState.
  • CommandDialog now takes a required label, which names both the dialog and the search input. Without it the palette announced as the literal string "Dialog". Required so the gap cannot silently return; there are no existing call sites.

popoverObject — the one CommandInput call site with no name at all now labels its <Command>, mirroring the sibling popover. No new i18n keys anywhere in this PR.

editFlowSettingsComponent — the Lock Flow row only renders when setLocked is passed. ShareModal passes none, so it was shipping a focusable, unnamed, inert switch. ExportModal and FlowSettings both pass it and are unaffected.

baseModal + templatesModalariaLabel now applies to the dialog/modal paths, not just type="full-screen", and suppresses the fallback title when set. TemplatesModal renders no BaseModal.Header, so it had no meaningful name; it now announces as "Templates" (WCAG 2.4.6 / 4.1.2).

crashErrorComponent — title is an <h1> (the page had zero headings), the report action is a single link instead of a <button> nested in an <a href> (nested interactive, WCAG 4.1.2), and the card carries role="alert" so the failure is announced. role="alert" on <main> fails aria-allowed-role, hence the card.

Out of scope — covered by other PRs

Two of the eight findings on the ticket are already fixed in open PRs and are deliberately untouched here:

Testing

  • Flipped every FINDING assertion in command.a11y.test.tsx, shareModal.a11y.test.tsx and templatesModal.a11y.test.tsx
  • New command tests: separator is presentational and passes axe; separator still hides while a search is active
  • New crashErrorComponent.a11y.test.tsx — this finding came from manual review and had no covering test
  • npx jest src/modals src/components/ui src/components/common src/components/core/dropdownComponent src/components/core/parameterRenderComponent → 114 suites, 1141 tests green

Conflict watch

Summary by CodeRabbit

  • Accessibility Improvements

    • Improved crash-screen headings, alerts, and action links for screen readers.
    • Improved accessible names for commands, dialogs, modals, and templates.
    • Command separators now behave appropriately during searches.
    • Added accessibility coverage for crash screens and updated modal and command checks.
  • Bug Fixes

    • Lock controls are no longer displayed when they cannot be used.
    • Localized the command options search placeholder.

Pre-existing gaps documented by the round-2 FINDING tests. cmdk hardcodes
aria-labelledby and role="separator" after spreading caller props, so the
first two needed the primitive reworked rather than a prop.

- CommandInput drops cmdk's aria-labelledby when given aria-label, so the
  caller's name wins instead of resolving to an empty label element
- CommandSeparator renders as presentational; separator is not an allowed
  child of CommandList's listbox
- CommandDialog requires a label, naming the dialog and the search input
- popoverObject names its Command, the last unnamed combobox call site
- EditFlowSettings hides the lock switch when no setLocked is passed, so
  shareModal stops shipping a focusable inert control
- BaseModal ariaLabel now applies to the dialog paths; templatesModal uses
  it and announces as "Templates" instead of "Dialog"
- Crash screen: h1 title, link-wrapped report button, role="alert"

Findings 3 and 8 are covered by #14250 and #14110 and left to those PRs.
@coderabbitai

coderabbitai Bot commented Jul 29, 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 Plus

Run ID: 68339d9c-64ae-4216-9726-2868c0658a1e

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 semantics and coverage were updated across command controls, crash handling, flow settings, and modal dialogs. Changes include explicit accessible names, presentational separators, conditional lock controls, localized labels, and expanded axe and interaction tests.

Changes

Accessibility updates

Layer / File(s) Summary
Command naming and separator semantics
src/frontend/src/components/ui/command.tsx, src/frontend/src/components/ui/__tests__/command.a11y.test.tsx, src/frontend/src/components/core/parameterRenderComponent/.../popoverObject/index.tsx
Command dialogs and inputs now use explicit accessible names, separators are presentational and hidden during searches, and option search labels are localized.
Modal accessible names
src/frontend/src/modals/baseModal/index.tsx, src/frontend/src/modals/templatesModal/*
Modal content receives supplied aria-label values, fallback titles are suppressed, and TemplatesModal provides its localized title.
Crash and lock control semantics
src/frontend/src/components/common/crashErrorComponent/*, src/frontend/src/components/core/editFlowSettingsComponent/index.tsx, src/frontend/src/modals/shareModal/__tests__/*
Crash UI roles, headings, and report-link structure were corrected, lock controls are omitted without setLocked, and accessibility tests cover these behaviors.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • langflow-ai/langflow#14302: Updates accessibility coverage for the cmdk-based Command UI, including dialog naming and separator behavior.

Suggested labels: bug


Important

Pre-merge checks failed

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

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Test Coverage For New Implementations ❌ Error Most changes have regression/a11y tests, but popoverObject's new Command label fallback has no test asserting the empty-placeholder accessible name. Add a regression test for CustomInputPopoverObject covering empty optionsPlaceholder and the resulting combobox accessible name.
✅ Passed checks (8 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main accessibility work across command UI, dialogs, and the crash screen.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 Quality And Coverage ✅ Passed PASS: The PR adds behavior-focused RTL/jest-axe tests for each changed UI path; assertions verify roles, names, axe, and user interactions, matching repo patterns.
Test File Naming And Structure ✅ Passed Changed frontend tests are correctly named *.test.tsx, use describe/it blocks, clear helpers/setup, and cover positive and negative cases.
Excessive Mock Usage Warning ✅ Passed Mocks isolate external stores, network, and heavy child panes; the tests still assert real DOM/interaction behavior, so mocking isn’t excessive.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch LE-2043

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 29, 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.

@viktoravelino

viktoravelino commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

QA plan — LE-2043 (PR #14313)

Accessibility fixes across three surfaces. Every case below is a manual check; automated coverage (jest-axe + IBM Equal Access) already runs in CI on this PR.

Environment / tooling

Need Suggestion
Screen readers VoiceOver + Safari (macOS), NVDA + Chrome (Windows) — at least one
Keyboard-only No mouse for the whole pass
Scanner axe DevTools or IBM Equal Access browser extension
Build This branch (LE-2043), base release-1.12.0

Regression risk is concentrated in dropdowns/comboboxes (a shared primitive changed) and the Share modal (a control was removed). Give those the most time.


1. Dropdown search field has a name (finding 1)

Where: any component parameter rendered as a dropdown — e.g. a model provider select, or any node input with a list of options.

Two different search boxes exist behind these dropdowns and both were fixed; check both.

  1. Open a flow, open a component that has a dropdown parameter, click it open.
  2. Tab/click into the search box at the top of the option list.
  3. Expect: the screen reader announces a meaningful name ("Search options", or the field's own placeholder text), not silence and not just "edit text / combo box".
  4. Run the scanner on the open dropdown. Expect: no aria-input-field-name or "form element has no label" violation.

Pass criteria: every dropdown search box announces a name. Check at least 3 different dropdown types (a standard component parameter dropdown, a model/provider select, and an object/key-value picker).

2. Divider inside dropdown lists (finding 2)

Where: dropdowns that show an extra action row at the bottom — e.g. a "+ Add …" / connect-new-provider row under the options.

  1. Open such a dropdown.
  2. Expect visually: the thin divider line between the option list and the bottom action row still renders, unchanged from today.
  3. Type into the search box.
  4. Expect: the divider disappears while filtering (same as before this change) and returns when the search is cleared.
  5. Arrow-key through the list. Expect: focus moves option → option, never landing on the divider.
  6. Scanner on the open dropdown. Expect: no aria-required-children violation.

This is the highest-regression-risk item — the divider was reimplemented. Visual parity matters as much as the a11y result.

3. Share modal has no dead lock switch (finding 4)

Where: Share a flow, and share a component (right-click a component → Share).

  1. Open the Share modal.
  2. Expect: there is no "Lock Flow" row and no toggle switch anywhere in it. Name and description remain read-only text.
  3. Tab through the whole modal. Expect: every stop is a real, usable control (tags → public checkbox → Export → Share). No stop that does nothing.
  4. Regression — the switch must still work elsewhere:
    • Flow Settings (flow menu → Settings): the Lock Flow row is present, the toggle flips, and locking disables the name/description fields. Saving persists the locked state.
    • Export modal: the Lock Flow row is present and the toggle works.
    • A read-only / no-permission flow: settings still render sensibly, switch disabled rather than missing-and-broken.

4. Templates modal announces its name (finding 5)

  1. Trigger the Templates modal (new flow / "Start from template").
  2. With a screen reader on, expect it announces as "Templates, dialog" — previously it said just "Dialog".
  3. Esc closes it; focus returns to the control that opened it; Tab stays trapped inside while open.
  4. Category rail, Get Started pane, Blank Flow button all still work.

Regression sweep (no behaviour should change)

  • Component search / dropdown filtering: typing filters, Enter selects, Esc closes, selected value persists on the node.
  • Multi-select parameters, DB provider selector, MCP tool selector, knowledge-base metadata combobox — all use the same primitive.
  • Export modal end-to-end (name, description, lock, download).
  • Flow Settings save/cancel.
  • All other modals still announce their own titles (API, Share, Edit Node, Flow Settings) — the modal shell was touched.

Explicitly out of scope for this PR

Sign-off

Ship when: cases 1–4 pass with a screen reader, the regression sweep shows no behaviour change, and no new scanner violations appear on the dropdown, Share modal or Templates modal.

@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 29, 2026
@viktoravelino viktoravelino self-assigned this Jul 29, 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: 1

🧹 Nitpick comments (1)
src/frontend/src/modals/baseModal/index.tsx (1)

334-382: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the newly changed content branches.

The current test exercises the default type="dialog" / DialogContent path. Add focused accessibility assertions for type="modal" and dialogContentWithouFixed so each new ariaLabel forwarding path is verified.

🤖 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/baseModal/index.tsx` around lines 334 - 382, Add
focused accessibility tests for the BaseModal branches using type="modal" and
dialogContentWithouFixed. Assert that each branch forwards its provided
ariaLabel to the rendered dialog content, while preserving the existing default
type="dialog" coverage.

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/components/core/parameterRenderComponent/components/inputComponent/components/popoverObject/index.tsx`:
- Line 127: Add the input.searchOptions translation key to every shipped locale
file under src/frontend/src/locales/, providing the appropriate localized
search-options label while preserving the existing locale structure and key
naming.

---

Nitpick comments:
In `@src/frontend/src/modals/baseModal/index.tsx`:
- Around line 334-382: Add focused accessibility tests for the BaseModal
branches using type="modal" and dialogContentWithouFixed. Assert that each
branch forwards its provided ariaLabel to the rendered dialog content, while
preserving the existing default type="dialog" coverage.
🪄 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 Plus

Run ID: 3a4604dc-8706-4bd9-b7f4-0513b9bcc6d3

📥 Commits

Reviewing files that changed from the base of the PR and between 22478d6 and 309dc8f.

📒 Files selected for processing (10)
  • src/frontend/src/components/common/crashErrorComponent/__tests__/crashErrorComponent.a11y.test.tsx
  • src/frontend/src/components/common/crashErrorComponent/index.tsx
  • src/frontend/src/components/core/editFlowSettingsComponent/index.tsx
  • src/frontend/src/components/core/parameterRenderComponent/components/inputComponent/components/popoverObject/index.tsx
  • src/frontend/src/components/ui/__tests__/command.a11y.test.tsx
  • src/frontend/src/components/ui/command.tsx
  • src/frontend/src/modals/baseModal/index.tsx
  • src/frontend/src/modals/shareModal/__tests__/shareModal.a11y.test.tsx
  • src/frontend/src/modals/templatesModal/__tests__/templatesModal.a11y.test.tsx
  • src/frontend/src/modals/templatesModal/index.tsx

align="center"
>
<Command
label={optionsPlaceholder || t("input.searchOptions")}

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -e json . -p 'locales' src/frontend/src | xargs -I{} sh -c 'echo === {} ===; jq ".input.searchOptions" {} 2>/dev/null || grep -n "searchOptions" {}'

Repository: langflow-ai/langflow

Length of output: 539


Add input.searchOptions to all shipped locale files.

The locale files under src/frontend/src/locales/ do not define this key, so non-English users will get a raw key or blank label instead of the translated search-options label.

🤖 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/parameterRenderComponent/components/inputComponent/components/popoverObject/index.tsx`
at line 127, Add the input.searchOptions translation key to every shipped locale
file under src/frontend/src/locales/, providing the appropriate localized
search-options label while preserving the existing locale structure and key
naming.

Review follow-ups.

BaseModal suppressed the DialogTitle when ariaLabel was set but rendered
nothing in its place, so Radix logged "DialogContent requires a
DialogTitle" on every render of templatesModal and of the canvas
playground modal, which already passed ariaLabel with no Header. Render a
VisuallyHidden DialogTitle from ariaLabel instead; full-screen keeps the
attribute since it is a plain div, not a Radix dialog.

dropdownComponent's search box is a bare input that had only a
placeholder, so the most common dropdown in the product still shipped an
unnamed search field.
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 29, 2026
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 49%
49.59% (71769/144716) 70.33% (10045/14282) 46.77% (1644/3515)

Unit Test Results

Tests Skipped Failures Errors Time
5530 0 💤 0 ❌ 0 🔥 24m 11s ⏱️

@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.70968% with 33 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.53%. Comparing base (281392d) to head (156f529).
⚠️ Report is 1 commits behind head on release-1.12.0.

Files with missing lines Patch % Lines
...rc/components/common/crashErrorComponent/index.tsx 0.00% 16 Missing ⚠️
src/frontend/src/components/ui/command.tsx 81.48% 15 Missing ⚠️
src/frontend/src/modals/baseModal/index.tsx 90.00% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##           release-1.12.0   #14313      +/-   ##
==================================================
+ Coverage           61.63%   62.53%   +0.90%     
==================================================
  Files                2416     2364      -52     
  Lines              242162   239176    -2986     
  Branches            36186    35828     -358     
==================================================
+ Hits               149249   149562     +313     
+ Misses              90995    87794    -3201     
+ Partials             1918     1820      -98     
Flag Coverage Δ
frontend 61.35% <78.70%> (+1.48%) ⬆️

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

Files with missing lines Coverage Δ
...opdownComponent/components/DropdownSearchInput.tsx 91.30% <100.00%> (ø)
...omponents/core/editFlowSettingsComponent/index.tsx 88.44% <100.00%> (+2.67%) ⬆️
.../inputComponent/components/popoverObject/index.tsx 85.71% <100.00%> (+0.19%) ⬆️
src/frontend/src/modals/templatesModal/index.tsx 96.15% <100.00%> (+0.11%) ⬆️
src/frontend/src/modals/baseModal/index.tsx 88.96% <90.00%> (-1.74%) ⬇️
src/frontend/src/components/ui/command.tsx 83.76% <81.48%> (+0.52%) ⬆️
...rc/components/common/crashErrorComponent/index.tsx 20.22% <0.00%> (+4.93%) ⬆️

... and 670 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 29, 2026
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 29, 2026
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Aug 5, 2026
@viktoravelino
viktoravelino enabled auto-merge August 5, 2026 12:42
@viktoravelino
viktoravelino added this pull request to the merge queue Aug 5, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 5, 2026
@viktoravelino
viktoravelino added this pull request to the merge queue Aug 5, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 5, 2026
@viktoravelino
viktoravelino added this pull request to the merge queue Aug 5, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 5, 2026
@viktoravelino
viktoravelino enabled auto-merge August 5, 2026 17:59
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Aug 5, 2026
@viktoravelino
viktoravelino added this pull request to the merge queue Aug 5, 2026
@erichare
erichare removed this pull request from the merge queue due to a manual request Aug 5, 2026
@erichare
erichare merged commit 62760b2 into release-1.12.0 Aug 5, 2026
105 checks passed
@erichare
erichare deleted the LE-2043 branch August 5, 2026 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y 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.

3 participants