Merge branch 'main' into a11y
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@xyflow/react": minor
|
||||||
|
"@xyflow/svelte": minor
|
||||||
|
"@xyflow/system": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add `ariaRole` prop to nodes and edges
|
||||||
@@ -136,28 +136,28 @@ export function EdgeWrapper<EdgeType extends Edge = Edge>({
|
|||||||
|
|
||||||
const onEdgeDoubleClick = onDoubleClick
|
const onEdgeDoubleClick = onDoubleClick
|
||||||
? (event: React.MouseEvent) => {
|
? (event: React.MouseEvent) => {
|
||||||
onDoubleClick(event, { ...edge });
|
onDoubleClick(event, { ...edge });
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
const onEdgeContextMenu = onContextMenu
|
const onEdgeContextMenu = onContextMenu
|
||||||
? (event: React.MouseEvent) => {
|
? (event: React.MouseEvent) => {
|
||||||
onContextMenu(event, { ...edge });
|
onContextMenu(event, { ...edge });
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
const onEdgeMouseEnter = onMouseEnter
|
const onEdgeMouseEnter = onMouseEnter
|
||||||
? (event: React.MouseEvent) => {
|
? (event: React.MouseEvent) => {
|
||||||
onMouseEnter(event, { ...edge });
|
onMouseEnter(event, { ...edge });
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
const onEdgeMouseMove = onMouseMove
|
const onEdgeMouseMove = onMouseMove
|
||||||
? (event: React.MouseEvent) => {
|
? (event: React.MouseEvent) => {
|
||||||
onMouseMove(event, { ...edge });
|
onMouseMove(event, { ...edge });
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
const onEdgeMouseLeave = onMouseLeave
|
const onEdgeMouseLeave = onMouseLeave
|
||||||
? (event: React.MouseEvent) => {
|
? (event: React.MouseEvent) => {
|
||||||
onMouseLeave(event, { ...edge });
|
onMouseLeave(event, { ...edge });
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const onKeyDown = (event: KeyboardEvent) => {
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
@@ -198,7 +198,8 @@ export function EdgeWrapper<EdgeType extends Edge = Edge>({
|
|||||||
onMouseLeave={onEdgeMouseLeave}
|
onMouseLeave={onEdgeMouseLeave}
|
||||||
onKeyDown={isFocusable ? onKeyDown : undefined}
|
onKeyDown={isFocusable ? onKeyDown : undefined}
|
||||||
tabIndex={isFocusable ? 0 : undefined}
|
tabIndex={isFocusable ? 0 : undefined}
|
||||||
role={isFocusable ? 'button' : 'img'}
|
role={edge.ariaRole ?? (isFocusable ? 'group' : 'img')}
|
||||||
|
aria-roledescription={edge.ariaRoleDescription || 'edge'}
|
||||||
data-id={id}
|
data-id={id}
|
||||||
data-testid={`rf__edge-${id}`}
|
data-testid={`rf__edge-${id}`}
|
||||||
aria-label={
|
aria-label={
|
||||||
|
|||||||
@@ -158,7 +158,6 @@ export function NodeWrapper<NodeType extends Node>({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cc([
|
className={cc([
|
||||||
@@ -196,7 +195,8 @@ export function NodeWrapper<NodeType extends Node>({
|
|||||||
onDoubleClick={onDoubleClickHandler}
|
onDoubleClick={onDoubleClickHandler}
|
||||||
onKeyDown={isFocusable ? onKeyDown : undefined}
|
onKeyDown={isFocusable ? onKeyDown : undefined}
|
||||||
tabIndex={isFocusable ? 0 : undefined}
|
tabIndex={isFocusable ? 0 : undefined}
|
||||||
role={isFocusable ? 'button' : undefined}
|
role={node.ariaRole ?? (isFocusable ? 'group' : undefined)}
|
||||||
|
aria-roledescription={node.ariaRoleDescription || 'node'}
|
||||||
aria-describedby={disableKeyboardA11y ? undefined : `${ARIA_NODE_DESC_KEY}-${rfId}`}
|
aria-describedby={disableKeyboardA11y ? undefined : `${ARIA_NODE_DESC_KEY}-${rfId}`}
|
||||||
aria-label={node.ariaLabel}
|
aria-label={node.ariaLabel}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cc(['react-flow', className, colorModeClassName])}
|
className={cc(['react-flow', className, colorModeClassName])}
|
||||||
id={id}
|
id={id}
|
||||||
|
role="application"
|
||||||
>
|
>
|
||||||
<Wrapper
|
<Wrapper
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
import type { CSSProperties, SVGAttributes, ReactNode, MouseEvent as ReactMouseEvent, ComponentType } from 'react';
|
import type {
|
||||||
|
CSSProperties,
|
||||||
|
SVGAttributes,
|
||||||
|
ReactNode,
|
||||||
|
MouseEvent as ReactMouseEvent,
|
||||||
|
ComponentType,
|
||||||
|
AriaRole,
|
||||||
|
} from 'react';
|
||||||
import type {
|
import type {
|
||||||
EdgeBase,
|
EdgeBase,
|
||||||
BezierPathOptions,
|
BezierPathOptions,
|
||||||
@@ -57,6 +64,11 @@ export type Edge<
|
|||||||
*/
|
*/
|
||||||
reconnectable?: boolean | HandleType;
|
reconnectable?: boolean | HandleType;
|
||||||
focusable?: boolean;
|
focusable?: boolean;
|
||||||
|
/**
|
||||||
|
* The ARIA role attribute for the edge, used for accessibility.
|
||||||
|
* @default "group"
|
||||||
|
*/
|
||||||
|
ariaRole?: AriaRole;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SmoothStepEdge<EdgeData extends Record<string, unknown> = Record<string, unknown>> = Edge<
|
type SmoothStepEdge<EdgeData extends Record<string, unknown> = Record<string, unknown>> = Edge<
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
|
import type { CSSProperties, MouseEvent as ReactMouseEvent, AriaRole } from 'react';
|
||||||
import type { CoordinateExtent, NodeBase, OnError, NodeProps as NodePropsBase, InternalNodeBase } from '@xyflow/system';
|
import type { CoordinateExtent, NodeBase, OnError, NodeProps as NodePropsBase, InternalNodeBase } from '@xyflow/system';
|
||||||
|
|
||||||
import { NodeTypes } from './general';
|
import { NodeTypes } from './general';
|
||||||
@@ -18,6 +18,12 @@ export type Node<
|
|||||||
className?: string;
|
className?: string;
|
||||||
resizing?: boolean;
|
resizing?: boolean;
|
||||||
focusable?: boolean;
|
focusable?: boolean;
|
||||||
|
/**
|
||||||
|
* The ARIA role attribute for the node element, used for accessibility.
|
||||||
|
* @default "group"
|
||||||
|
*/
|
||||||
|
|
||||||
|
ariaRole?: AriaRole;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
style:width={toPxString(width)}
|
style:width={toPxString(width)}
|
||||||
style:height={toPxString(height)}
|
style:height={toPxString(height)}
|
||||||
style:z-index={z}
|
style:z-index={z}
|
||||||
role="button"
|
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (selectEdgeOnClick && id) store.handleEdgeSelection(id);
|
if (selectEdgeOnClick && id) store.handleEdgeSelection(id);
|
||||||
|
|||||||
@@ -137,7 +137,8 @@
|
|||||||
? ariaLabel
|
? ariaLabel
|
||||||
: `Edge from ${source} to ${target}`}
|
: `Edge from ${source} to ${target}`}
|
||||||
aria-describedby={focusable ? `${ARIA_EDGE_DESC_KEY}-${store.flowId}` : undefined}
|
aria-describedby={focusable ? `${ARIA_EDGE_DESC_KEY}-${store.flowId}` : undefined}
|
||||||
role={focusable ? 'button' : 'img'}
|
role={edge.ariaRole ?? (focusable ? 'group' : 'img')}
|
||||||
|
aria-roledescription={edge.ariaRoleDescription || 'edge'}
|
||||||
onkeydown={focusable ? onkeydown : undefined}
|
onkeydown={focusable ? onkeydown : undefined}
|
||||||
tabindex={focusable ? 0 : undefined}
|
tabindex={focusable ? 0 : undefined}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -253,7 +253,8 @@
|
|||||||
: undefined}
|
: undefined}
|
||||||
onkeydown={focusable ? onKeyDown : undefined}
|
onkeydown={focusable ? onKeyDown : undefined}
|
||||||
tabIndex={focusable ? 0 : undefined}
|
tabIndex={focusable ? 0 : undefined}
|
||||||
role={focusable ? 'button' : undefined}
|
role={node.ariaRole ?? (focusable ? 'group' : undefined)}
|
||||||
|
aria-roledescription={node.ariaRoleDescription || 'node'}
|
||||||
aria-describedby={store.disableKeyboardA11y
|
aria-describedby={store.disableKeyboardA11y
|
||||||
? undefined
|
? undefined
|
||||||
: `${ARIA_NODE_DESC_KEY}-${store.flowId}`}
|
: `${ARIA_NODE_DESC_KEY}-${store.flowId}`}
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ export type Edge<
|
|||||||
style?: string;
|
style?: string;
|
||||||
class?: ClassValue;
|
class?: ClassValue;
|
||||||
focusable?: boolean;
|
focusable?: boolean;
|
||||||
|
/**
|
||||||
|
* The ARIA role attribute for the edge, used for accessibility.
|
||||||
|
* @default "group"
|
||||||
|
*/
|
||||||
|
ariaRole?: HTMLAttributes<HTMLElement>['role'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BaseEdgeProps = Pick<
|
export type BaseEdgeProps = Pick<
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { Component } from 'svelte';
|
import type { Component } from 'svelte';
|
||||||
import type { ClassValue } from 'svelte/elements';
|
import type { ClassValue, HTMLAttributes } from 'svelte/elements';
|
||||||
import type { InternalNodeBase, NodeBase, NodeProps as NodePropsBase } from '@xyflow/system';
|
import type { InternalNodeBase, NodeBase, NodeProps as NodePropsBase } from '@xyflow/system';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,6 +21,11 @@ export type Node<
|
|||||||
class?: ClassValue;
|
class?: ClassValue;
|
||||||
style?: string;
|
style?: string;
|
||||||
focusable?: boolean;
|
focusable?: boolean;
|
||||||
|
/**
|
||||||
|
* The ARIA role attribute for the node element, used for accessibility.
|
||||||
|
* @default "group"
|
||||||
|
*/
|
||||||
|
ariaRole?: HTMLAttributes<HTMLElement>['role'];
|
||||||
};
|
};
|
||||||
|
|
||||||
// @todo: currently generics for nodes are not really supported
|
// @todo: currently generics for nodes are not really supported
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ export type EdgeBase<
|
|||||||
* This property sets the width of that invisible path.
|
* This property sets the width of that invisible path.
|
||||||
*/
|
*/
|
||||||
interactionWidth?: number;
|
interactionWidth?: number;
|
||||||
|
/**
|
||||||
|
* A description of the edge's, used for accessibility.
|
||||||
|
* @default "edge"
|
||||||
|
*/
|
||||||
|
ariaRoleDescription?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SmoothStepPathOptions = {
|
export type SmoothStepPathOptions = {
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ export type NodeBase<
|
|||||||
*/
|
*/
|
||||||
origin?: NodeOrigin;
|
origin?: NodeOrigin;
|
||||||
handles?: NodeHandle[];
|
handles?: NodeHandle[];
|
||||||
|
/**
|
||||||
|
* A description of the node's role, used for accessibility.
|
||||||
|
* @default "node"
|
||||||
|
*/
|
||||||
|
ariaRoleDescription?: string;
|
||||||
measured?: {
|
measured?: {
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user