Skip to content

Commit

Permalink
feat(progress-tracker): add orientation prop
Browse files Browse the repository at this point in the history
  • Loading branch information
soykje committed Dec 7, 2023
1 parent 4d23fb2 commit 898b30e
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 42 deletions.
10 changes: 6 additions & 4 deletions packages/components/progress-tracker/src/ProgressTracker.doc.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ Use `size` prop to set the size of the progress indicators. Smallest size doesn'

<Canvas of={stories.Size} />

### Orientation

Use `orientation` prop to set `horizontal|vertical` orientation.

<Canvas of={stories.Orientation} />

### Intent

Use `intent` prop to set the color.
Expand All @@ -69,10 +75,6 @@ Use `intent` prop to set the color.

Use `design` prop to set the look and feel.

### Orientation

Use `orientation` prop to set `horizontal|vertical` orientation.

## Accessibility

### Keyboard Interactions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const meta: Meta<typeof ProgressTracker> = {
export default meta

const sizes: ProgressTrackerProps['size'][] = ['sm', 'md', 'lg']
const orientations: ProgressTrackerProps['orientation'][] = ['horizontal', 'vertical']

export const Default: StoryFn = _args => (
<ProgressTracker stepIndex={1} onStepClick={id => console.log('Clicked on', id)}>
Expand Down Expand Up @@ -50,3 +51,27 @@ export const Size: StoryFn = _args => (
))}
</div>
)

export const Orientation: StoryFn = _args => (
<div className="flex flex-wrap gap-lg">
{orientations.map(orientation => (
<div key={orientation}>
<StoryLabel>{`${orientation}${
orientation === 'horizontal' ? ' (default)' : ''
}`}</StoryLabel>
<ProgressTracker
stepIndex={1}
orientation={orientation as ProgressTrackerProps['orientation']}
{...(orientation === 'vertical' && {
className: 'w-[120px]',
})}
>
<ProgressTracker.Step label="Build" />
<ProgressTracker.Step label="Deploy with confidence" disabled />
<ProgressTracker.Step label="Iterate again and again" disabled />
<ProgressTracker.Step label="Repeat" />
</ProgressTracker>
</div>
))}
</div>
)
13 changes: 13 additions & 0 deletions packages/components/progress-tracker/src/ProgressTracker.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { cva } from 'class-variance-authority'

export const progressListVariant = cva(['flex flex-nowrap'], {
variants: {
orientation: {
horizontal: 'flex-row w-full',
vertical: 'flex-col',
},
},
defaultVariants: {
orientation: 'horizontal',
},
})
8 changes: 5 additions & 3 deletions packages/components/progress-tracker/src/ProgressTracker.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { cx } from 'class-variance-authority'
import { type ComponentPropsWithoutRef, forwardRef, type PropsWithChildren, useState } from 'react'

