c2cbdeec44
* 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>
64 lines
1.9 KiB
TypeScript
64 lines
1.9 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { act, renderHook } from "@testing-library/react";
|
|
import { useOnboardingState } from "@/features/onboarding/useOnboardingState";
|
|
|
|
describe("useOnboardingState", () => {
|
|
afterEach(() => {
|
|
// Clean up localStorage between tests
|
|
try {
|
|
window.localStorage.removeItem("claw3d:onboarding:completed");
|
|
} catch {
|
|
// noop
|
|
}
|
|
});
|
|
|
|
it("shows onboarding by default when localStorage is empty", () => {
|
|
const { result } = renderHook(() => useOnboardingState());
|
|
expect(result.current.showOnboarding).toBe(true);
|
|
});
|
|
|
|
it("hides onboarding after completeOnboarding is called", () => {
|
|
const { result } = renderHook(() => useOnboardingState());
|
|
expect(result.current.showOnboarding).toBe(true);
|
|
|
|
act(() => {
|
|
result.current.completeOnboarding();
|
|
});
|
|
|
|
expect(result.current.showOnboarding).toBe(false);
|
|
});
|
|
|
|
it("persists completion to localStorage", () => {
|
|
const { result } = renderHook(() => useOnboardingState());
|
|
|
|
act(() => {
|
|
result.current.completeOnboarding();
|
|
});
|
|
|
|
expect(window.localStorage.getItem("claw3d:onboarding:completed")).toBe(
|
|
"true",
|
|
);
|
|
});
|
|
|
|
it("reads completion state from localStorage on mount", () => {
|
|
window.localStorage.setItem("claw3d:onboarding:completed", "true");
|
|
const { result } = renderHook(() => useOnboardingState());
|
|
expect(result.current.showOnboarding).toBe(false);
|
|
});
|
|
|
|
it("resets onboarding when resetOnboarding is called", () => {
|
|
const { result } = renderHook(() => useOnboardingState());
|
|
|
|
act(() => {
|
|
result.current.completeOnboarding();
|
|
});
|
|
expect(result.current.showOnboarding).toBe(false);
|
|
|
|
act(() => {
|
|
result.current.resetOnboarding();
|
|
});
|
|
expect(result.current.showOnboarding).toBe(true);
|
|
expect(window.localStorage.getItem("claw3d:onboarding:completed")).toBeNull();
|
|
});
|
|
});
|