JSPM

  • Created
  • Published
  • Downloads 386
  • Score
    100M100P100Q175017F
  • License MIT

Sammy Labs Walkthroughs

Package Exports

  • @sammy-labs/walkthroughs
  • @sammy-labs/walkthroughs/dist/index.css

Readme

@sammy-labs/walkthroughs

This package provides a simple, Provider + Hook architecture to integrate Sammy's interactive walkthroughs into your React (or Next.js) application. It manages global state, highlights elements on the page, and guides the user step-by-step through a sequence of interactions.

Key features include:

  • Global WalkthroughProvider to manage authentication tokens, driver instances, and global config.
  • React Hook useWalkthrough to start, stop, or control walkthroughs anywhere in your app.
  • Utility methods for fetching and transforming walkthrough data (from pre-fetched).
  • Highly configurable overlay, popover, animations, and interaction settings.

Table of Contents


Installation

npm install @sammy-labs/walkthroughs
# or
yarn add @sammy-labs/walkthroughs

Core Concepts

  1. WalkthroughProvider
    A React provider that must wrap the part of your app which uses the walkthroughs. It manages:

    • The short-lived auth token (JWT) for API calls.
    • The current walkthrough state (highlighted element, popover, etc.).
    • Global configuration (e.g., debug mode, default API URL).
  2. useWalkthrough Hook
    A React Hook that gives you access to simple methods to:

    • startWithData(flowData): Start a walkthrough using pre-fetched or pre-built data.
    • stop(): Stop any active walkthrough.
    • isActive(): Check if a walkthrough is currently running.
    • Additional convenience and configuration methods.
  3. flow-guide utilities
    Internally, the package has utilities for:

    • Fetching walkthrough data from an API.
    • Constructing or parsing the data format into steps that the driver can execute.

These are used behind the scenes by methods like startWithId or startWithData.

Important Principle

The <WalkthroughProvider> is the single source of truth for starting/stopping walkthroughs. All calls to start or stop a walkthrough should happen via the Hook (useWalkthrough) that reads from this Provider.


Quick Start

  1. Install the package:

    npm install @sammy-labs/walkthroughs
  2. Wrap your app (or a high-level part of it) with the <WalkthroughProvider>:

    import React from "react";
    import { WalkthroughProvider } from "@sammy-labs/walkthroughs";
    
    function MyApp({ Component, pageProps }) {
      return (
        <WalkthroughProvider
          token={/* your JWT token, if any */}
          baseUrl="https://api.com"
          onTokenExpired={() => console.log("Token expired!")}
          onError={(err) => console.error("Walkthrough error:", err)}
          driverConfig={{ overlayOpacity: 0.8 }}
          config={{ debug: true }}
        >
          <Component {...pageProps} />
        </WalkthroughProvider>
      );
    }
    
    export default MyApp;

    The token here is typically fetched from your API or via some authentication method.

  3. Use the useWalkthrough hook in any React component to start a walkthrough:

    import React from "react";
    import { useWalkthrough } from "@sammy-labs/walkthroughs";
    
    export function MyPage() {
      const { startWithId, stop, isActive } = useWalkthrough();
      let data = [...]
    
      const handleStart = async () => {
        const success = await startWithdata(data);
        if (!success) {
          alert("Failed to start walkthrough.");
        }
      };
    
      return (
        <div>
          <button onClick={handleStart}>Start Walkthrough #1234</button>
          {isActive() && <button onClick={stop}>Stop Walkthrough</button>}
        </div>
      );
    }

That's it! The library will handle highlighting and guiding the user.


Usage in Next.js

Below is a simplified pattern for integrating the library into a Next.js application. You can adapt it to your framework of choice.

Provider Example

You'll want a custom provider in your Next.js app that fetches an JWT token (if needed) and passes it into <WalkthroughProvider>. For example:

"use client";

