-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[UI v2] feat: Adds useStepper hook to be used for automations creatio…
…n wizard (#16461)
- Loading branch information
1 parent
dc79adc
commit 7bf7697
Showing
2 changed files
with
195 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}; | ||
}; |