Skip to content

Commit

Permalink
fix(LinearProgress): set correct required props
Browse files Browse the repository at this point in the history
  • Loading branch information
filiptammergard committed Apr 5, 2023
1 parent 575c4de commit 29c1455
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 33 deletions.
5 changes: 5 additions & 0 deletions .changeset/blue-planes-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@einride/ui": patch
---

LinearProgress: Set correct required props.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
Meta,
Primary,
Description,
Source,
Title,
ArgTypes,
Subtitle,
Controls,
Story,
Canvas,
Stories,
Markdown,
} from "@storybook/blocks"
import * as LinearProgress from "./LinearProgress.stories.tsx"

<Meta of={LinearProgress} />

<Title />

<Description of={LinearProgress} />

```tsx
import { LinearProgress } from "@einride/ui"
```

## Basic

<Description of={LinearProgress.Basic} />

<Canvas of={LinearProgress.Basic} />

<Controls of={LinearProgress.Basic} />

## Custom range

<Description of={LinearProgress.CustomRange} />

<Canvas of={LinearProgress.CustomRange} />

<Controls of={LinearProgress.CustomRange} include={["min", "max", "value"]} />

## Range color

<Description of={LinearProgress.CustomColor} />

<Canvas of={LinearProgress.CustomColor} />

<Controls of={LinearProgress.CustomColor} include="color" />

## Accessibility

Adding a descriptive `aria-label` is required to convey what the progress is displaynig.
Original file line number Diff line number Diff line change
@@ -1,25 +1,75 @@
import { expect } from "@storybook/jest"
import { Meta, StoryObj } from "@storybook/react"
import { within } from "@storybook/testing-library"
import { SnapshotWrapper } from "../../../lib/storybook/SnapshotWrapper"
import { LinearProgress } from "./LinearProgress"
import { contentColors } from "../../../lib/theme/types"
import { DEFAULT_MAX, DEFAULT_MIN, LinearProgress } from "./LinearProgress"

const meta = {
component: LinearProgress,
argTypes: {
color: {
control: {
type: "select",
},
options: contentColors,
},
},
} satisfies Meta<typeof LinearProgress>

export default meta
type Story = StoryObj<typeof meta>

export const Default = {
export const Basic = {
args: {
"aria-label": "Progress",
value: 50,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const gauge = canvas.getByRole("progressbar", { name: Basic.args["aria-label"] })
await expect(gauge).toHaveAttribute("aria-valuemin", DEFAULT_MIN.toString())
await expect(gauge).toHaveAttribute("aria-valuemax", DEFAULT_MAX.toString())
await expect(gauge).toHaveAttribute("aria-valuenow", Basic.args.value.toString())
},
} satisfies Story

/** If you have a custom value range, you can set that with the `min` and `max` props, and `value` will take those into account. In this example, `value: 110` is 75% of the range between 80 and 110, which is also conveyed by the progress. */
export const CustomRange = {
args: {
...Basic.args,
max: 120,
min: 80,
value: 110,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const gauge = canvas.getByRole("progressbar", { name: CustomRange.args["aria-label"] })
await expect(gauge).toHaveAttribute("aria-valuemin", CustomRange.args.min.toString())
await expect(gauge).toHaveAttribute("aria-valuemax", CustomRange.args.max.toString())
await expect(gauge).toHaveAttribute("aria-valuenow", CustomRange.args.value.toString())
},
} satisfies Story

/** Change the color of the progress with the `color` prop. */
export const CustomColor = {
args: {
...Basic.args,
color: "negative",
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const gauge = canvas.getByRole("progressbar", { name: CustomColor.args["aria-label"] })
await expect(gauge).toHaveAttribute("aria-valuemin", DEFAULT_MIN.toString())
await expect(gauge).toHaveAttribute("aria-valuemax", DEFAULT_MAX.toString())
await expect(gauge).toHaveAttribute("aria-valuenow", CustomColor.args.value.toString())
},
} satisfies Story

export const Snapshot = {
render: () => (
<SnapshotWrapper>
{[Default].map((Story, index) => (
{[Basic, CustomRange, CustomColor].map((Story, index) => (
// eslint-disable-next-line react/no-array-index-key
<LinearProgress key={index} {...Story.args} />
))}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import styled from "@emotion/styled"
import { forwardRef, HTMLAttributes } from "react"
import { ContentColor } from "../../../lib/theme/types"
import { ComponentPropsWithoutRef, forwardRef } from "react"
import { getColor } from "../../../lib/theme/prop-system"
import { Color } from "../../../lib/theme/props"
import { Box } from "../../layout/Box/Box"

export interface LinearProgressProps extends Omit<ComponentPropsWithoutRef<"div">, "color"> {
/** Accessible name. Describes what information the progress is conveying. */
"aria-label": string

interface LinearProgressBaseProps extends HTMLAttributes<HTMLDivElement> {
/** Color of the completed progress line. Default is `positive`. */
color?: ContentColor
color?: Color

/** Maximum value. Default is `100`. */
max?: number
Expand All @@ -16,23 +21,16 @@ interface LinearProgressBaseProps extends HTMLAttributes<HTMLDivElement> {
value: number
}

export type LinearProgressProps = (
| {
/** Accessible name. */
"aria-label": string
}
| {
/** Accessible name. */
"aria-labelledby": string
}
) &
LinearProgressBaseProps

/** Either `aria-label` or `aria-labelledby` is required for accessibility. */
/** A linear progress that can be used for conveying progress. */
export const LinearProgress = forwardRef<HTMLDivElement, LinearProgressProps>(
({ color = "positive", max = 100, min = 0, value, ...props }, ref) => {
({ color = "positive", max = DEFAULT_MAX, min = DEFAULT_MIN, value, ...props }, ref) => {
return (
<Wrapper
<Box
background="tertiary"
blockSize={1}
borderRadius="sm"
position="relative"
inlineSize="100%"
role="progressbar"
aria-valuemax={max}
aria-valuemin={min}
Expand All @@ -41,29 +39,23 @@ export const LinearProgress = forwardRef<HTMLDivElement, LinearProgressProps>(
ref={ref}
>
<Value max={max} min={min} textColor={color} value={value} />
</Wrapper>
</Box>
)
},
)

const Wrapper = styled.div`
background: ${({ theme }) => theme.colors.background.tertiary};
block-size: ${({ theme }) => theme.spacingBase}rem;
border-radius: ${({ theme }) => theme.borderRadii.sm};
position: relative;
/* Needed to make sure component takes up full inline size in flex containers */
inline-size: 100%;
`
export const DEFAULT_MIN = 0
export const DEFAULT_MAX = 100

interface ValueProps {
max: number
min: number
textColor: ContentColor
textColor: Color
value: number
}

const Value = styled.div<ValueProps>`
background: ${({ textColor, theme }) => theme.colors.content[textColor]};
background: ${({ textColor, theme }) => getColor(textColor, theme)};
block-size: ${({ theme }) => theme.spacingBase}rem;
border-radius: ${({ theme }) => theme.borderRadii.sm};
inline-size: ${({ max, min, value }) => getInlineSize(max, min, value)}%;
Expand Down

0 comments on commit 29c1455

Please sign in to comment.