Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e688e94
chore: add accessibility action plan and gap report for IBM Level 1 c…
viktoravelino Jun 9, 2026
2ffd2d1
Merge branch 'release-1.10.0' into feat/a11y
viktoravelino Jun 9, 2026
fa7d541
feat: integrate accessibility checker for automated a11y scans
viktoravelino Jun 9, 2026
c9d45fa
feat: add accessibility test usage documentation for IBM scans
viktoravelino Jun 9, 2026
f188b5b
feat: enhance accessibility testing with JSON report aggregation and …
viktoravelino Jun 10, 2026
170acde
feat: add GitHub Actions workflow for IBM accessibility scans
viktoravelino Jun 10, 2026
6d5ef55
feat: update a11y scan workflow to allow testing from feat/a11y branch
viktoravelino Jun 10, 2026
db8da2a
Merge branch 'release-1.10.0' into feat/a11y
viktoravelino Jun 10, 2026
cd9ab69
feat: skip Puppeteer download during npm install in Dockerfiles for a…
viktoravelino Jun 10, 2026
66318cf
Merge branch 'feat/a11y' of github.com:langflow-ai/langflow into feat…
viktoravelino Jun 10, 2026
583afd0
Merge branch 'release-1.11.0' into feat/a11y
viktoravelino Jun 12, 2026
d76e217
feat: enhance a11y scan workflow to resolve and scan latest release b…
viktoravelino Jun 12, 2026
9dbde72
Merge branch 'release-1.11.0' into feat/a11y
viktoravelino Jun 15, 2026
3c1b2cd
test: add component a11y unit tests (jest-axe) (#13613)
viktoravelino Jun 15, 2026
287ca4c
Merge branch 'release-1.11.0' into feat/a11y
viktoravelino Jun 15, 2026
0007f20
feat: Add a11y regression scan suite (#13663)
viktoravelino Jun 16, 2026
caad567
Merge branch 'release-1.11.0' into feat/a11y
viktoravelino Jun 16, 2026
5992048
Merge branch 'release-1.11.0' into feat/a11y
viktoravelino Jun 17, 2026
d82723e
Merge branch 'release-1.11.0' into feat/a11y
viktoravelino Jun 18, 2026
e6fbeb1
fix(a11y): fix traces page accessibility violations — rowgroup tabbab…
Jun 18, 2026
a1078ee
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 18, 2026
2d79e19
fix lighthouse errors on panel
Jun 18, 2026
477fd03
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 18, 2026
c49a8db
Merge branch 'release-1.11.0' into feat/a11y
viktoravelino Jun 19, 2026
c368ade
fix(a11y): restore focus-visible indicators (#13664)
olayinkaadelakun Jun 22, 2026
d36083e
a11y: improve auth accessibility coverage (#13724)
viktoravelino Jun 22, 2026
2159d04
Merge branch 'feat/a11y' into LE-1611
olayinkaadelakun Jun 22, 2026
0f673d8
Merge remote-tracking branch 'origin/release-1.11.0' into LE-1611
Jun 26, 2026
b36e71e
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 26, 2026
3921499
fix generic typing
Jun 26, 2026
ae56cce
remove uneccesary files
Jun 26, 2026
c1ca5d1
fix(traces): restore table a11y
viktoravelino Jul 2, 2026
6e8e0f7
Merge branch 'release-1.11.0' into LE-1611
viktoravelino Jul 2, 2026
c16c89c
Fix a11y(trace): fix WCAG AA contrast, ARIA tree widget, and hook dec…
olayinkaadelakun Jul 6, 2026
565093e
Merge branch 'release-1.11.0' into LE-1611
viktoravelino Jul 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/frontend/src/components/core/codeTabsComponent/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import {
oneDark,
Expand All @@ -19,6 +20,7 @@ export default function SimplifiedCodeTabComponent({
language,
maxHeight,
}: SimplifiedCodeTabProps) {
const { t } = useTranslation();
const [isCopied, setIsCopied] = useState<boolean>(false);
const dark = useDarkStore((state) => state.dark);

Expand Down Expand Up @@ -51,6 +53,7 @@ export default function SimplifiedCodeTabComponent({
className="text-muted-foreground hover:bg-card"
data-testid="copy-code-button"
onClick={copyToClipboard}
aria-label={isCopied ? t("chat.copiedCode") : t("chat.copyCode")}
>
{isCopied ? (
<IconComponent name="Check" className="h-4 w-4" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { AgGridReact, type AgGridReactProps } from "ag-grid-react";
import cloneDeep from "lodash";
import { type ElementRef, forwardRef, useRef, useState } from "react";
import TableOptions from "./components/TableOptions";
import { applyRowTabIndices } from "./utils/applyRowTabIndices";
import resetGrid from "./utils/reset-grid-columns";

export interface TableComponentProps extends AgGridReactProps {
Expand Down Expand Up @@ -264,6 +265,7 @@ const TableComponent = forwardRef<
// @ts-ignore
const realRef: React.MutableRefObject<AgGridReact> =
useRef<AgGridReact | null>(null);
const containerRef = useRef<HTMLDivElement>(null);
const dark = useDarkStore((state) => state.dark);
const initialColumnDefs = useRef(colDef);
const [columnStateChange, setColumnStateChange] = useState(false);
Expand Down Expand Up @@ -382,6 +384,7 @@ const TableComponent = forwardRef<

return (
<div
ref={containerRef}
className={cn(
dark ? "ag-theme-quartz-dark" : "ag-theme-quartz",
"ag-theme-shadcn flex h-full flex-col",
Expand Down Expand Up @@ -494,6 +497,14 @@ const TableComponent = forwardRef<
setColumnStateChange(true);
}
}}
onFirstDataRendered={(e) => {
applyRowTabIndices(containerRef.current);
props.onFirstDataRendered?.(e);
}}
onRowDataUpdated={(e) => {
applyRowTabIndices(containerRef.current);
props.onRowDataUpdated?.(e);
}}
/>
{!props.tableOptions?.hide_options && props.pagination && (
<TableOptions
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function applyRowTabIndices(containerEl: HTMLElement | null): void {
if (!containerEl) return;
containerEl
.querySelectorAll<HTMLElement>(".ag-center-cols-container [role='row']")
.forEach((row, idx) => {
row.setAttribute("tabindex", idx === 0 ? "0" : "-1");
});
}
8 changes: 4 additions & 4 deletions src/frontend/src/components/ui/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ const Alert = React.forwardRef<
Alert.displayName = "Alert";

const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
HTMLElement,
React.HTMLAttributes<HTMLElement> & { as?: React.ElementType }
>(({ className, as: Comp = "div", ...props }, ref) => (
<Comp
ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props}
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@
"chat.cannotOpenDescription": "Dies ist kein Chat-Ablauf.",
"chat.cannotOpenTitle": "Der Chat lässt sich nicht öffnen",
"chat.copied": "Kopiert!",
"chat.copyCode": "Copy code",
"chat.copiedCode": "Code copied",
Comment on lines +221 to +222

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

Translate the new German locale entries.

These additions are still English literals, so de users will get mixed-language UI text and ARIA labels instead of German.

Also applies to: 1953-1956

🤖 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/locales/de.json` around lines 220 - 221, The new locale
strings in the German translations are still in English, so update the added
entries in de.json to proper German text. Make sure the translation keys for
chat.copyCode and chat.copiedCode (and the related added entries mentioned in
the same locale block) are localized consistently so the UI and ARIA labels
remain fully German.

"chat.copyMessage": "Nachricht kopieren",
"chat.defaultSession": "Standardsitzung",
"chat.deleteSessionsCount_one": "{{count}} -Sitzung löschen",
Expand Down Expand Up @@ -1948,6 +1950,10 @@
"trace.clearAll": "Alles löschen",
"trace.clearAllConfirm": "Möchten Sie wirklich alle Datensätze löschen? Dadurch werden alle zugehörigen Flow-Aktivitätsverläufe endgültig gelöscht; dieser Vorgang kann nicht rückgängig gemacht werden.",
"trace.clearAllRecords": "Alle Datensätze löschen",
"trace.tableAriaLabel": "Trace runs table",
"trace.statusFilterLabel": "Filter by status",
"trace.expandSpan": "Expand span",
"trace.collapseSpan": "Collapse span",
"trace.clearDates": "Termine löschen",
"trace.clearError": "Fehler beim Löschen von Datensätzen",
"trace.clearedSuccess": "Datensätze wurden erfolgreich gelöscht",
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1341,6 +1341,10 @@
"trace.dateRangeLabel": "{{start}} - {{end}}",
"trace.invalidDateRangeMessage": "End date cannot be earlier than start date.",
"trace.clearDates": "Clear dates",
"trace.tableAriaLabel": "Trace runs table",
"trace.statusFilterLabel": "Filter by status",
"trace.expandSpan": "Expand span",
"trace.collapseSpan": "Collapse span",
"flow.restoreVersion": "Restore this version of your flow",
"flow.saveVersion": "Save a version of your flow",
"flow.moreOptions": "More options",
Expand Down Expand Up @@ -1515,6 +1519,8 @@
"chat.options": "Options",
"chat.editMessage": "Edit message",
"chat.copied": "Copied!",
"chat.copyCode": "Copy code",
"chat.copiedCode": "Code copied",
"chat.copyMessage": "Copy message",
"chat.helpful": "Helpful",
"chat.notHelpful": "Not helpful",
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/src/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@
"chat.cannotOpenDescription": "Esto no es un flujo de chat.",
"chat.cannotOpenTitle": "No se puede abrir el chat",
"chat.copied": "Copiado",
"chat.copyCode": "Copy code",
"chat.copiedCode": "Code copied",
Comment on lines +221 to +222

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

Translate the new Spanish locale entries.

These additions are still English literals, so es users will get mixed-language UI text and ARIA labels instead of Spanish.

Also applies to: 1953-1956

🤖 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/locales/es.json` around lines 220 - 221, The new entries in
the es locale are still English literals, so translate the recently added
chat.copyCode and chat.copiedCode strings into Spanish in the locale JSON. Make
sure the corresponding ARIA/UI text for the related copied-code messages in the
same locale are also localized consistently, and update any matching entries
mentioned by the review so es users see only Spanish text.

"chat.copyMessage": "Copiar mensaje",
"chat.defaultSession": "Sesión predeterminada",
"chat.deleteSessionsCount_one": "Eliminar una sesión de « {{count}} »",
Expand Down Expand Up @@ -1948,6 +1950,10 @@
"trace.clearAll": "Borrar todo",
"trace.clearAllConfirm": "¿Estás seguro de que quieres borrar todos los registros? Esto eliminará de forma permanente todos los registros de actividad de Flow relacionados y no se podrá deshacer.",
"trace.clearAllRecords": "Borrar todos los registros",
"trace.tableAriaLabel": "Trace runs table",
"trace.statusFilterLabel": "Filter by status",
"trace.expandSpan": "Expand span",
"trace.collapseSpan": "Collapse span",
"trace.clearDates": "Borrar fechas",
"trace.clearError": "Error al borrar registros",
"trace.clearedSuccess": "Los registros se han borrado correctamente",
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@
"chat.cannotOpenDescription": "Ce n'est pas un flux de conversation.",
"chat.cannotOpenTitle": "Impossible d'ouvrir la discussion",
"chat.copied": "Copié",
"chat.copyCode": "Copy code",
"chat.copiedCode": "Code copied",
Comment on lines +221 to +222

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

Localize these new French strings before shipping.

These entries are still English, so the French UI will expose English button/table labels to screen readers and sighted users instead of localized copy.

Also applies to: 1953-1956

🤖 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/locales/fr.json` around lines 220 - 221, The new French
locale entries are still in English, so update the added keys in the fr.json
translations with proper French text. Locate the chat.copyCode and
chat.copiedCode entries in the French locale file and replace their values with
localized French strings consistent with the rest of the translation set, then
verify any other newly added French entries in the referenced range are also
translated.

"chat.copyMessage": "Copier le message",
"chat.defaultSession": "Session par défaut",
"chat.deleteSessionsCount_one": "Supprimer la session d' {{count}}",
Expand Down Expand Up @@ -1948,6 +1950,10 @@
"trace.clearAll": "Effacer tout",
"trace.clearAllConfirm": "Êtes-vous sûr de vouloir effacer tous les enregistrements? Cette opération supprimera définitivement toutes les traces d'activité de flux associées et ne pourra pas être annulée.",
"trace.clearAllRecords": "Effacer tous les enregistrements",
"trace.tableAriaLabel": "Trace runs table",
"trace.statusFilterLabel": "Filter by status",
"trace.expandSpan": "Expand span",
"trace.collapseSpan": "Collapse span",
"trace.clearDates": "Effacer les dates",
"trace.clearError": "Erreur lors de la suppression des enregistrements",
"trace.clearedSuccess": "Les enregistrements ont été effacés avec succès",
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/src/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@
"chat.cannotOpenDescription": "これはチャットフローではありません。",
"chat.cannotOpenTitle": "チャットが開けません",
"chat.copied": "コピーされました",
"chat.copyCode": "Copy code",
"chat.copiedCode": "Code copied",
Comment on lines +221 to +222

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

Replace these English placeholders with Japanese copy.

Because the keys exist in the Japanese bundle, screen readers will announce these controls in English instead of Japanese. That undermines the a11y fix for ja.

Also applies to: 1953-1956

🤖 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/locales/ja.json` around lines 220 - 221, The Japanese locale
entries for chat.copyCode and chat.copiedCode are still English placeholders,
which breaks the intended accessibility/localization behavior. Update the
corresponding strings in the ja.json bundle to proper Japanese copy, and make
the same localization fix for the other referenced chat-related keys in this
bundle so screen readers announce them in Japanese. Use the existing locale key
names to locate and replace the untranslated values.

"chat.copyMessage": "メッセージをコピーする",
"chat.defaultSession": "デフォルトのセッション",
"chat.deleteSessionsCount_one": "{{count}} のセッションを削除する",
Expand Down Expand Up @@ -1948,6 +1950,10 @@
"trace.clearAll": "すべて消去",
"trace.clearAllConfirm": "すべての記録を削除してもよろしいですか? これにより、関連するすべてのフローアクティビティトレースが完全に削除され、元に戻すことはできません。",
"trace.clearAllRecords": "すべての記録を消去",
"trace.tableAriaLabel": "Trace runs table",
"trace.statusFilterLabel": "Filter by status",
"trace.expandSpan": "Expand span",
"trace.collapseSpan": "Collapse span",
"trace.clearDates": "日付を消去",
"trace.clearError": "レコードの削除中にエラーが発生しました",
"trace.clearedSuccess": "レコードのクリアに成功しました",
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/src/locales/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@
"chat.cannotOpenDescription": "Isso não é um fluxo de chat.",
"chat.cannotOpenTitle": "Não é possível abrir o chat",
"chat.copied": "Copiado!",
"chat.copyCode": "Copy code",
"chat.copiedCode": "Code copied",
Comment on lines +221 to +222

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

These pt translations are still in English.

The new aria-labels and control labels will render in English for Portuguese users, so this locale stays partially untranslated in the exact accessibility path this PR is fixing.

Also applies to: 1953-1956

🤖 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/locales/pt.json` around lines 220 - 221, The Portuguese
locale still contains English strings for the new chat copy controls, so update
the entries for chat.copyCode and chat.copiedCode in pt.json to proper
Portuguese text. Make sure the corresponding accessibility-related labels added
in this PR are translated consistently in the pt locale, including the other
affected chat label entries referenced by the comment, so the UI and aria labels
don’t fall back to English for Portuguese users.

"chat.copyMessage": "Copiar mensagem",
"chat.defaultSession": "Sessão padrão",
"chat.deleteSessionsCount_one": "Excluir a sessão do {{count}}",
Expand Down Expand Up @@ -1948,6 +1950,10 @@
"trace.clearAll": "Limpar Tudo",
"trace.clearAllConfirm": "Tem certeza de que deseja apagar todos os registros? Isso excluirá permanentemente todos os rastros de atividade do fluxo relacionados e não poderá ser desfeito.",
"trace.clearAllRecords": "Limpar todos os registros",
"trace.tableAriaLabel": "Trace runs table",
"trace.statusFilterLabel": "Filter by status",
"trace.expandSpan": "Expand span",
"trace.collapseSpan": "Collapse span",
"trace.clearDates": "Limpar datas",
"trace.clearError": "Erro ao limpar registros",
"trace.clearedSuccess": "Os registros foram apagados com sucesso",
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/src/locales/zh-Hans.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@
"chat.cannotOpenDescription": "这不是一个聊天流程。",
"chat.cannotOpenTitle": "无法打开聊天窗口",
"chat.copied": "已复制!",
"chat.copyCode": "Copy code",
"chat.copiedCode": "Code copied",
Comment on lines +221 to +222

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

Localize the new zh-Hans accessibility strings.

Right now these values are English, so the copy button and trace controls won't have Simplified Chinese labels in this locale.

Also applies to: 1953-1956

🤖 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/locales/zh-Hans.json` around lines 220 - 221, The new
accessibility strings in the zh-Hans locale are still in English, so update the
localized entries for the copy button and trace controls to Simplified Chinese
in the zh-Hans translation file. Use the existing locale keys such as
chat.copyCode and chat.copiedCode, and also localize the related entries
referenced in the comment’s affected range so all new UI labels match the rest
of the zh-Hans translations.

"chat.copyMessage": "复制消息",
"chat.defaultSession": "默认会话",
"chat.deleteSessionsCount_one": "删除 {{count}} 会话",
Expand Down Expand Up @@ -1948,6 +1950,10 @@
"trace.clearAll": "全部清除",
"trace.clearAllConfirm": "您确定要清除所有记录吗? 这将永久删除所有相关的流程活动跟踪记录,且无法撤销。",
"trace.clearAllRecords": "清除所有记录",
"trace.tableAriaLabel": "Trace runs table",
"trace.statusFilterLabel": "Filter by status",
"trace.expandSpan": "Expand span",
"trace.collapseSpan": "Collapse span",
"trace.clearDates": "清除日期",
"trace.clearError": "清除记录时发生错误",
"trace.clearedSuccess": "记录已成功清除",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CellClickedEvent } from "ag-grid-community";
import type { CellClickedEvent, CellKeyDownEvent } from "ag-grid-community";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { useSearchParams } from "react-router-dom";
Expand Down Expand Up @@ -182,6 +182,15 @@ export function FlowInsightsContent({
setTracePanelOpen(true);
}, []);

const handleCellKeyDown = useCallback((event: CellKeyDownEvent) => {
const keyboardEvent = event.event as KeyboardEvent | undefined;
if (keyboardEvent?.key === "Enter") {
const rowData = event.data as TraceListItem | undefined;
setTracePanelTraceId(rowData?.id ?? null);
setTracePanelOpen(true);
}
}, []);

const totalRuns = tracesData?.total ?? rows.length;
const totalPages = Math.max(
1,
Expand Down Expand Up @@ -306,7 +315,10 @@ export function FlowInsightsContent({
</div>

<Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="h-8 w-[130px] [&>span]:truncate">
<SelectTrigger
className="h-8 w-[130px] [&>span]:truncate"
aria-label={t("trace.statusFilterLabel")}
>
<SelectValue placeholder={t("trace.allStatus")} />
</SelectTrigger>
<SelectContent>
Expand Down Expand Up @@ -404,6 +416,7 @@ export function FlowInsightsContent({
<TableComponent
key="Executions"
readOnlyEdit
aria-label={t("trace.tableAriaLabel")}
className="h-max-full h-full w-full"
data-testid="flow-insights-trace-table"
pagination={false}
Expand All @@ -412,6 +425,7 @@ export function FlowInsightsContent({
rowData={rows}
headerHeight={rows.length === 0 ? 0 : undefined}
onCellClicked={handleCellClicked}
onCellKeyDown={handleCellKeyDown}
/>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import IconComponent from "@/components/common/genericIconComponent";
import useFlowStore from "@/stores/flowStore";
import { cn } from "@/utils/utils";
Expand All @@ -22,6 +23,7 @@ export function SpanNode({
onToggle,
onSelect,
}: SpanNodeProps) {
const { t } = useTranslation();
const nodes = useFlowStore((state) => state.nodes);
const componentIconMap = useMemo(() => {
const map = new Map<string, string>();
Expand Down Expand Up @@ -71,6 +73,13 @@ export function SpanNode({
}}
tabIndex={-1}
aria-hidden={!hasChildren}
aria-label={
hasChildren
? isExpanded
? t("trace.collapseSpan")
: t("trace.expandSpan")
: undefined
}
>
<IconComponent
name={isExpanded ? "ChevronDown" : "ChevronRight"}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { applyRowTabIndices } from "@/components/core/parameterRenderComponent/components/tableComponent/utils/applyRowTabIndices";

function makeGrid(rowCount: number): HTMLElement {
const container = document.createElement("div");
const colsContainer = document.createElement("div");
colsContainer.className = "ag-center-cols-container";

for (let i = 0; i < rowCount; i++) {
const row = document.createElement("div");
row.setAttribute("role", "row");
colsContainer.appendChild(row);
}

container.appendChild(colsContainer);
return container;
}

describe("applyRowTabIndices — rowgroup tabbable fix", () => {
it("sets tabindex=0 on the first row and -1 on all others", () => {
const container = makeGrid(3);
applyRowTabIndices(container);

const rows = container.querySelectorAll<HTMLElement>("[role='row']");
expect(rows[0].getAttribute("tabindex")).toBe("0");
expect(rows[1].getAttribute("tabindex")).toBe("-1");
expect(rows[2].getAttribute("tabindex")).toBe("-1");
});

it("handles a single row — that row gets tabindex=0", () => {
const container = makeGrid(1);
applyRowTabIndices(container);

const rows = container.querySelectorAll<HTMLElement>("[role='row']");
expect(rows[0].getAttribute("tabindex")).toBe("0");
});

it("does nothing when containerEl is null", () => {
expect(() => applyRowTabIndices(null)).not.toThrow();
});

it("does nothing when there are no rows", () => {
const container = makeGrid(0);
applyRowTabIndices(container);

const rows = container.querySelectorAll("[role='row']");
expect(rows.length).toBe(0);
});

it("overwrites any existing tabindex on rows", () => {
const container = makeGrid(2);
const rows = container.querySelectorAll<HTMLElement>("[role='row']");
rows[0].setAttribute("tabindex", "5");
rows[1].setAttribute("tabindex", "5");

applyRowTabIndices(container);

expect(rows[0].getAttribute("tabindex")).toBe("0");
expect(rows[1].getAttribute("tabindex")).toBe("-1");
});
});
3 changes: 2 additions & 1 deletion src/frontend/tests/a11y/auth-pages.a11y.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect, type LangflowPage, test } from "../fixtures";
import { expect, test } from "../fixtures";
import type { LangflowPage } from "../utils/types";

async function disableAutoLogin(page: LangflowPage) {
await page.route("**/api/v1/auto_login", (route) => {
Expand Down
2 changes: 0 additions & 2 deletions src/frontend/tests/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import {
} from "./utils/accessibility-checker";
import type { A11yScanOptions, LangflowPage } from "./utils/types";

export type { A11yScanOptions, LangflowPage } from "./utils/types";

const RUN_A11Y = process.env.RUN_A11Y === "true";
const RUN_A11Y_ASSERT = process.env.RUN_A11Y_ASSERT === "true";

Expand Down
Loading