Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/improve-accessibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@serverlessworkflow/diagram-editor": minor
---

Audit and improve accessibility
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { PanelRightIcon } from "lucide-react";
import { Slot } from "radix-ui";

import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export const DiagramEditor = (props: DiagramEditorProps) => {
return (
<div
className={`dec-root${resolvedColorMode === "dark" ? " dark" : ""}`}
lang={locale}
data-testid={"dec-root"}
>
<I18nProvider locale={locale} dictionaries={dictionaries}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ type ErrorPageProps = {

export const ErrorPage = ({ title, message, snippet }: ErrorPageProps) => {
return (
<div className="dec:p-6">
<div role="alert" aria-live="assertive" className="dec:p-6">
<div className="dec:flex dec:items-center dec:gap-2">
<AlertTriangle className="dec:size-5 dec:shrink-0 dec:text-red-600 dec:dark:text-red-400" />
<AlertTriangle
className="dec:size-5 dec:shrink-0 dec:text-red-600 dec:dark:text-red-400"
aria-hidden="true"
/>
<h2 className="dec:text-base dec:font-semibold dec:text-gray-900 dec:dark:text-gray-100">
{title}
</h2>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ export const en = {
"sidebar.exportMermaid.copy": "Copy Mermaid Code",
"sidebar.exportMermaid.download": "Download as Mermaid File",
"sidebar.exportMermaid.copied": "Copied!",
"aria.minimap.hide": "Hide minimap",
"aria.minimap.show": "Show minimap",
"aria.badge": "Badge:",
"aria.panel.nodeDetails": "Node details panel",
"aria.panel.workflowInfo": "Workflow information panel",
"aria.panel.content": "Panel content",
"aria.panel.exportActions": "Export actions",
} as const;

export type TranslationKeys = keyof typeof en;
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import * as React from "react";
import * as RF from "@xyflow/react";
import { useI18n } from "@serverlessworkflow/i18n";
import { ReactFlowNodeTypes } from "../nodes/Nodes";
import "@xyflow/react/dist/style.css";
import "./Diagram.css";
Expand Down Expand Up @@ -46,6 +47,7 @@ export type DiagramProps = {
};

export const Diagram = ({ divRef, ref, colorMode = "light" }: DiagramProps) => {
const { t } = useI18n();
const reactFlowInstance: RF.ReactFlowInstance = RF.useReactFlow();
const { model, errors, nodes, edges, isReadOnly, setNodes, setEdges, setSelectedNodeId } =
useDiagramEditorContext();
Expand Down Expand Up @@ -187,7 +189,12 @@ export const Diagram = ({ divRef, ref, colorMode = "light" }: DiagramProps) => {
position={"bottom-right"}
showInteractive={false}
>
<RF.ControlButton onClick={() => setMinimapVisible(!minimapVisible)}>M</RF.ControlButton>
<RF.ControlButton
onClick={() => setMinimapVisible(!minimapVisible)}
aria-label={minimapVisible ? t("aria.minimap.hide") : t("aria.minimap.show")}
>
M
</RF.ControlButton>
</RF.Controls>
<RF.Background className="diagram-background" variant={RF.BackgroundVariant.Cross} />
</RF.ReactFlow>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,18 @@ interface BadgeProps {
}

function TaskNodeBadge({ badge, testId }: BadgeProps) {
const { t } = useI18n();
const isUnknown = !KNOWN_BADGES.has(badge.toLowerCase());

if (isUnknown) {
/* TODO: instead of using the browser default to display tool tip like below, replace with tooltip component when we add it */
return (
<span title={badge} className="dec-task-node-badge-custom" data-testid={`${testId}-custom`}>
<span
title={badge}
aria-label={`${t("aria.badge")} ${badge}`}
className="dec-task-node-badge-custom"
data-testid={`${testId}-custom`}
>
{badge}
</span>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,16 @@ export function SidePanel() {
}, [selectedNodeId, setOpen]);

return (
<Sidebar side="right">
<Sidebar
side="right"
aria-label={selectedNode ? t("aria.panel.nodeDetails") : t("aria.panel.workflowInfo")}
role="complementary"
>
<SidebarHeader>
<div className="dec-sidebar-header-title">
<span
className={`dec-sidebar-header-icon-wrap${nodeConfig ? " colored" : ""}`}
aria-hidden="true"
style={
nodeConfig
? ({ "--task-node-color": nodeConfig.color } as React.CSSProperties)
Expand All @@ -84,22 +89,24 @@ export function SidePanel() {
</div>
</div>
</SidebarHeader>
<SidebarContent>
<SidebarContent aria-label={t("aria.panel.content")} role="region">
{selectedNode ? (
<NodeDetailsView node={selectedNode} />
) : (
<>
<div className="dec-sidebar-hint">
<Info className="dec-sidebar-hint-icon" />
<Info className="dec-sidebar-hint-icon" aria-hidden="true" />
<span className="dec-sidebar-hint-text">{t("sidebar.selectNode")}</span>
</div>
{model !== null ? <WorkflowInfoView document={model.document} /> : null}
</>
)}
</SidebarContent>
<SidebarFooter>
{model !== null && selectedNodeId === null && <MermaidActions model={model} />}
</SidebarFooter>
{model !== null && selectedNodeId === null ? (
<SidebarFooter aria-label={t("aria.panel.exportActions")}>
<MermaidActions model={model} />
</SidebarFooter>
) : null}
</Sidebar>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ describe("React Flow custom node types", () => {
listen: { to: { any: [] } },
}),
];
render(
renderWithProviders(
<div>
<RF.ReactFlow nodeTypes={ReactFlowNodeTypes} nodes={nodesWithBadges} edges={allEdges} />
</div>,
Expand All @@ -255,7 +255,7 @@ describe("React Flow custom node types", () => {
call: "customCall",
}),
];
render(
renderWithProviders(
<div>
<RF.ReactFlow
nodeTypes={ReactFlowNodeTypes}
Expand All @@ -281,7 +281,7 @@ describe("React Flow custom node types", () => {
fork: { branches: [], compete: true },
}),
];
render(
renderWithProviders(
<div>
<RF.ReactFlow
nodeTypes={ReactFlowNodeTypes}
Expand Down
Loading