Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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,6 +20,7 @@ import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { PanelRightIcon } from "lucide-react";
import { Slot } from "radix-ui";
import { useI18n } from "@serverlessworkflow/i18n";
Comment thread
cheryl7114 marked this conversation as resolved.
Outdated

import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
Expand Down Expand Up @@ -221,15 +222,16 @@ function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<t

function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
const { toggleSidebar } = useSidebar();
const { t } = useI18n();

return (
Comment thread
cheryl7114 marked this conversation as resolved.
<button
data-sidebar="rail"
data-slot="sidebar-rail"
aria-label="Toggle Sidebar"
aria-label={t("aria.sidebar.toggle")}
tabIndex={-1}
onClick={toggleSidebar}
title="Toggle Sidebar"
title={t("sidebar.toggle")}
className={cn(
"dec:absolute dec:inset-y-0 dec:z-20 dec:hidden dec:w-4 dec:-translate-x-1/2 dec:transition-all dec:ease-linear dec:group-data-[side=left]:-right-4 dec:group-data-[side=right]:left-0 dec:after:absolute dec:after:inset-y-0 dec:after:left-1/2 dec:after:w-[2px] dec:hover:after:bg-sidebar-border dec:sm:flex",
"dec:in-data-[side=left]:cursor-w-resize dec:in-data-[side=right]:cursor-e-resize",
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,14 @@ 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.sidebar.toggle": "Toggle Sidebar",
"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")}
>
Comment thread
cheryl7114 marked this conversation as resolved.
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
5 changes: 4 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading