Skip to content

Commit

Permalink
feat(progress-tracker): add base markup for progress-tracker
Browse files Browse the repository at this point in the history
  • Loading branch information
soykje committed Dec 4, 2023
1 parent 799fd9b commit d98c374
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 10 deletions.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 16 additions & 4 deletions packages/components/progress-tracker/src/ProgressTracker.doc.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Meta, Canvas } from '@storybook/addon-docs'
import { ArgTypes } from '@storybook/blocks';
import { ArgTypes } from '@docs/helpers/ArgTypes'

import { ProgressTracker } from '.'

Expand All @@ -20,12 +20,24 @@ npm install @spark-ui/progress-tracker
## Import

```tsx
import { ProgressTracker } from "@spark-ui/progress-tracker"
import { ProgressTracker } from '@spark-ui/progress-tracker'
```

## Props

<ArgTypes of={ProgressTracker} />
<ArgTypes
of={ProgressTracker}
description="Contains all the progress tracker component parts."
subcomponents={{
'ProgressTracker.Step': {
of: ProgressTracker.Step,
description: 'Contains the button associated to a step',
},
}}
/>

## Usage

### Default

## Variants
<Canvas of={stories.Default} />
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@ const meta: Meta<typeof ProgressTracker> = {

export default meta

export const Default: StoryFn = _args => <ProgressTracker>Hello World!</ProgressTracker>
export const Default: StoryFn = _args => (
<ProgressTracker stepIndex={1} onStepClick={id => console.log('Clicked on', id)}>
<ProgressTracker.Step label="Etape 1" />
<ProgressTracker.Step label="Etape 2" />
<ProgressTracker.Step label="Etape 3" />
</ProgressTracker>
)
50 changes: 46 additions & 4 deletions packages/components/progress-tracker/src/ProgressTracker.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,51 @@
import { ComponentPropsWithoutRef, forwardRef, PropsWithChildren } from 'react'
import { type ComponentPropsWithoutRef, forwardRef, type PropsWithChildren, useState } from 'react'

export type ProgressTrackerProps = ComponentPropsWithoutRef<'div'>
import { ProgressTrackerContext } from './ProgressTrackerContext'

export interface ProgressTrackerProps extends ComponentPropsWithoutRef<'div'> {
/**
* The index of the current step.
* @default 0
*/
stepIndex?: number
/**
* Event handler called when clicking on a step.
*/
onStepClick?: (stepId: string) => void
/**
* Sets the component as interactive or not.
* @default false
*/
readOnly?: boolean
}

export const ProgressTracker = forwardRef<HTMLDivElement, PropsWithChildren<ProgressTrackerProps>>(
(props, ref) => {
return <div ref={ref} {...props} />
(
{
stepIndex: propStepIndex = 0,
onStepClick: onStepClickProp,
readOnly = false,
children,
...rest
},
ref
) => {
const [steps, setSteps] = useState<Set<string>>(new Set())
const [stepIndex, setStepIndex] = useState<number>(propStepIndex)

const onStepClick = (stepId: string) => {
setStepIndex([...steps].indexOf(stepId))
onStepClickProp?.(stepId)

Check warning on line 38 in packages/components/progress-tracker/src/ProgressTracker.tsx

View check run for this annotation

Codecov / codecov/patch

packages/components/progress-tracker/src/ProgressTracker.tsx#L37-L38

Added lines #L37 - L38 were not covered by tests
}

const Component = readOnly ? 'div' : 'nav'

return (
<ProgressTrackerContext.Provider value={{ stepIndex, onStepClick, steps, setSteps }}>
<Component ref={ref} {...rest}>
<ol>{children}</ol>
</Component>
</ProgressTrackerContext.Provider>
)
}
)
16 changes: 16 additions & 0 deletions packages/components/progress-tracker/src/ProgressTrackerContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createContext, type Dispatch, type SetStateAction, useContext } from 'react'

import type { ProgressTrackerProps } from './ProgressTracker'

export type ProgressTrackerContextInterface = Required<
Pick<ProgressTrackerProps, 'stepIndex' | 'onStepClick'>
> & {
steps: Set<string>
setSteps: Dispatch<SetStateAction<Set<string>>>
}

export const ProgressTrackerContext = createContext<ProgressTrackerContextInterface>(
{} as ProgressTrackerContextInterface
)

export const useProgressTrackerContext = () => useContext(ProgressTrackerContext)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { cva, type VariantProps } from 'class-variance-authority'

export const progressTrackerStepVariants = cva(['inline-flex', 'bg-main'], {
variants: {
active: {
true: 'bg-success',
false: '',
},
disabled: {
true: 'bg-info',
false: '',
},
},
defaultVariants: {
active: false,
disabled: false,
},
})

export type ProgressTrackerStepVariantsProps = VariantProps<typeof progressTrackerStepVariants>
39 changes: 39 additions & 0 deletions packages/components/progress-tracker/src/ProgressTrackerStep.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { type ComponentPropsWithoutRef, forwardRef, useEffect, useId, useState } from 'react'

import { useProgressTrackerContext } from './ProgressTrackerContext'
import { progressTrackerStepVariants } from './ProgressTrackerStep.styles'

export type ProgressTrackerStepProps = ComponentPropsWithoutRef<'li'> & {
label?: string
}

export const ProgressTrackerStep = forwardRef<HTMLLIElement, ProgressTrackerStepProps>(
({ label, className, ...rest }, ref) => {
const { stepIndex: activeStepIndex, steps, onStepClick, setSteps } = useProgressTrackerContext()

const ID = useId()
const stepId = `step-${ID}`

const [isActive, setIsActive] = useState(false)

useEffect(() => setSteps(steps => steps.add(stepId)), [])

Check warning on line 19 in packages/components/progress-tracker/src/ProgressTrackerStep.tsx

View workflow job for this annotation

GitHub Actions / Lint

React Hook useEffect has missing dependencies: 'setSteps' and 'stepId'. Either include them or remove the dependency array

useEffect(
() => setIsActive([...steps].indexOf(stepId) === activeStepIndex),
[activeStepIndex, steps, stepId]
)

return (
<li
id={stepId}
ref={ref}
className={progressTrackerStepVariants({ active: isActive, className })}
{...rest}
>
<button type="button" onClick={() => onStepClick(stepId)}>
<span>{label}</span>
</button>
</li>
)
}
)
16 changes: 15 additions & 1 deletion packages/components/progress-tracker/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
export { ProgressTracker } from './ProgressTracker'
import type { FC } from 'react'

import { ProgressTracker as Root, type ProgressTrackerProps } from './ProgressTracker'
import { ProgressTrackerStep as Step, type ProgressTrackerStepProps } from './ProgressTrackerStep'

export const ProgressTracker: FC<ProgressTrackerProps> & {
Step: typeof Step
} = Object.assign(Root, {
Step,
})

ProgressTracker.displayName = 'ProgressTracker'
Step.displayName = 'ProgressTracker.Step'

export type { ProgressTrackerProps, ProgressTrackerStepProps }

0 comments on commit d98c374

Please sign in to comment.