Skip to content

Commit

Permalink
[UI v2] feat: Adds useStepper hook to be used for automations creatio…
Browse files Browse the repository at this point in the history
…n wizard (#16461)
  • Loading branch information
devinvillarosa authored Dec 20, 2024
1 parent dc79adc commit 7bf7697
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 0 deletions.
131 changes: 131 additions & 0 deletions ui-v2/src/hooks/use-stepper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { act, renderHook } from "@testing-library/react";
import { useStepper } from "./use-stepper";

import { describe, expect, it } from "vitest";

const TOTAL_NUM_STEPS = 3;

describe("useStepper()", () => {
it("incrementStep() to the next available step", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS));

// Update State
act(() => result.current.incrementStep());

// Asserts
expect(result.current.currentStep).toEqual(1);
});

it("incrementStep() does not increment at final step", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS, 2));

// Update State
act(() => result.current.incrementStep());

// Asserts
expect(result.current.currentStep).toEqual(2);
});

it("decrementStep() to the previous available step", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS, 2));

// Update State
act(() => result.current.decrementStep());

// Asserts
expect(result.current.currentStep).toEqual(1);
});

it("decrementStep() does not decrement at first step", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS));

// Update State
act(() => result.current.decrementStep());

// Asserts
expect(result.current.currentStep).toEqual(0);
});

it("getIsCurrentStep() returns true if its the correct step", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS));

// Asserts
expect(result.current.getIsCurrentStep(0)).toBe(true);
});
it("getIsCurrentStep() returns false if its the correct step", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS));

// Asserts
expect(result.current.getIsCurrentStep(1)).toBe(false);
});

it("getIsStepCompleted() returns true if the step has been passed", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS, 1));

// Asserts
expect(result.current.getIsStepCompleted(0)).toBe(true);
});

it("getIsStepCompleted() returns false if the step has not been passed", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS, 1));

// Asserts
expect(result.current.getIsStepCompleted(1)).toBe(false);
expect(result.current.getIsStepCompleted(2)).toBe(false);
});

it("isFinalStep returns true if its the final step", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS, 2));

// Asserts
expect(result.current.isFinalStep).toBe(true);
});
it("isFinalStep returns false if its the final step", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS));

// Asserts
expect(result.current.isFinalStep).toBe(false);
});

it("isStartingStep returns true if its the first step", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS));

// Asserts
expect(result.current.isStartingStep).toBe(true);
});
it("isStartingStep returns false if its the final step", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS, 1));

// Asserts
expect(result.current.isStartingStep).toBe(false);
});

it("reset() returns to the initial step", () => {
// Setup
const { result } = renderHook(() => useStepper(TOTAL_NUM_STEPS, 1));

// Update State
act(() => result.current.decrementStep());

// Asserts
expect(result.current.currentStep).toEqual(0);

// Update State
act(() => result.current.reset());

// Asserts
expect(result.current.currentStep).toEqual(1);
});
});
64 changes: 64 additions & 0 deletions ui-v2/src/hooks/use-stepper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { useState } from "react";

/**
*
* @param steps
* @param startingStep
* @returns state with declarative utilities to increment, decrement, or skip to a current state
*
* @example
* ```ts
* const STEPS = ['Trigger', 'Actions', 'Details'] as const
*
* const stepper = useStepper(STEPS.length);
*
* return (
* <div>
* <h2>Current Step: {STEPS[stepper.currentStep]}</h2>
* <ul>
* {STEPS.map((step, i) => (
* <li key={i} onClick={() => stepper.setStep(i)}>{step}</li>
* )}
* </ul>
* <div>
* <Button disabled={stepper.isStartingStep} onClick={stepper.decrementStep()}>Previous</Button>
* <Button onClick={stepper.incrementStep()}>{stepper.isFinalStep ? 'Save' : 'Next'}</Button>
* </div>
* </div>
* )
* ```
*/
export const useStepper = (numSteps: number, startingStep: number = 0) => {
const [currentStep, setCurrentStep] = useState(startingStep);

const incrementStep = () => {
if (currentStep < numSteps - 1) {
setCurrentStep((curr) => curr + 1);
}
};

const decrementStep = () => {
if (currentStep > 0) {
setCurrentStep((curr) => curr - 1);
}
};

const reset = () => setCurrentStep(startingStep);

const getIsStepCompleted = (stepNum: number) => stepNum < currentStep;
const getIsCurrentStep = (stepNum: number) => stepNum === currentStep;
const isFinalStep = currentStep === numSteps - 1;
const isStartingStep = currentStep === 0;

return {
currentStep,
decrementStep,
getIsCurrentStep,
getIsStepCompleted,
incrementStep,
isFinalStep,
isStartingStep,
reset,
setCurrentStep,
};
};

0 comments on commit 7bf7697

Please sign in to comment.