import { progressListVariant } from './ProgressTracker.styles'
import {
ProgressTrackerContext,
type ProgressTrackerContextInterface,
Expand All @@ -9,7 +10,7 @@ import type { ProgressTrackerStepVariantsProps } from './ProgressTrackerStep.sty

export interface ProgressTrackerProps
extends ComponentPropsWithoutRef<'div'>,
Pick<ProgressTrackerStepVariantsProps, 'size'> {
Pick<ProgressTrackerStepVariantsProps, 'size' | 'orientation'> {
/**
* The index of the current step.
* @default 0
Expand All @@ -33,6 +34,7 @@ export const ProgressTracker = forwardRef<HTMLDivElement, PropsWithChildren<Prog
onStepClick: onStepClickProp,
readOnly = false,
size = 'lg',
orientation = 'horizontal',
children,
className,
...rest
Expand All @@ -53,15 +55,15 @@ export const ProgressTracker = forwardRef<HTMLDivElement, PropsWithChildren<Prog

return (
<ProgressTrackerContext.Provider
value={{ stepIndex, onStepClick, steps, setSteps, size, readOnly }}
value={{ stepIndex, onStepClick, steps, setSteps, size, orientation, readOnly }}
>
<Component
ref={ref}
aria-label="progress"
className={cx('inline-flex', className)}
{...rest}
>
<ol className="flex w-full flex-nowrap" style={{ counterReset: 'step' }}>
<ol className={progressListVariant({ orientation })} style={{ counterReset: 'step' }}>
{children}
</ol>
</Component>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface Step {
}

export type ProgressTrackerContextInterface = Required<
Pick<ProgressTrackerProps, 'stepIndex' | 'onStepClick' | 'size' | 'readOnly'>
Pick<ProgressTrackerProps, 'stepIndex' | 'onStepClick' | 'size' | 'orientation' | 'readOnly'>
> & {
steps: Set<Step>
setSteps: Dispatch<SetStateAction<Set<Step>>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,93 @@ import { cva, type VariantProps } from 'class-variance-authority'

export const stepItemVariant = cva(
[
'relative inline-flex flex-auto first:grow-0 justify-center',
'relative inline-flex flex-auto first:grow-0 justify-center group',
// Progress Track
'before:absolute after:absolute before:z-base after:z-base',
'before:h-[1px] after:h-[1px]',
'before:bg-basic after:bg-basic',
'before:left-none after:right-none',
'first:before:content-none last:after:content-none',
'after:absolute after:z-base',
'after:bg-basic',
'last:after:content-none',
],
{
variants: {
size: {
sm: [
'pt-[calc(16px+theme("spacing.md"))]',
sm: '',
md: '',
lg: '',
},
disabled: {
true: 'before:opacity-dim-3',
false: '',
},
disabledAfter: {
true: 'after:opacity-dim-3',
false: '',
},
orientation: {
vertical: ['after:w-[1px]', 'after:bottom-none'],
horizontal: [
'before:h-[1px] after:h-[1px]',
'before:bg-basic before:absolute before:z-base',
'before:left-none after:right-none',
'first:before:content-none',
],
},
},
compoundVariants: [
{
size: 'sm',
orientation: 'horizontal',
class: [
'before:top-[8px] after:top-[8px]',
'before:right-[calc(50%+12px)] after:left-[calc(50%+12px)]',
],
md: [
'pt-[calc(24px+theme("spacing.md"))]',
},
{
size: 'md',
orientation: 'horizontal',
class: [
'before:top-[12px] after:top-[12px]',
'before:right-[calc(50%+16px)] after:left-[calc(50%+16px)]',
],
lg: [
'pt-[calc(32px+theme("spacing.md"))]',
},
{
size: 'lg',
orientation: 'horizontal',
class: [
'before:top-[16px] after:top-[16px]',
'before:right-[calc(50%+20px)] after:left-[calc(50%+20px)]',
],
},
disabled: {
true: 'before:opacity-dim-3 after:opacity-dim-3',
false: '',
{
size: 'sm',
orientation: 'vertical',
class: ['after:left-[8px]', 'after:top-[24px]'],
},
disabledBefore: {
true: 'before:opacity-dim-3',
false: '',
{
size: 'md',
orientation: 'vertical',
class: ['after:left-[12px]', 'after:top-[32px]'],
},
disabledAfter: {
true: 'after:opacity-dim-3',
false: '',
{
size: 'lg',
orientation: 'vertical',
class: ['after:left-[16px]', 'after:top-[40px]'],
},
},
],
defaultVariants: {
disabled: false,
disabledBefore: false,
disabledAfter: false,
size: 'lg',
orientation: 'horizontal',
},
}
)

// eslint-disable-next-line tailwindcss/no-custom-classname
export const stepButtonVariant = cva(
[
'block mx-sm mb-auto',
'border-x-sm border-x-transparent',
'flex gap-md',
// Progress Indicator
'before:[counter-increment:step] before:content-[counter(step)]',
'before:absolute before:z-raised before:top-none before:left-[50%] before:translate-x-[-50%]',
'before:flex before:items-center before:justify-center before:rounded-full',
'before:flex before:shrink-0 before:items-center before:justify-center before:rounded-full before:mx-auto',
'before:border-sm before:text-on-basic-container',
'before:text-body-2 before:font-bold',
],
Expand Down Expand Up @@ -89,6 +118,19 @@ export const stepButtonVariant = cva(
true: ['cursor-default'],
false: '',
},
orientation: {
vertical: [
'flex-row items-baseline',
'my-sm mr-auto group-first:mt-none group-last:mb-none',
'border-y-sm border-y-transparent',
'text-left',
],
horizontal: [
'flex-col items-center',
'mx-sm mb-auto group-first:ml-none group-last:mr-none',
'border-x-sm border-x-transparent',
],
},
},
compoundVariants: [
{
Expand All @@ -105,6 +147,7 @@ export const stepButtonVariant = cva(
active: false,
complete: false,
size: 'lg',
orientation: 'horizontal',
},
}
)
Expand Down
20 changes: 14 additions & 6 deletions packages/components/progress-tracker/src/ProgressTrackerStep.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { type ComponentPropsWithoutRef, forwardRef, useEffect, useId, useState } from 'react'
import {
type ComponentPropsWithoutRef,
forwardRef,
type ReactNode,
useEffect,
useId,
useState,
} from 'react'

import { useProgressTrackerContext } from './ProgressTrackerContext'
import { stepButtonVariant, stepItemVariant } from './ProgressTrackerStep.styles'

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

Expand All @@ -16,6 +23,7 @@ export const ProgressTrackerStep = forwardRef<HTMLLIElement, ProgressTrackerStep
onStepClick,
setSteps,
size,
orientation,
readOnly,
} = useProgressTrackerContext()

Expand Down Expand Up @@ -48,15 +56,14 @@ export const ProgressTrackerStep = forwardRef<HTMLLIElement, ProgressTrackerStep
aria-current={progressState === 'active'}
className={stepItemVariant({
size,
orientation,
disabled,
disabledBefore: !![...steps][stepIndex - 1]?.disabled,
disabledAfter: !![...steps][stepIndex + 1]?.disabled,
disabledAfter: !![...steps][stepIndex + 1]?.disabled, // should reload on disabled updates?
})}
{...rest}
>
<button
type="button"
// {...(!disabled || !readonly) && ({})}
{...(!disabled &&
!readOnly && {
onClick: () => onStepClick(stepId),
Expand All @@ -68,10 +75,11 @@ export const ProgressTrackerStep = forwardRef<HTMLLIElement, ProgressTrackerStep
disabled,
readOnly,
size,
orientation,
className,
})}
>
{label && <span className="block text-body-2 font-bold text-on-surface">{label}</span>}
{label && <div className="block text-body-2 font-bold text-on-surface">{label}</div>}
</button>
</li>
)
Expand Down
21 changes: 21 additions & 0 deletions packages/components/progress-tracker/src/variants/disabled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export const disabledItemVariants = {
disabled: {
true: 'before:opacity-dim-3 after:opacity-dim-3',
false: '',
},
disabledBefore: {
true: 'before:opacity-dim-3',
false: '',
},
disabledAfter: {
true: 'after:opacity-dim-3',
false: '',
},
}

export const disabledButtonVariants = {
disabled: {
true: 'before:opacity-dim-3',
false: '',
},
}
2 changes: 2 additions & 0 deletions packages/components/progress-tracker/src/variants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './size'
export * from './disabled'
34 changes: 34 additions & 0 deletions packages/components/progress-tracker/src/variants/size.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export const sizeItemVariants = [
{
size: 'sm',
orientation: ['horizontal', 'vertical'],
class: [
'before:top-[8px] after:top-[8px]',
'before:right-[calc(50%+12px)] after:left-[calc(50%+12px)]',
],
},
{
size: 'md',
orientation: ['horizontal', 'vertical'],
class: [
'before:top-[12px] after:top-[12px]',
'before:right-[calc(50%+16px)] after:left-[calc(50%+16px)]',
],
},
{
size: 'lg',
orientation: ['horizontal', 'vertical'],
class: [
'before:top-[16px] after:top-[16px]',
'before:right-[calc(50%+20px)] after:left-[calc(50%+20px)]',
],
},
] as const

export const sizeButtonVariants = {
size: {
sm: ['before:w-sz-16 before:h-sz-16', 'before:!content-["_"]'],
md: 'before:w-sz-24 before:h-sz-24',
lg: 'before:w-sz-32 before:h-sz-32',
},
}

0 comments on commit 898b30e

Please sign in to comment.