Skip to content

Commit

Permalink
feat: Update wizard logic (#401)
Browse files Browse the repository at this point in the history
  • Loading branch information
floreks authored Feb 2, 2023
1 parent 619bb3b commit ee857e8
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 56 deletions.
4 changes: 2 additions & 2 deletions src/components/wizard/Picker.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import styled from 'styled-components'

import {
createRef,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react'

Expand Down Expand Up @@ -61,7 +61,7 @@ function PickerUnstyled({ items, ...props }: PickerProps): JSX.Element {
const { onSelect, selected, selectedCount } = usePicker()
const [search, setSearch] = useState<string>(undefined)
const [scrollable, setScrollable] = useState(false)
const scrollRef = useRef<HTMLDivElement>(null)
const scrollRef = createRef<HTMLDivElement>()
const isScrollbarVisible = (el: HTMLDivElement) => el?.scrollHeight > el?.clientHeight
const filtered = useMemo(() => items.filter(item => (search ? item.label.toLowerCase().includes(search) : true)), [items, search])

Expand Down
22 changes: 13 additions & 9 deletions src/components/wizard/Wizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ type WizardProps = {
onClose?: MouseEventHandler<void>
onComplete?: (completed: boolean) => void
onSelect?: (selected: Array<StepConfig>) => void
onResetRef?: MutableRefObject<{onReset: Dispatch<void>}>
onResetRef?: MutableRefObject<{ onReset: Dispatch<void> }>
ref?: Ref<HTMLDivElement>
children?: {
stepper?: ReactElement,
Expand All @@ -111,17 +111,21 @@ function WizardUnstyled({
[steps, completed, onComplete])
useEffect(() => onSelect && onSelect(selected), [onSelect, selected])
useEffect(() => {
if (IsEmpty(dependencySteps)) {
if (steps.some(s => s.isDependency)) onReset()
setSteps(steps => {
if (IsEmpty(dependencySteps)) {
if (steps.some(s => s.isDependency)) onReset()

return
}
return steps
}

const arr = steps.filter(step => !step.isDependency)
const arr = steps.filter(step => !step.isDependency)

arr.splice(1, 0, ...dependencySteps)
setSteps(arr)
}, [dependencySteps])
arr.splice(1, 0, ...dependencySteps)

return arr
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dependencySteps?.length, onReset, setSteps])

return (
<div
Expand Down
9 changes: 7 additions & 2 deletions src/components/wizard/context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { Dispatch, ReactElement, createContext } from 'react'
import {
Dispatch,
ReactElement,
SetStateAction,
createContext,
} from 'react'

import createIcon from '../icons/createIcon'

Expand All @@ -8,7 +13,7 @@ import { Installer } from './Installer'

type ContextProps<T = unknown> = {
steps: Array<StepConfig<T>>
setSteps: Dispatch<Array<StepConfig<T>>>
setSteps: Dispatch<SetStateAction<Array<StepConfig<T>>>>
active: number
setActive: Dispatch<number>
completed: boolean
Expand Down
98 changes: 56 additions & 42 deletions src/components/wizard/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,60 @@ import { ContextProps, StepConfig, WizardContext } from './context'

const useActive = <T = unknown>() => {
const { steps, setSteps, active: activeIdx } = useContext<ContextProps<T>>(WizardContext)
const active: StepConfig<T> = useMemo<StepConfig<T>>(() => steps?.at(activeIdx), [activeIdx, steps])
const valid = useMemo(() => active.isDefault || active.isValid, [active, steps])
const active: StepConfig<T> = useMemo<StepConfig<T>>(() => steps.at(activeIdx), [activeIdx, steps])
const valid = useMemo(() => active.isDefault || active.isValid, [active])
const completed = useMemo(() => !active.isDefault && active.isCompleted, [active])

const setValid = useCallback((valid: boolean) => {
if (valid === active.isValid) {
return
}
setSteps(steps => {
const active = steps.at(activeIdx)

const updated = { ...active, isValid: valid } as StepConfig<T>
const arr = Array.from(steps)
if (valid === active.isValid) {
return steps
}

arr[activeIdx] = updated
const updated = { ...active, isValid: valid } as StepConfig<T>
const arr = Array.from(steps)

setSteps(arr)
}, [active, steps, activeIdx, setSteps])
arr[activeIdx] = updated

return arr
})
}, [activeIdx, setSteps])

const setCompleted = useCallback((completed: boolean) => {
if (completed === active.isCompleted) {
return
}
setSteps(steps => {
const active = steps.at(activeIdx)

if (completed === active.isCompleted) {
return steps
}

const updated = { ...active, isCompleted: completed } as StepConfig<T>
const arr = Array.from(steps)
const updated = { ...active, isCompleted: completed } as StepConfig<T>
const arr = Array.from(steps)

arr[activeIdx] = updated
arr[activeIdx] = updated

setSteps(arr)
}, [active, steps, activeIdx, setSteps])
return arr
})
}, [activeIdx, setSteps])

const setData = useCallback((data: T) => {
if (IsEmpty(data) || IsEqual(data, active.data)) {
return
}
setSteps(steps => {
const active = steps.at(activeIdx)

if (IsEmpty(data) || IsEqual(data, active.data)) {
return steps
}

const updated = { ...active, data } as StepConfig<T>
const arr = Array.from(steps)
const updated = { ...active, data } as StepConfig<T>
const arr = Array.from(steps)

arr[activeIdx] = updated
arr[activeIdx] = updated

setSteps(arr)
}, [active, steps, activeIdx, setSteps])
return arr
})
}, [activeIdx, setSteps])

return {
active, setValid, setData, setCompleted, valid, completed,
Expand Down Expand Up @@ -93,12 +105,10 @@ const useNavigation = () => {
}, [steps, active, setActive])

const onReset = useCallback(() => {
const defaultSteps = steps.filter(step => step.isDefault || step.isPlaceholder)

setActive(0)
setSteps(defaultSteps)
setCompleted(false)
}, [steps, setActive, setSteps, setCompleted])
setSteps(steps => steps.filter(step => step.isDefault || step.isPlaceholder))
}, [setActive, setSteps, setCompleted])

const onEdit = useCallback((step: StepConfig) => {
const idx = steps.findIndex(s => s.key === step.key)
Expand Down Expand Up @@ -132,20 +142,22 @@ const usePicker = () => {
const { steps, setSteps } = useContext(WizardContext)

const onSelect = useCallback((elem: StepConfig) => {
const idx = steps.findIndex(s => s.label === elem.label)
const isDependency = steps.at(idx)?.isDependency
const arr = Array.from(steps)
setSteps(steps => {
const idx = steps.findIndex(s => s.label === elem.label)
const isDependency = steps.at(idx)?.isDependency
const arr = Array.from(steps)

if (idx > -1) {
arr.splice(idx, 1)
}
if (idx > -1) {
arr.splice(idx, 1)
}

if (idx < 0 || (idx > -1 && isDependency)) {
arr.splice(-2, 0, elem)
}
if (idx < 0 || (idx > -1 && isDependency)) {
arr.splice(-2, 0, elem)
}

setSteps(arr)
}, [steps, setSteps])
return arr
})
}, [setSteps])

const selected = useMemo(() => steps.filter(step => !step.isDefault && !step.isPlaceholder && !step.isDependency), [steps])
const requiredLength = useMemo(() => steps.filter(step => step.isRequired).length, [steps])
Expand All @@ -170,10 +182,12 @@ const useStepper = <T = unknown>() => {
}

const useWizard = (initialSteps: Array<StepConfig> = [], limit = 10): ContextProps => {
const [steps, setSteps] = useState<Array<StepConfig>>(initialSteps)
const [steps, setSteps] = useState<Array<StepConfig>>(initialSteps ?? [])
const [active, setActive] = useState<number>(0)
const [completed, setCompleted] = useState<boolean>(false)

useEffect(() => setSteps(initialSteps), [initialSteps])

return {
steps,
setSteps,
Expand Down
2 changes: 1 addition & 1 deletion src/stories/Wizard.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ function StandaloneTemplate() {
return (
<Flex
width="100%"
height="1000px"
height="750px"
>
<Wizard
defaultSteps={DEFAULT_STEPS}
Expand Down

0 comments on commit ee857e8

Please sign in to comment.