410 lines
15 KiB
Markdown
410 lines
15 KiB
Markdown
# React Flow Documentation Visualizations
|
|
|
|
Use [React Flow](https://reactflow.dev/) when structured documentation data needs an interactive spatial view: roadmaps, Gantt-like timelines, dependency graphs, or traceability maps between Features, requirements, and architectural decisions. React Flow is a renderer and interaction layer, not the authoritative data store and not a built-in layout engine.
|
|
|
|
This capability assumes the local YAML data pattern in [local-yaml-data-database.md](local-yaml-data-database.md). Build nodes and edges from validated source YAML records. Never maintain a second copy of roadmap or relationship data inside TSX or MDX.
|
|
|
|
## Appropriate uses
|
|
|
|
Use React Flow for:
|
|
|
|
- a roadmap organized by Epic, lane, status, release, or date;
|
|
- a Gantt-like roadmap where time determines horizontal position and lane determines vertical position;
|
|
- Feature-to-Feature dependencies;
|
|
- Requirement-to-Feature n:m traceability;
|
|
- architectural decision-to-Feature n:m traceability;
|
|
- a combined, filterable delivery or architecture dependency map.
|
|
|
|
Do not use React Flow for ordinary prose, small static tables, or a linear list with no useful spatial relationship. Keep a semantic table fallback for every visualization so the information remains searchable, printable, and accessible without client-side JavaScript.
|
|
|
|
## Official baseline
|
|
|
|
Use the current package name documented by React Flow:
|
|
|
|
```text
|
|
pnpm add @xyflow/react
|
|
```
|
|
|
|
Import the required base stylesheet once in the visualization component or shared theme entry:
|
|
|
|
```ts
|
|
import '@xyflow/react/dist/style.css';
|
|
```
|
|
|
|
Relevant official guidance:
|
|
|
|
- [Quick Start](https://reactflow.dev/learn)
|
|
- [Layouting overview](https://reactflow.dev/learn/layouting/layouting)
|
|
- [Accessibility](https://reactflow.dev/learn/advanced-use/accessibility)
|
|
- [Server-side rendering](https://reactflow.dev/learn/advanced-use/ssr-ssg-configuration)
|
|
- [Testing](https://reactflow.dev/learn/advanced-use/testing)
|
|
|
|
React Flow does not provide one automatic graph layout. Its official layouting guide describes external options including Dagre, D3, and ELK. Choose and document the layout deliberately.
|
|
|
|
## Component boundary
|
|
|
|
Keep data loading, graph adaptation, layout, and rendering separate:
|
|
|
|
```text
|
|
data/**/*.yml
|
|
↓ build-time validation
|
|
DocumentationDatabase
|
|
↓ pure adapter
|
|
GraphModel { nodes, edges, fallbackRows, legend }
|
|
↓ pure layout
|
|
PositionedGraphModel
|
|
↓ React component
|
|
RoadmapFlow | DependencyMap
|
|
↓ MDX composition
|
|
Documentation page
|
|
```
|
|
|
|
Recommended source structure:
|
|
|
|
```text
|
|
src/components/documentation-data/
|
|
├── react-flow/
|
|
│ ├── DocumentationFlow.tsx
|
|
│ ├── RoadmapFlow.tsx
|
|
│ ├── DependencyMap.tsx
|
|
│ ├── nodes/
|
|
│ │ ├── FeatureNode.tsx
|
|
│ │ ├── RequirementNode.tsx
|
|
│ │ └── ArchitectureDecisionNode.tsx
|
|
│ ├── adapters/
|
|
│ │ ├── roadmapGraph.ts
|
|
│ │ ├── featureDependencyGraph.ts
|
|
│ │ ├── requirementFeatureGraph.ts
|
|
│ │ └── decisionFeatureGraph.ts
|
|
│ ├── layout/
|
|
│ │ ├── ganttLayout.ts
|
|
│ │ └── dependencyLayout.ts
|
|
│ └── DocumentationFlow.module.css
|
|
└── fallbacks/
|
|
├── RoadmapTable.tsx
|
|
└── TraceabilityTable.tsx
|
|
```
|
|
|
|
Adapters and layout functions must be pure TypeScript functions. Test them without a browser. React components receive already validated records or a typed graph model; they do not parse files.
|
|
|
|
## Docusaurus client boundary
|
|
|
|
React Flow 12 supports server rendering when node dimensions and handle positions are supplied. That is an advanced path. For normal interactive Docusaurus documentation, use Docusaurus `BrowserOnly` to avoid hydration differences and browser-global failures:
|
|
|
|
```tsx
|
|
import BrowserOnly from '@docusaurus/BrowserOnly';
|
|
import type {Props} from './DependencyMap';
|
|
|
|
export default function DependencyMapClient(props: Props) {
|
|
return (
|
|
<BrowserOnly fallback={<div>Loading dependency map…</div>}>
|
|
{() => {
|
|
const DependencyMap = require('./DependencyMap').default;
|
|
return <DependencyMap {...props} />;
|
|
}}
|
|
</BrowserOnly>
|
|
);
|
|
}
|
|
```
|
|
|
|
Use `@docusaurus/BrowserOnly`, not an unguarded `window` check scattered through components. If server-rendered graph HTML is an explicit requirement, follow the official React Flow 12 SSR guidance and provide deterministic node `width` and `height`, handle positions, and initial viewport data.
|
|
|
|
## Required canvas sizing
|
|
|
|
React Flow requires a parent with explicit dimensions. Never rely on content height:
|
|
|
|
```css
|
|
.canvas {
|
|
width: 100%;
|
|
height: min(72vh, 760px);
|
|
min-height: 420px;
|
|
border: 1px solid var(--ifm-color-emphasis-300);
|
|
border-radius: 0;
|
|
}
|
|
|
|
:global(.react-flow__node),
|
|
:global(.react-flow__controls-button),
|
|
:global(.react-flow__minimap) {
|
|
border-radius: 0;
|
|
}
|
|
```
|
|
|
|
Use sharp edges throughout. Do not introduce pill nodes, rounded cards, rounded controls, or rounded filter inputs.
|
|
|
|
## Shared read-only renderer
|
|
|
|
Documentation views are read-only unless an editing workflow is separately approved. Disable accidental graph mutation:
|
|
|
|
```tsx
|
|
import {
|
|
Background,
|
|
Controls,
|
|
MiniMap,
|
|
ReactFlow,
|
|
ReactFlowProvider,
|
|
type Edge,
|
|
type Node,
|
|
} from '@xyflow/react';
|
|
import '@xyflow/react/dist/style.css';
|
|
|
|
export function DocumentationFlow({nodes, edges}: {
|
|
nodes: Node[];
|
|
edges: Edge[];
|
|
}) {
|
|
return (
|
|
<div className={styles.canvas} aria-label="Documentation dependency map">
|
|
<ReactFlowProvider>
|
|
<ReactFlow
|
|
nodes={nodes}
|
|
edges={edges}
|
|
fitView
|
|
nodesDraggable={false}
|
|
nodesConnectable={false}
|
|
deleteKeyCode={null}
|
|
nodesFocusable
|
|
edgesFocusable
|
|
disableKeyboardA11y={false}
|
|
minZoom={0.35}
|
|
maxZoom={1.8}
|
|
proOptions={{hideAttribution: false}}
|
|
>
|
|
<Controls showInteractive={false} />
|
|
<MiniMap pannable zoomable />
|
|
<Background />
|
|
</ReactFlow>
|
|
</ReactFlowProvider>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
Keep attribution behavior compliant with the installed package's license and React Flow terms. Do not hide attribution merely for visual preference.
|
|
|
|
## Roadmap and Gantt-like layout
|
|
|
|
React Flow is not a dedicated Gantt package. Implement a Gantt-like roadmap as a deterministic node layout over validated scheduling fields.
|
|
|
|
Recommended Feature source fields:
|
|
|
|
```yaml
|
|
id: feature-001
|
|
title: Display project lifecycle state
|
|
epic_id: epic-001
|
|
status: in-progress
|
|
depends_on_feature_ids:
|
|
- feature-000
|
|
roadmap:
|
|
start: 2026-08-01
|
|
end: 2026-08-21
|
|
lane: portal
|
|
progress: 60
|
|
```
|
|
|
|
Layout rules:
|
|
|
|
1. Parse dates in one declared timezone and reject invalid ranges.
|
|
2. Compute `x` from `start` relative to the roadmap's minimum date.
|
|
3. Compute node width from the duration between `start` and `end`.
|
|
4. Compute `y` from the stable lane order in data or configuration.
|
|
5. Draw Feature dependency edges from `depends_on_feature_ids`.
|
|
6. Render release or milestone markers as non-connectable custom nodes.
|
|
7. Preserve a consistent time scale while zooming; include a visible date-axis component outside the graph viewport.
|
|
8. Provide filters for Epic, status, lane, release, and owner without mutating source data.
|
|
9. Provide a table fallback ordered by lane, start date, end date, and stable Feature ID.
|
|
|
|
Do not let dragging persist a new schedule. A documentation reader moving a node is not a data edit. If manual positioning is required for a non-time-based roadmap, store approved coordinates in source YAML and validate them.
|
|
|
|
For a roadmap without dates, use a phase or status-column layout. Keep phases in source data and calculate positions deterministically. Do not infer ordering from file enumeration.
|
|
|
|
## Dependency layouts
|
|
|
|
### Feature dependencies
|
|
|
|
Build one node per Feature and one directed edge per `depends_on_feature_ids` entry. Reject missing Feature IDs. Detect cycles and either fail validation for a declared DAG or render cycles with an explicit warning when cyclic relationships are valid domain data.
|
|
|
|
For ordinary directed acyclic Feature graphs, use Dagre:
|
|
|
|
```text
|
|
pnpm add @dagrejs/dagre
|
|
```
|
|
|
|
Dagre is appropriate for a compact, deterministic directed layout. Set node dimensions before layout and map Dagre's center-based coordinates to React Flow positions.
|
|
|
|
### Complex or grouped dependencies
|
|
|
|
For larger maps with nested Epics, compound nodes, multiple ports, or stronger edge-routing requirements, use ELK:
|
|
|
|
```text
|
|
pnpm add elkjs
|
|
```
|
|
|
|
ELK layout is asynchronous. Resolve it before rendering the final graph and show a stable loading state. Do not run a new layout on every React render.
|
|
|
|
### Requirement-to-Feature traceability
|
|
|
|
Create typed nodes and edges from canonical requirement `feature_ids`:
|
|
|
|
```text
|
|
Functional Requirement ── satisfies/constrains ──> Feature
|
|
Non-functional Requirement ── constrains ──> Feature
|
|
```
|
|
|
|
Use distinct node types, edge styles, labels, and a visible legend. Filters must distinguish functional and non-functional requirements. Do not encode meaning only with color.
|
|
|
|
### Architectural decision-to-Feature traceability
|
|
|
|
Create typed nodes and edges from canonical architectural decision `feature_ids`:
|
|
|
|
```text
|
|
Architectural Decision ── governs/affects ──> Feature
|
|
```
|
|
|
|
Display decision status and link each node to its canonical documentation route. A superseded decision remains traceable but must be visually and textually identified as superseded.
|
|
|
|
### Combined maps
|
|
|
|
A combined map may include Features, requirements, and architectural decisions, but it must start with a constrained default scope. Large all-record graphs become unreadable and expensive. Require one or more of:
|
|
|
|
- selected Epic;
|
|
- selected Feature;
|
|
- selected requirement type;
|
|
- selected decision status;
|
|
- bounded dependency depth;
|
|
- search result or explicit ID list.
|
|
|
|
Apply filtering before layout so hidden nodes do not consume space.
|
|
|
|
## Adapter contract
|
|
|
|
A graph adapter should return all rendering and fallback information from one source:
|
|
|
|
```ts
|
|
export type DocumentationGraph = {
|
|
nodes: Node<DocumentationNodeData>[];
|
|
edges: Edge<DocumentationEdgeData>[];
|
|
fallbackRows: TraceabilityRow[];
|
|
legend: LegendItem[];
|
|
warnings: GraphWarning[];
|
|
};
|
|
|
|
export function requirementFeatureGraph(
|
|
database: DocumentationDatabase,
|
|
filter: RequirementFeatureFilter,
|
|
): DocumentationGraph;
|
|
```
|
|
|
|
Requirements for every adapter:
|
|
|
|
- stable node IDs equal stable database IDs;
|
|
- stable edge IDs derived from source type, source ID, relation, and target ID;
|
|
- no random coordinates or IDs;
|
|
- no filesystem access;
|
|
- no mutation of database records;
|
|
- explicit behavior for missing, filtered, superseded, or cyclic records;
|
|
- table fallback rows derived in the same function as nodes and edges.
|
|
|
|
## MDX composition
|
|
|
|
MDX chooses the view and scope. It does not define nodes or edges:
|
|
|
|
```mdx
|
|
---
|
|
title: "Explore Feature dependencies"
|
|
description: "Inspect Feature dependencies and related requirements."
|
|
---
|
|
|
|
import DependencyMapClient from '@site/src/components/documentation-data/react-flow/DependencyMapClient';
|
|
import TraceabilityTable from '@site/src/components/documentation-data/fallbacks/TraceabilityTable';
|
|
|
|
<DependencyMapClient
|
|
mode="feature-requirements"
|
|
epicId="epic-001"
|
|
/>
|
|
|
|
<TraceabilityTable
|
|
mode="feature-requirements"
|
|
epicId="epic-001"
|
|
/>
|
|
```
|
|
|
|
The map and table must consume the same adapter output or the same normalized selector so they cannot disagree.
|
|
|
|
## Accessibility
|
|
|
|
React Flow provides keyboard and screen-reader support. Preserve it:
|
|
|
|
- keep `nodesFocusable` and `edgesFocusable` enabled;
|
|
- keep `disableKeyboardA11y={false}`;
|
|
- provide meaningful `ariaLabel` values for custom nodes and edges;
|
|
- ensure custom node links and controls have visible focus states;
|
|
- provide text labels in addition to colors and line styles;
|
|
- provide a legend that explains node types, statuses, and edge meanings;
|
|
- provide an adjacent semantic table fallback with the same filtered records;
|
|
- preserve keyboard access to filters, fit-view controls, and linked detail pages;
|
|
- test at 200% zoom and in both light and dark themes.
|
|
|
|
Read-only means mutation is disabled, not navigation or focus.
|
|
|
|
## Performance
|
|
|
|
- Memoize node types outside React components.
|
|
- Build graph data with pure selectors and memoize by database revision and filter state.
|
|
- Filter before layout.
|
|
- Avoid recreating nodes and edges on every render.
|
|
- Do not render the full database by default.
|
|
- Use bounded dependency depth for large graphs.
|
|
- Lazy-load the BrowserOnly visualization component.
|
|
- Run asynchronous ELK layout only when the relevant graph input changes.
|
|
- Measure browser interaction with representative production-size data, not only two-node examples.
|
|
|
|
## Testing and verification
|
|
|
|
Test pure transformation and layout behavior first:
|
|
|
|
- scheduling dates produce stable Gantt `x`, width, and lane `y` values;
|
|
- Feature dependencies produce the expected directed edges;
|
|
- Requirement and architectural decision n:m mappings produce complete edges;
|
|
- filters remove nodes before layout;
|
|
- dangling IDs fail validation before graph construction;
|
|
- stable inputs produce byte-equivalent graph IDs and coordinates;
|
|
- fallback table rows represent the same relationships as graph edges;
|
|
- cycles follow the declared policy.
|
|
|
|
Then test React behavior:
|
|
|
|
- the BrowserOnly fallback renders during static generation;
|
|
- the canvas mounts with explicit width and height;
|
|
- nodes and edges are keyboard focusable;
|
|
- graph mutation is disabled;
|
|
- filters, detail links, fit view, pan, and zoom work;
|
|
- missing and empty datasets render useful states;
|
|
- table fallback remains usable without the graph.
|
|
|
|
Required delivery checks:
|
|
|
|
```text
|
|
pnpm data:validate
|
|
pnpm data:check
|
|
pnpm test
|
|
pnpm typecheck
|
|
pnpm build
|
|
```
|
|
|
|
After the production build, exercise each affected page in a real browser. Verify light and dark themes, desktop and narrow widths, keyboard navigation, labels, filters, links, pan/zoom, empty states, and the table fallback. Then verify the exact Gitea Actions SHA and deployed route.
|
|
|
|
## Acceptance checklist
|
|
|
|
- [ ] `@xyflow/react` and its base stylesheet are installed and imported.
|
|
- [ ] React Flow receives validated build-time data from source YAML through a pure adapter.
|
|
- [ ] Roadmap scheduling and Feature dependencies are stored in YAML, not TSX or MDX.
|
|
- [ ] Gantt-like positions are deterministic from dates and lanes.
|
|
- [ ] Dagre or ELK is chosen explicitly for dependency layout; React Flow is not described as providing automatic layout.
|
|
- [ ] Feature, requirement, and architectural decision relationships use stable IDs and typed nodes/edges.
|
|
- [ ] Docusaurus uses `BrowserOnly`, or the advanced React Flow 12 SSR dimensions are fully configured.
|
|
- [ ] The canvas has explicit width and height.
|
|
- [ ] Documentation views disable dragging, connecting, and deletion.
|
|
- [ ] Keyboard and screen-reader support remains enabled.
|
|
- [ ] Sharp styling applies `border-radius: 0` to nodes, controls, minimaps, filters, and surrounding panels.
|
|
- [ ] Every visualization has a semantic table fallback derived from the same adapter.
|
|
- [ ] Unit, component, type, production build, browser, Actions, and deployed-route checks pass.
|