feat: onboard React Flow documentation views
This commit is contained in:
@@ -105,12 +105,21 @@ id: feature-001
|
||||
title: Display project lifecycle state
|
||||
status: in-progress
|
||||
epic_id: epic-001
|
||||
depends_on_feature_ids:
|
||||
- feature-000
|
||||
roadmap:
|
||||
start: 2026-08-01
|
||||
end: 2026-08-21
|
||||
lane: portal
|
||||
progress: 60
|
||||
summary: >
|
||||
Display the current lifecycle state and supporting evidence.
|
||||
owners:
|
||||
- root-at-skic
|
||||
```
|
||||
|
||||
`depends_on_feature_ids` is the canonical Feature dependency field. `roadmap` is optional unless the documentation exposes a date-based roadmap. When present, validate ISO dates, require `end` not to precede `start`, require a stable lane, and constrain `progress` to 0–100. React Flow adapters may derive dependency edges and Gantt-like positions from these fields; TSX and MDX must not maintain separate schedules or dependency lists.
|
||||
|
||||
### Task
|
||||
|
||||
A Task belongs to one Feature. The Task owns that relationship through `feature_id`.
|
||||
@@ -267,6 +276,8 @@ Validation must fail for:
|
||||
- an unknown entity type, status, or requirement category;
|
||||
- a required field with an empty value;
|
||||
- a dangling `epic_id`, `feature_id`, or entry in `feature_ids`;
|
||||
- a dangling entry in `depends_on_feature_ids`;
|
||||
- an invalid roadmap date, reversed date range, missing lane, or progress outside 0–100;
|
||||
- an index entry whose record is missing;
|
||||
- a record missing from its collection index;
|
||||
- a stale relationship index;
|
||||
@@ -386,6 +397,7 @@ For schema evolution, increment `schema_version`, migrate all records and indexe
|
||||
- [ ] Every entity has one stable-ID record file.
|
||||
- [ ] Every leaf collection has a deterministic `index.yml`.
|
||||
- [ ] Canonical relationship fields implement Epic → Feature → Task and n:m mappings to requirements and architectural decisions.
|
||||
- [ ] Feature dependencies and optional roadmap scheduling remain in source YAML and pass referential and date-range validation.
|
||||
- [ ] Reverse indexes are generated, committed, and stale-index checked.
|
||||
- [ ] Referential validation rejects duplicate and dangling IDs.
|
||||
- [ ] Docusaurus loads data at build time.
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user