diff --git a/.changeset/plenty-mangos-sniff.md b/.changeset/plenty-mangos-sniff.md new file mode 100644 index 00000000000..e2d9b1f5904 --- /dev/null +++ b/.changeset/plenty-mangos-sniff.md @@ -0,0 +1,7 @@ +--- +'@primer/react': patch +--- + +Adding aria-attributes and role to the ProgressBar component + + diff --git a/docs/content/ProgressBar.mdx b/docs/content/ProgressBar.mdx index 888cccf8d15..4e3eb5c7246 100644 --- a/docs/content/ProgressBar.mdx +++ b/docs/content/ProgressBar.mdx @@ -14,7 +14,7 @@ import {ProgressBar} from '@primer/react' ## Examples ```jsx live - + ``` ### Inline @@ -24,7 +24,7 @@ If you'd like to use ProgressBar inline, pass the `inline` boolean prop & **be s ```jsx live <> 5 of 10 - + ``` @@ -34,7 +34,7 @@ If you want to show multiple segments in a ProgressBar, pass separate `Item`s as ```jsx live <> - + diff --git a/src/ProgressBar/ProgressBar.features.stories.tsx b/src/ProgressBar/ProgressBar.features.stories.tsx index de3b28d9735..5c1e860a26a 100644 --- a/src/ProgressBar/ProgressBar.features.stories.tsx +++ b/src/ProgressBar/ProgressBar.features.stories.tsx @@ -1,25 +1,25 @@ import React from 'react' -import {ComponentMeta} from '@storybook/react' +import {Meta} from '@storybook/react' import {ProgressBar} from '..' export default { title: 'Components/ProgressBar/Features', component: ProgressBar, -} as ComponentMeta +} as Meta -export const ProgressZero = () => -export const ProgressHalf = () => -export const ProgressDone = () => +export const ProgressZero = () => +export const ProgressHalf = () => +export const ProgressDone = () => -export const SizeSmall = () => -export const SizeLarge = () => +export const SizeSmall = () => +export const SizeLarge = () => -export const Inline = () => +export const Inline = () => -export const Color = () => +export const Color = () => export const MultipleItems = () => ( - + diff --git a/src/ProgressBar/ProgressBar.stories.tsx b/src/ProgressBar/ProgressBar.stories.tsx index 36628e8a777..fc4ee9e07a5 100644 --- a/src/ProgressBar/ProgressBar.stories.tsx +++ b/src/ProgressBar/ProgressBar.stories.tsx @@ -7,10 +7,10 @@ export default { component: ProgressBar, } as Meta -export const Default = () => +export const Default = () => export const Playground: ComponentStory = args => ( - + ) Playground.args = { diff --git a/src/ProgressBar/ProgressBar.tsx b/src/ProgressBar/ProgressBar.tsx index 88c8dbbf500..bc7fb26a5be 100644 --- a/src/ProgressBar/ProgressBar.tsx +++ b/src/ProgressBar/ProgressBar.tsx @@ -1,8 +1,9 @@ -import React from 'react' +import React, {forwardRef} from 'react' import styled from 'styled-components' import {width, WidthProps} from 'styled-system' import {get} from '../constants' import sx, {SxProp} from '../sx' +import {warning} from '../utils/warning' type ProgressProp = {progress?: string | number} @@ -37,22 +38,37 @@ const ProgressContainer = styled.span` ${sx}; ` -export type ProgressBarProps = React.PropsWithChildren & {bg?: string} & StyledProgressContainerProps & ProgressProp - -export const ProgressBar = ({ - progress, - bg = 'success.emphasis', - barSize = 'default', - children, - ...rest -}: ProgressBarProps) => { - if (children && progress) { - throw new Error('You should pass `progress` or children, not both.') - } - - return ( - - {children ?? } - - ) -} +export type ProgressBarProps = React.HTMLAttributes & {bg?: string} & StyledProgressContainerProps & + ProgressProp + +export const ProgressBar = forwardRef( + ({progress, bg = 'success.emphasis', barSize = 'default', children, ...rest}: ProgressBarProps, forwardRef) => { + if (children && progress) { + throw new Error('You should pass `progress` or children, not both.') + } + + warning( + children && + typeof (rest as React.AriaAttributes)['aria-valuenow'] === 'undefined' && + typeof (rest as React.AriaAttributes)['aria-valuetext'] === 'undefined', + 'Expected `aria-valuenow` or `aria-valuetext` to be provided to . Provide one of these values so screen reader users can determine the current progress. This warning will become an error in the next major release.', + ) + + const progressAsNumber = typeof progress === 'string' ? parseInt(progress, 10) : progress + + const ariaAttributes = { + 'aria-valuenow': progressAsNumber ? Math.round(progressAsNumber) : undefined, + 'aria-valuemin': 0, + 'aria-valuemax': 100, + 'aria-busy': progressAsNumber ? progressAsNumber !== 100 : false, + } + + return ( + + {children ?? } + + ) + }, +) + +ProgressBar.displayName = 'ProgressBar' diff --git a/src/__tests__/ProgressBar.test.tsx b/src/__tests__/ProgressBar.test.tsx index 19735c739e6..4170d0db8f4 100644 --- a/src/__tests__/ProgressBar.test.tsx +++ b/src/__tests__/ProgressBar.test.tsx @@ -15,26 +15,41 @@ describe('ProgressBar', () => { }) it('should have no axe violations', async () => { - const {container} = HTMLRender() + const {container} = HTMLRender() const results = await axe(container) expect(results).toHaveNoViolations() }) it('respects the "barSize" prop', () => { - expect(render()).toHaveStyleRule('height', '5px') - expect(render()).toHaveStyleRule('height', '8px') - expect(render()).toHaveStyleRule('height', '10px') + expect(render()).toHaveStyleRule( + 'height', + '5px', + ) + expect(render()).toHaveStyleRule( + 'height', + '8px', + ) + expect(render()).toHaveStyleRule( + 'height', + '10px', + ) }) it('respects the "inline" prop', () => { - expect(render()).toHaveStyleRule('display', 'inline-flex') + expect(render()).toHaveStyleRule( + 'display', + 'inline-flex', + ) }) it('respects the "width" prop', () => { - expect(render()).toHaveStyleRule('width', '100px') + expect(render()).toHaveStyleRule( + 'width', + '100px', + ) }) it('respects the "progress" prop', () => { - expect(render()).toMatchSnapshot() + expect(render()).toMatchSnapshot() }) }) diff --git a/src/__tests__/__snapshots__/ProgressBar.test.tsx.snap b/src/__tests__/__snapshots__/ProgressBar.test.tsx.snap index 7050d8d3b7d..dad58b54e9e 100644 --- a/src/__tests__/__snapshots__/ProgressBar.test.tsx.snap +++ b/src/__tests__/__snapshots__/ProgressBar.test.tsx.snap @@ -19,7 +19,11 @@ exports[`ProgressBar renders consistently 1`] = ` }