import React, { useState, useEffect, useCallback } from "react";
import { WalkthroughProvider } from "@sammy-labs/walkthroughs";
import type { ReactNode } from "react";

export default function SammyWalkthroughProvider({
  children,
}: {
  children: ReactNode;
}) {
  const [token, setToken] = useState<string | undefined>(undefined);
  const [loading, setLoading] = useState(true);

  // Typically from an .env file
  const baseUrl = process.env.NEXT_PUBLIC_SAMMY_BASE_URL || "";

  const fetchToken = useCallback(async () => {
    try {
      // Your logic to fetch/generate a token
      const resp = await fetch("/api/auth/sammy");
      const { token: newToken } = await resp.json();
      setToken(newToken);
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    fetchToken();
  }, [fetchToken]);

  const handleTokenExpired = useCallback(() => {
    console.log("Token expired, fetching again...");
    fetchToken();
  }, [fetchToken]);

  if (loading) {
    return <div>Loading token...</div>;
  }

  return (
    <WalkthroughProvider
      token={token}
      baseUrl={baseUrl}
      onTokenExpired={handleTokenExpired}
      onError={(err) => console.warn("Walkthrough error:", err)}
      driverConfig={{ overlayOpacity: 0.8 }}
      config={{ debug: true }}
    >
      {children}
    </WalkthroughProvider>
  );
}

Then in your Next.js layout.tsx or wherever you set up root providers:

// app/layout.tsx
import SammyWalkthroughProvider from "./providers/sammy-walkthrough-provider";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <SammyWalkthroughProvider>{children}</SammyWalkthroughProvider>
      </body>
    </html>
  );
}

Using the Hook in Pages or Components

Within your page (e.g., app/login/page.tsx or src/pages/login.tsx), you can:

"use client";

import { useState } from "react";
import { useWalkthrough, WalkthroughResponse } from "@sammy-labs/walkthroughs";

export default function LoginPage() {
  const { startWithData, stop, isActive } = useWalkthrough();

  const [someData, setSomeData] = useState<WalkthroughResponse | null>(null);

  const handleStart = async () => {
    if (!someData) {
      return;
    }
    const success = await startWithData(someData);
    if (!success) {
      alert("Failed to start walkthrough.");
    }
  };

  return (
    <div>
      <button onClick={handleStart} disabled={!someData}>
        Start Walkthrough
      </button>
      {isActive() && <button onClick={stop}>Stop Walkthrough</button>}
    </div>
  );
}

You can fetch or generate someData from your server or from an API endpoint-this library will parse it to steps automatically.


API Reference

<WalkthroughProvider />

Prop Type Description
token string | null The authentication token (JWT). If not provided, certain API calls may fail.
baseUrl string The base API URL for fetching walkthrough data. Defaults to http://localhost:8000.
onTokenExpired () => void Callback fired if the token is invalid or expires (e.g., 401 responses). You can re-fetch a new token here.
onError (error: FlowError) => void Callback for reporting any error encountered while starting or running a walkthrough.
driverConfig Partial<DriverConfig> Fine-grained configuration for the highlight driver (overlay color, opacity, popover offset, etc.).
config Partial<WalkthroughConfig> Global configuration (debug, max element find attempts, wait time, etc.).
logoUrl string Optional logo to display in the popover.
children ReactNode The rest of your app.

Example:

<WalkthroughProvider
  token={myToken}
  baseUrl="https://myapi.com"
  onTokenExpired={() => {
    /* re-fetch token */
  }}
  onError={(err) => console.error("Walkthrough Error:", err)}
  driverConfig={{ overlayOpacity: 0.8, stagePadding: 8 }}
  config={{ debug: true, flowIdQueryParam: "sammy_flow_id" }}
>
  <App />
</WalkthroughProvider>

useWalkthrough(options?)

A Hook that returns convenience methods and state.

Signature:

