Skip to content

Commit

Permalink
Merge 344cf29 into c909285
Browse files Browse the repository at this point in the history
  • Loading branch information
TylerJDev authored Sep 24, 2024
2 parents c909285 + 344cf29 commit b67413f
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 25 deletions.
77 changes: 58 additions & 19 deletions packages/react/src/ProgressBar/ProgressBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const shimmer = keyframes`
to { mask-position: 0%; }
`

export const Item = styled.span<ProgressProp & SxProp>`
const ProgressItem = styled.span<ProgressProp & SxProp>`
width: ${props => (props.progress ? `${props.progress}%` : 0)};
background-color: ${props => get(`colors.${props.bg || 'success.emphasis'}`)};
Expand All @@ -34,8 +34,6 @@ export const Item = styled.span<ProgressProp & SxProp>`
${sx};
`

Item.displayName = 'ProgressBar.Item'

const sizeMap = {
small: '5px',
large: '10px',
Expand All @@ -60,36 +58,77 @@ const ProgressContainer = styled.span<StyledProgressContainerProps>`
${sx};
`

export type ProgressBarProps = React.HTMLAttributes<HTMLSpanElement> & {bg?: string} & StyledProgressContainerProps &
ProgressProp
export type ProgressBarItems = React.HTMLAttributes<HTMLSpanElement> & {'aria-label'?: string} & ProgressProp & SxProp

export const ProgressBar = forwardRef<HTMLSpanElement, ProgressBarProps>(
export const Item = forwardRef<HTMLSpanElement, ProgressBarItems>(
(
{animated, progress, bg = 'success.emphasis', barSize = 'default', children, ...rest}: ProgressBarProps,
{progress, 'aria-label': ariaLabel, 'aria-valuenow': ariaValueNow, 'aria-valuetext': ariaValueText, ...rest},
forwardRef,
) => {
if (children && progress) {
throw new Error('You should pass `progress` or children, not both.')
const progressAsNumber = typeof progress === 'string' ? parseInt(progress, 10) : progress

const ariaAttributes = {
'aria-valuenow':
ariaValueNow || (progressAsNumber && progressAsNumber >= 0 ? Math.round(progressAsNumber) : undefined),
'aria-valuemin': 0,
'aria-valuemax': 100,
'aria-valuetext': ariaValueText,
}

warning(
children &&
typeof (rest as React.AriaAttributes)['aria-valuenow'] === 'undefined' &&
typeof (rest as React.AriaAttributes)['aria-valuetext'] === 'undefined',
ariaAttributes['aria-valuenow'] === undefined && ariaAttributes['aria-valuetext'] === undefined,
'Expected `aria-valuenow` or `aria-valuetext` to be provided to <ProgressBar>. 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
return (
<ProgressItem
role="progressbar"
aria-label={ariaLabel}
ref={forwardRef}
progress={progress}
{...ariaAttributes}
{...rest}
/>
)
},
)

const ariaAttributes = {
'aria-valuenow': progressAsNumber ? Math.round(progressAsNumber) : undefined,
'aria-valuemin': 0,
'aria-valuemax': 100,
Item.displayName = 'ProgressBar.Item'

export type ProgressBarProps = React.HTMLAttributes<HTMLSpanElement> & {bg?: string} & StyledProgressContainerProps &
ProgressProp

export const ProgressBar = forwardRef<HTMLSpanElement, ProgressBarProps>(
(
{
animated,
progress,
bg = 'success.emphasis',
barSize = 'default',
children,
'aria-label': ariaLabel,
'aria-valuenow': ariaValueNow,
'aria-valuetext': ariaValueText,
...rest
}: ProgressBarProps,
forwardRef,
) => {
if (children && progress) {
throw new Error('You should pass `progress` or children, not both.')
}

return (
<ProgressContainer ref={forwardRef} role="progressbar" barSize={barSize} {...ariaAttributes} {...rest}>
{children ?? <Item data-animated={animated} progress={progress} bg={bg} />}
<ProgressContainer ref={forwardRef} barSize={barSize} {...rest}>
{children ?? (
<Item
data-animated={animated}
progress={progress}
aria-label={ariaLabel}
aria-valuenow={ariaValueNow}
aria-valuetext={ariaValueText}
bg={bg}
/>
)}
</ProgressContainer>
)
},
Expand Down
51 changes: 50 additions & 1 deletion packages/react/src/__tests__/ProgressBar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {render as HTMLRender} from '@testing-library/react'
import axe from 'axe-core'

describe('ProgressBar', () => {
behavesAsComponent({Component: ProgressBar})
behavesAsComponent({Component: ProgressBar, toRender: () => <ProgressBar aria-valuenow={10} progress={0} />})

checkExports('ProgressBar', {
default: undefined,
Expand Down Expand Up @@ -50,4 +50,53 @@ describe('ProgressBar', () => {
it('respects the "progress" prop', () => {
expect(render(<ProgressBar progress={80} aria-label="Upload test.png" />)).toMatchSnapshot()
})

it('passed the `aria-label` down to the progress bar', () => {
const {getByRole, getByLabelText} = HTMLRender(<ProgressBar progress={80} aria-label="Upload test.png" />)
expect(getByRole('progressbar')).toHaveAttribute('aria-label', 'Upload test.png')
expect(getByLabelText('Upload test.png')).toBeInTheDocument()
})

it('passed the `aria-valuenow` down to the progress bar', () => {
const {getByRole} = HTMLRender(<ProgressBar progress={80} aria-valuenow={80} />)
expect(getByRole('progressbar')).toHaveAttribute('aria-valuenow', '80')
})

it('passed the `aria-valuetext` down to the progress bar', () => {
const {getByRole} = HTMLRender(<ProgressBar aria-valuetext="80 percent" />)
expect(getByRole('progressbar')).toHaveAttribute('aria-valuetext', '80 percent')
})

it('does not pass the `aria-label` down to the progress bar if there are multiple items', () => {
const {getByRole} = HTMLRender(
<ProgressBar aria-label="Upload test.png">
<ProgressBar.Item progress={80} />
</ProgressBar>,
)
expect(getByRole('progressbar')).not.toHaveAttribute('aria-label')
})

it('passes aria attributes to the progress bar item', () => {
const {getByRole} = HTMLRender(
<ProgressBar>
<ProgressBar.Item progress={50} aria-label="Progress" ria-valuenow="50"></ProgressBar.Item>
</ProgressBar>,
)
expect(getByRole('progressbar')).toHaveAttribute('aria-valuenow', '50')
expect(getByRole('progressbar')).toHaveAttribute('aria-label', 'Progress')
})

it('provides `aria-valuenow` to the progress bar item if it is not already provided', () => {
const {getByRole} = HTMLRender(<ProgressBar progress={50} />)
expect(getByRole('progressbar')).toHaveAttribute('aria-valuenow', '50')
})

it('should warn users if they do not pass the correct props or ARIA attributes', async () => {
const spy = jest.spyOn(console, 'warn').mockImplementationOnce(() => {})

render(<ProgressBar />)

expect(spy).toHaveBeenCalledTimes(1)
spy.mockRestore()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ exports[`ProgressBar respects the "progress" prop 1`] = `
}
<span
aria-label="Upload test.png"
aria-valuemax={100}
aria-valuemin={0}
aria-valuenow={80}
className="c0"
role="progressbar"
>
<span
aria-label="Upload test.png"
aria-valuemax={100}
aria-valuemin={0}
aria-valuenow={80}
className="c1"
role="progressbar"
/>
</span>
`;

0 comments on commit b67413f

Please sign in to comment.