diff --git a/packages/usehooks-ts/src/useStep/useStep.demo.tsx b/packages/usehooks-ts/src/useStep/useStep.demo.tsx index 083f6239..ff82d9e9 100644 --- a/packages/usehooks-ts/src/useStep/useStep.demo.tsx +++ b/packages/usehooks-ts/src/useStep/useStep.demo.tsx @@ -1,7 +1,8 @@ import { useStep } from './useStep' export default function Component() { - const [currentStep, helpers] = useStep(5) + const initialStep = 2; + const [currentStep, helpers] = useStep(5, intitialStep) const { canGoToPrevStep, @@ -14,6 +15,7 @@ export default function Component() { return ( <> +

Initial step is {initialStep}

Current step is {currentStep}

Can go to previous step {canGoToPrevStep ? 'yes' : 'no'}

Can go to next step {canGoToNextStep ? 'yes' : 'no'}

diff --git a/packages/usehooks-ts/src/useStep/useStep.test.ts b/packages/usehooks-ts/src/useStep/useStep.test.ts index 092a100a..794b77eb 100644 --- a/packages/usehooks-ts/src/useStep/useStep.test.ts +++ b/packages/usehooks-ts/src/useStep/useStep.test.ts @@ -76,4 +76,31 @@ describe('useStep()', () => { expect(result.current[1].canGoToNextStep).toBe(true) }) + + it('should set inital step to 1 if max steps is exceeded', () => { + const { result } = renderHook(() => useStep(2, 3)) + + expect(result.current[0]).toBe(1) + }) + + it('should set inital step to 1 if inital step is below minimum', () => { + const { result } = renderHook(() => useStep(2, 0)) + + expect(result.current[0]).toBe(1) + }) + + it('should set inital step to 2', () => { + const { result } = renderHook(() => useStep(2, 2)) + + expect(result.current[0]).toBe(2) + }) + + it('should throw an error', () => { + const nonInteger = '' as never + vi.spyOn(console, 'error').mockImplementation(() => vi.fn()) + expect(() => { + renderHook(() => useStep(2, nonInteger)) + }).toThrowError(/initialStep must be an integer/) + vi.resetAllMocks() + }) }) diff --git a/packages/usehooks-ts/src/useStep/useStep.ts b/packages/usehooks-ts/src/useStep/useStep.ts index 792f373a..60f66082 100644 --- a/packages/usehooks-ts/src/useStep/useStep.ts +++ b/packages/usehooks-ts/src/useStep/useStep.ts @@ -1,28 +1,29 @@ -import { useCallback, useState } from 'react' +import { useCallback, useState } from "react"; -import type { Dispatch, SetStateAction } from 'react' +import type { Dispatch, SetStateAction } from "react"; /** Represents the second element of the output of the `useStep` hook. */ type UseStepActions = { - /** Go to the next step in the process. */ - goToNextStep: () => void - /** Go to the previous step in the process. */ - goToPrevStep: () => void - /** Reset the step to the initial step. */ - reset: () => void - /** Check if the next step is available. */ - canGoToNextStep: boolean - /** Check if the previous step is available. */ - canGoToPrevStep: boolean - /** Set the current step to a specific value. */ - setStep: Dispatch> -} + /** Go to the next step in the process. */ + goToNextStep: () => void; + /** Go to the previous step in the process. */ + goToPrevStep: () => void; + /** Reset the step to the initial step. */ + reset: () => void; + /** Check if the next step is available. */ + canGoToNextStep: boolean; + /** Check if the previous step is available. */ + canGoToPrevStep: boolean; + /** Set the current step to a specific value. */ + setStep: Dispatch>; +}; -type SetStepCallbackType = (step: number | ((step: number) => number)) => void +type SetStepCallbackType = (step: number | ((step: number) => number)) => void; /** * Custom hook that manages and navigates between steps in a multi-step process. * @param {number} maxStep - The maximum step in the process. + * @param {number} [initialStep] - The initial step for the current step state (default is `1`). * @returns {[number, UseStepActions]} An tuple containing the current step and helper functions for navigating steps. * @public * @see [Documentation](https://usehooks-ts.com/react-hook/use-step) @@ -32,52 +33,58 @@ type SetStepCallbackType = (step: number | ((step: number) => number)) => void * // Access and use the current step and provided helper functions. * ``` */ -export function useStep(maxStep: number): [number, UseStepActions] { - const [currentStep, setCurrentStep] = useState(1) +export function useStep( + maxStep: number, + initialStep = 1, +): [number, UseStepActions] { + if (!Number.isInteger(initialStep)) { + throw new Error('initialStep must be an integer'); + } + const [currentStep, setCurrentStep] = useState(initialStep > 0 && initialStep <= maxStep ? initialStep : 1); - const canGoToNextStep = currentStep + 1 <= maxStep - const canGoToPrevStep = currentStep - 1 > 0 + const canGoToNextStep = currentStep + 1 <= maxStep; + const canGoToPrevStep = currentStep - 1 > 0; - const setStep = useCallback( - step => { - // Allow value to be a function so we have the same API as useState - const newStep = step instanceof Function ? step(currentStep) : step + const setStep = useCallback( + (step) => { + // Allow value to be a function so we have the same API as useState + const newStep = step instanceof Function ? step(currentStep) : step; - if (newStep >= 1 && newStep <= maxStep) { - setCurrentStep(newStep) - return - } + if (newStep >= 1 && newStep <= maxStep) { + setCurrentStep(newStep); + return; + } - throw new Error('Step not valid') - }, - [maxStep, currentStep], - ) + throw new Error("Step not valid"); + }, + [maxStep, currentStep], + ); - const goToNextStep = useCallback(() => { - if (canGoToNextStep) { - setCurrentStep(step => step + 1) - } - }, [canGoToNextStep]) + const goToNextStep = useCallback(() => { + if (canGoToNextStep) { + setCurrentStep((step) => step + 1); + } + }, [canGoToNextStep]); - const goToPrevStep = useCallback(() => { - if (canGoToPrevStep) { - setCurrentStep(step => step - 1) - } - }, [canGoToPrevStep]) + const goToPrevStep = useCallback(() => { + if (canGoToPrevStep) { + setCurrentStep((step) => step - 1); + } + }, [canGoToPrevStep]); - const reset = useCallback(() => { - setCurrentStep(1) - }, []) + const reset = useCallback(() => { + setCurrentStep(1); + }, []); - return [ - currentStep, - { - goToNextStep, - goToPrevStep, - canGoToNextStep, - canGoToPrevStep, - setStep, - reset, - }, - ] + return [ + currentStep, + { + goToNextStep, + goToPrevStep, + canGoToNextStep, + canGoToPrevStep, + setStep, + reset, + }, + ]; }