Feat: basic keyboard controls and better WAI-ARIA defaults (#2333)
* feat(nodes): focusable and moveable with keys * refactor(key-handling): cleanup, handle selections * feat(edges): selectable with keys * refactor(nodes-edges): cleanup keyboard controls and aria- attrs * refactor(nodes): node needs to be selected for arrow keys * refactor(minimap): create const for labelledby closes #1033
This commit is contained in:
@@ -40,7 +40,8 @@ interface EdgeRendererProps {
|
||||
edgeUpdaterRadius?: number;
|
||||
noPanClassName?: string;
|
||||
elevateEdgesOnSelect: boolean;
|
||||
rfId?: string;
|
||||
rfId: string;
|
||||
disableKeyboardA11y: boolean;
|
||||
}
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
@@ -182,6 +183,8 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
||||
onEdgeUpdateStart={props.onEdgeUpdateStart}
|
||||
onEdgeUpdateEnd={props.onEdgeUpdateEnd}
|
||||
rfId={props.rfId}
|
||||
ariaLabel={edge.ariaLabel}
|
||||
disableKeyboardA11y={props.disableKeyboardA11y}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -22,6 +22,7 @@ export type FlowRendererProps = Omit<
|
||||
| 'onlyRenderVisibleElements'
|
||||
| 'selectNodesOnDrag'
|
||||
| 'defaultMarkerColor'
|
||||
| 'rfId'
|
||||
> & {
|
||||
children: ReactNode;
|
||||
};
|
||||
@@ -56,6 +57,7 @@ const FlowRenderer = ({
|
||||
onSelectionContextMenu,
|
||||
noWheelClassName,
|
||||
noPanClassName,
|
||||
disableKeyboardA11y,
|
||||
}: FlowRendererProps) => {
|
||||
const store = useStoreApi();
|
||||
const nodesSelectionActive = useStore(selector);
|
||||
@@ -95,7 +97,11 @@ const FlowRenderer = ({
|
||||
{children}
|
||||
<UserSelection selectionKeyPressed={selectionKeyPressed} />
|
||||
{nodesSelectionActive && (
|
||||
<NodesSelection onSelectionContextMenu={onSelectionContextMenu} noPanClassName={noPanClassName} />
|
||||
<NodesSelection
|
||||
onSelectionContextMenu={onSelectionContextMenu}
|
||||
noPanClassName={noPanClassName}
|
||||
disableKeyboardA11y={disableKeyboardA11y}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className="react-flow__pane react-flow__container"
|
||||
|
||||
@@ -22,6 +22,8 @@ export interface GraphViewProps
|
||||
noWheelClassName: string;
|
||||
noPanClassName: string;
|
||||
defaultViewport: Viewport;
|
||||
rfId: string;
|
||||
disableKeyboardA11y: boolean;
|
||||
}
|
||||
|
||||
const GraphView = ({
|
||||
@@ -79,7 +81,8 @@ const GraphView = ({
|
||||
noWheelClassName,
|
||||
noPanClassName,
|
||||
elevateEdgesOnSelect,
|
||||
id,
|
||||
disableKeyboardA11y,
|
||||
rfId,
|
||||
}: GraphViewProps) => {
|
||||
useOnInitHandler(onInit);
|
||||
|
||||
@@ -112,6 +115,7 @@ const GraphView = ({
|
||||
noDragClassName={noDragClassName}
|
||||
noWheelClassName={noWheelClassName}
|
||||
noPanClassName={noPanClassName}
|
||||
disableKeyboardA11y={disableKeyboardA11y}
|
||||
>
|
||||
<ViewportWrapper>
|
||||
<EdgeRenderer
|
||||
@@ -134,7 +138,8 @@ const GraphView = ({
|
||||
defaultMarkerColor={defaultMarkerColor}
|
||||
noPanClassName={noPanClassName}
|
||||
elevateEdgesOnSelect={!!elevateEdgesOnSelect}
|
||||
rfId={id}
|
||||
disableKeyboardA11y={disableKeyboardA11y}
|
||||
rfId={rfId}
|
||||
/>
|
||||
<NodeRenderer
|
||||
nodeTypes={nodeTypes}
|
||||
@@ -148,6 +153,8 @@ const GraphView = ({
|
||||
onlyRenderVisibleElements={onlyRenderVisibleElements}
|
||||
noPanClassName={noPanClassName}
|
||||
noDragClassName={noDragClassName}
|
||||
disableKeyboardA11y={disableKeyboardA11y}
|
||||
rfId={rfId}
|
||||
/>
|
||||
</ViewportWrapper>
|
||||
</FlowRenderer>
|
||||
|
||||
@@ -18,6 +18,8 @@ interface NodeRendererProps {
|
||||
onlyRenderVisibleElements: boolean;
|
||||
noPanClassName: string;
|
||||
noDragClassName: string;
|
||||
rfId: string;
|
||||
disableKeyboardA11y: boolean;
|
||||
}
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
@@ -110,6 +112,9 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
||||
noDragClassName={props.noDragClassName}
|
||||
noPanClassName={props.noPanClassName}
|
||||
initialized={!!node.width && !!node.height}
|
||||
rfId={props.rfId}
|
||||
disableKeyboardA11y={props.disableKeyboardA11y}
|
||||
ariaLabel={node.ariaLabel}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { forwardRef } from 'react';
|
||||
import React, { forwardRef, useId } from 'react';
|
||||
import cc from 'classcat';
|
||||
|
||||
import Attribution from '../../components/Attribution';
|
||||
@@ -29,6 +29,7 @@ import GraphView from '../GraphView';
|
||||
import { createNodeTypes } from '../NodeRenderer/utils';
|
||||
import { useNodeOrEdgeTypes } from './utils';
|
||||
import Wrapper from './Wrapper';
|
||||
import A11yDescriptions from '../../components/A11yDescriptions';
|
||||
|
||||
const defaultNodeTypes: NodeTypes = {
|
||||
input: InputNode,
|
||||
@@ -141,12 +142,14 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
||||
proOptions,
|
||||
defaultEdgeOptions,
|
||||
elevateEdgesOnSelect = false,
|
||||
disableKeyboardA11y = false,
|
||||
...rest
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const nodeTypesWrapped = useNodeOrEdgeTypes(nodeTypes, createNodeTypes) as NodeTypesWrapped;
|
||||
const edgeTypesWrapped = useNodeOrEdgeTypes(edgeTypes, createEdgeTypes) as EdgeTypesWrapped;
|
||||
const rfId = useId();
|
||||
|
||||
return (
|
||||
<div {...rest} ref={ref} className={cc(['react-flow', className])}>
|
||||
@@ -205,7 +208,8 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
||||
noWheelClassName={noWheelClassName}
|
||||
noPanClassName={noPanClassName}
|
||||
elevateEdgesOnSelect={elevateEdgesOnSelect}
|
||||
id={rest?.id}
|
||||
rfId={rfId}
|
||||
disableKeyboardA11y={disableKeyboardA11y}
|
||||
/>
|
||||
<StoreUpdater
|
||||
nodes={nodes}
|
||||
@@ -245,6 +249,7 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
||||
{onSelectionChange && <SelectionListener onSelectionChange={onSelectionChange} />}
|
||||
{children}
|
||||
<Attribution proOptions={proOptions} position={attributionPosition} />
|
||||
<A11yDescriptions rfId={rfId} disableKeyboardA11y={disableKeyboardA11y} />
|
||||
</Wrapper>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { FlowRendererProps } from '../FlowRenderer';
|
||||
|
||||
type ZoomPaneProps = Omit<
|
||||
FlowRendererProps,
|
||||
'deleteKeyCode' | 'selectionKeyCode' | 'multiSelectionKeyCode' | 'noDragClassName'
|
||||
'deleteKeyCode' | 'selectionKeyCode' | 'multiSelectionKeyCode' | 'noDragClassName' | 'disableKeyboardA11y'
|
||||
> & { selectionKeyPressed: boolean };
|
||||
|
||||
const viewChanged = (prevViewport: Viewport, eventViewport: any): boolean =>
|
||||
|
||||
Reference in New Issue
Block a user