feat(issue-17): add onboarding wizard for new users (#26)
* feat(issue-17): add onboarding wizard for new users
Adds a step-based onboarding wizard that guides new users through their
first Claw3D setup: welcome, prerequisites, gateway connection, agent
discovery, and a completion screen.
Architecture:
src/features/onboarding/ (new feature module):
- types.ts: Step definitions, navigation helpers (getNextStep/getPrevStep)
- useOnboardingState.ts: localStorage-backed persistence hook
- index.ts: Barrel exports for clean imports
- components/OnboardingWizard.tsx: Main wizard container with step
navigation, progress bar, and modal overlay
- components/WelcomeStep.tsx: Feature highlights grid
- components/PrerequisitesStep.tsx: Checklist with links/commands
- components/ConnectStep.tsx: Compact gateway connection form
- components/AgentsStep.tsx: Agent discovery feedback
- components/CompleteStep.tsx: Final screen with CTA
Design decisions:
- Modular step system: new steps can be added by extending
OnboardingStepId and registering a component in the switch
- localStorage persistence: wizard shows once per browser, resettable
from settings (future: wire into Studio settings API)
- Connect step gates forward navigation: users cannot skip connection
- Follows Claw3D conventions: feature-first module, no shared state
pollution, Tailwind utility classes, lucide-react icons
- Does NOT modify existing routes or components — zero-risk integration
(parent wiring left to maintainer preference)
Integration guide (for maintainer):
1. Import { OnboardingWizard, useOnboardingState } from the module
2. Add useOnboardingState() to the root layout or agents page
3. Render <OnboardingWizard /> when showOnboarding is true
4. Pass gateway connection props from existing store
tests/unit/onboardingTypes.test.ts (13 tests):
- Step structure validation, navigation helpers, ordering
tests/unit/onboardingState.test.ts (5 tests):
- localStorage persistence, show/hide/reset lifecycle
Addresses #17
* fix(onboarding): wire wizard launch into office UI
Mount the onboarding wizard in OfficeScreen and add a settings action that can re-open it so the new-user flow is reachable and testable.
Made-with: Cursor
---------
Co-authored-by: Neo <neo@openclaw.ai>
Co-authored-by: iamlukethedev <lucas.guilherme@smartwayslfl.com>
This commit is contained in:
committed by
GitHub
parent
ac30f71db0
commit
c2cbdeec44
@@ -0,0 +1,90 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
getNextStep,
|
||||
getPrevStep,
|
||||
getStepIndex,
|
||||
ONBOARDING_STEPS,
|
||||
} from "@/features/onboarding/types";
|
||||
|
||||
describe("ONBOARDING_STEPS", () => {
|
||||
it("has at least 3 steps", () => {
|
||||
expect(ONBOARDING_STEPS.length).toBeGreaterThanOrEqual(3);
|
||||
});
|
||||
|
||||
it("starts with welcome and ends with complete", () => {
|
||||
expect(ONBOARDING_STEPS[0].id).toBe("welcome");
|
||||
expect(ONBOARDING_STEPS[ONBOARDING_STEPS.length - 1].id).toBe("complete");
|
||||
});
|
||||
|
||||
it("has unique step IDs", () => {
|
||||
const ids = ONBOARDING_STEPS.map((s) => s.id);
|
||||
expect(new Set(ids).size).toBe(ids.length);
|
||||
});
|
||||
|
||||
it("every step has a non-empty title and description", () => {
|
||||
for (const step of ONBOARDING_STEPS) {
|
||||
expect(step.title.length).toBeGreaterThan(0);
|
||||
expect(step.description.length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("getStepIndex", () => {
|
||||
it("returns 0 for welcome", () => {
|
||||
expect(getStepIndex("welcome")).toBe(0);
|
||||
});
|
||||
|
||||
it("returns last index for complete", () => {
|
||||
expect(getStepIndex("complete")).toBe(ONBOARDING_STEPS.length - 1);
|
||||
});
|
||||
|
||||
it("returns correct index for connect", () => {
|
||||
const idx = ONBOARDING_STEPS.findIndex((s) => s.id === "connect");
|
||||
expect(getStepIndex("connect")).toBe(idx);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getNextStep", () => {
|
||||
it("returns prerequisites after welcome", () => {
|
||||
expect(getNextStep("welcome")).toBe("prerequisites");
|
||||
});
|
||||
|
||||
it("returns null after complete", () => {
|
||||
expect(getNextStep("complete")).toBeNull();
|
||||
});
|
||||
|
||||
it("advances through all steps in order", () => {
|
||||
let current = ONBOARDING_STEPS[0].id;
|
||||
const visited: string[] = [current];
|
||||
let next = getNextStep(current);
|
||||
while (next) {
|
||||
visited.push(next);
|
||||
current = next;
|
||||
next = getNextStep(current);
|
||||
}
|
||||
expect(visited).toEqual(ONBOARDING_STEPS.map((s) => s.id));
|
||||
});
|
||||
});
|
||||
|
||||
describe("getPrevStep", () => {
|
||||
it("returns null for welcome", () => {
|
||||
expect(getPrevStep("welcome")).toBeNull();
|
||||
});
|
||||
|
||||
it("returns prerequisites for connect", () => {
|
||||
expect(getPrevStep("connect")).toBe("prerequisites");
|
||||
});
|
||||
|
||||
it("navigates backward through all steps", () => {
|
||||
let current = ONBOARDING_STEPS[ONBOARDING_STEPS.length - 1].id;
|
||||
const visited: string[] = [current];
|
||||
let prev = getPrevStep(current);
|
||||
while (prev) {
|
||||
visited.push(prev);
|
||||
current = prev;
|
||||
prev = getPrevStep(current);
|
||||
}
|
||||
visited.reverse();
|
||||
expect(visited).toEqual(ONBOARDING_STEPS.map((s) => s.id));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user