function useWalkthrough(options?: {
  checkQueryOnMount?: boolean;
  onError?: (error: FlowError) => void;
  waitTime?: number;
  driverConfig?: Partial<DriverConfig>;
  config?: Partial<WalkthroughConfig>;
  disableRedirects?: boolean;
  autoStartPendingWalkthrough?: boolean;
}): {
  // Methods
  startWithId(flowId: string | number): Promise<boolean>;
  startWithData(
    data: WalkthroughResponse | WalkthroughSearchResult | any
  ): Promise<boolean>;
  stop(): boolean;
  isActive(): boolean;
  configure(config: Partial<DriverConfig>): void;
  configureGlobal(config: Partial<WalkthroughConfig>): void;

  // State
  state: {
    isTokenValid: boolean;
    isLoading: boolean;
    error: FlowError | null;
    token: string | null;
    baseUrl: string;
    isActive: boolean;
    config: WalkthroughConfig;
  };
};

Methods

  • startWithId(flowId)
    Fetches walkthrough data from the API (using flowId) and starts the flow if successful.

    • Returns a Promise of true if successful, false otherwise.
  • startWithData(data)
    Starts a walkthrough using pre-fetched or user-provided data in the correct shape.

    • Accepts either a WalkthroughResponse or an older search result format.
    • Returns a Promise of true if started successfully, false otherwise.
  • stop()
    Stops any active walkthrough and cleans up the highlight/overlay.

  • isActive()
    Returns true if a walkthrough is currently active.

  • configure(driverConfig)
    Dynamically updates the driver configuration.

  • configureGlobal(globalConfig)
    Dynamically updates the global configuration.

State

  • isTokenValid: boolean
    Whether the last token validation was successful.
  • isLoading: boolean
    Indicates if the library is currently fetching the flow data.
  • error: FlowError \| null
    Contains any recent error encountered.
  • token: string \| null
    Current token provided to the library.
  • baseUrl: string
    Current base URL for API calls.
  • isActive: boolean
    Shortcut for isActive().
  • config: WalkthroughConfig
    The merged global configuration.

Global and Driver Config

Global Configuration (WalkthroughConfig)

export type WalkthroughConfig = {
  flowIdQueryParam: string; // Default: "sammy_flow_id"
  waitTimeAfterLoadMs: number; // Default: 5000
  maxElementFindAttempts: number; // Default: 3
  elementFindTimeoutMs: number; // Default: 10000
  debug: boolean; // Default: false
  apiBaseUrl: string; // Default: "http://localhost:8000"
  logoUrl: string; // Default: ""
};

You can pass a partial override to <WalkthroughProvider config={...}> or to the hook. This controls the overall behavior (like how many attempts to locate elements, how long to wait for them, etc.).

Driver Configuration (DriverConfig)

export interface DriverConfig {
  steps?: DriveStep[];
  animate?: boolean;
  overlayColor?: string;
  overlayOpacity?: number;
  smoothScroll?: boolean;
  allowClose?: boolean;
  overlayClickBehavior?: "close" | "nextStep";
  stagePadding?: number;
  stageRadius?: number;
  disableActiveInteraction?: boolean;
  allowKeyboardControl?: boolean;
  popoverClass?: string;
  popoverOffset?: number;
  showButtons?: AllowedButtons[];
  disableButtons?: AllowedButtons[];
  showProgress?: boolean;
  progressText?: string;
  nextBtnText?: string;
  prevBtnText?: string;
  doneBtnText?: string;
  logoUrl?: string;
  // Additional callbacks
  onHighlightStarted?: DriverHook;
  onHighlighted?: DriverHook;
  onDeselected?: DriverHook;
  onDestroyStarted?: DriverHook;
  onDestroyed?: DriverHook;
  onNextClick?: DriverHook;
  onPrevClick?: DriverHook;
  onCloseClick?: DriverHook;
}

These govern the visuals and interactivity of the highlight overlay and popover. For example, overlayOpacity: 0.7 or smoothScroll: true.