-
Notifications
You must be signed in to change notification settings - Fork 0
Walkthrough
The walkthrough system provides guided, step-by-step tours for major pages in the graph algorithm visualizer. It uses react-joyride to guide users through features with visual highlights and tooltips.
Each walkthrough is defined in its own React component under src\components\walkthrough, colocated with the page it describes:
| Page | Walkthrough Component |
|---|---|
| Home Page | HomePageWalkthrough.tsx |
| Graph Selection Page | GraphSelectPageWalkthrough.tsx |
| Graph Execution Page | GraphPageWalkthrough.tsx |
| Algorithm Visualization Page | AlgorithmPageWalkthrough.tsx |
Global walkthrough state is managed via Zustand (src\stores\walkthrough-store.ts) and coordinated via src\hooks\WalkthroughController.ts.
Each walkthrough is self-contained and affects only the page that imports it. Components remain decoupled and reusable.
Create a new file (e.g., MyPageWalkthrough.tsx) and define the walkthrough steps using react-joyride:
const steps: Step[] = [
{
target: ".joyride-feature-button",
content: <div>This button does XYZ. Try clicking it!</div>,
},
{
target: "body",
placement: "center",
content: <div>🎉 All done!</div>,
},
];Use the Zustand store to control the walkthrough state:
const MyPageWalkthrough: React.FC = () => {
const { run, stepIndex, visible } = useWalkthroughStore((s) => s.walkthroughs.myPageKey);
return (
<>
{visible && (
<Joyride
steps={steps}
stepIndex={stepIndex}
run={run}
continuous
callback={(data) => handleWalkthroughCallback("myPageKey", data)}
...
/>
)}
</>
);
};To avoid hardcoding walkthrough-specific class names inside components, pass them as props from the page level.
✅ Example in AlgorithmPage.tsx:
<GraphSplitView
...
classNameGraphCard="graph-card"
classNameDownloadGraph="download-graph"
classNameHelpText="help-text"
/>
<CodeViewer
...
classNameController="controller"
classNameViewVariables="view-variables"
/>🔁 In your walkthrough steps:
{
target: ".download-graph",
content: <div>💾 Use this to download your graph!</div>
}This ensures components stay reusable and unaffected by walkthrough logic.
- Add the key to
WalkthroughKeyinwalkthrough-store.ts - Initialize it in
defaultEntries - Use that key in your walkthrough component and in
handleWalkthroughCallback
- Add the Key in Zustand Store
Update WalkthroughKey and defaultEntries in walkthrough-store.ts:
export type WalkthroughKey = "home" | "graphSelect" | "graph" | "algorithm" | "myPageKey";- How Walkthrough Controller controls the Joyride Callback
Each Joyride component defines a callback prop that listens to lifecycle events emitted by react-joyride. These include step changes, user actions (next, back, skip), and tour completion.
We use a shared handler:
callback={(data) => handleWalkthroughCallback("myPageKey", data)}The handleWalkthroughCallback function (in WalkthroughController.ts) receives the data object the joyride sends when user advance or go back and uses Zustand methods to:
- Advance to the next step with
goToStep - Stop the current walkthrough using
stopWalkthrough - Automatically start the next walkthrough in sequence using
startWalkthrough
The data object passed to the callback includes fields like:
{
index: number;
action: "next" | "prev" | "close" | "skip";
lifecycle: "init" | "complete" | ...;
status: "finished" | "skipped" | ...;
type: "step:after" | "tour:end" | ...;
}One thing to be noticed, every walkthrough step emits more than one data object. And we normally only listen to the one with lifecycle:"complete".
You can use this data to customize step behavior, transition timing, or conditionally trigger external logic during the tour.
- Link it in Controller if Needed
If it should automatically transition after the previous page, update nextMap in WalkthroughController.ts:
const nextMap: Record<WalkthroughKey, WalkthroughKey | null> = {
home: "graphSelect",
graphSelect: "graph",
graph: "algorithm",
algorithm: null,
};- Trigger the Walkthrough
Use startWalkthrough("myPageKey") to begin the tour when appropriate.
The website pages are based on React.animatedroutes, which gives a fading animation whenever navigating to another page. But the DOM element that walkthrough are tied to doesn't fade like others, which cause the "glitch" problem.
To avoid this, use the useSafeNavigate hook (defined in src\utils\safe-navigate.ts), which wraps React Router's useNavigate:
const navigate = useSafeNavigate();
navigate("/some-page");This hook ensures that all walkthroughs are temporarily hidden before navigation and restored after the target page has mounted, using the pauseWalkthroughs function from the Zustand store.
Internally, it wraps navigation like this:
store.pauseWalkthroughs(() => navigate(to, options));You should use useSafeNavigate as a drop-in replacement for useNavigate anywhere navigation might occur during or after a walkthrough.