Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions documentation-site/examples/progress-bar/intent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from "react";
import { ProgressBar, INTENT } from "baseui/progress-bar";

export default function Example() {
return (
<>
<ProgressBar value={50} showLabel intent={INTENT.positive} />
<br />
<ProgressBar value={50} showLabel intent={INTENT.warning} />
<br />
<ProgressBar value={50} showLabel intent={INTENT.negative} />
<br />
<ProgressBar value={50} showLabel intent={INTENT.brand} />
<br />
<ProgressBar infinite showLabel intent={INTENT.positive} />
</>
);
}
4 changes: 3 additions & 1 deletion documentation-site/examples/progress-bar/steps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ export default function Example() {
return (
<div>
<ProgressBar value={value} steps={5} />
<div style={{ display: "flex", justifyContent: "center" }}>
<div
style={{ display: "flex", justifyContent: "center", marginTop: "10px" }}
>
<Button
onClick={() => setValue((prevValue) => prevValue - stepSize)}
size={SIZE.compact}
Expand Down
9 changes: 8 additions & 1 deletion documentation-site/pages/components/progress-bar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import WithLabel from "examples/progress-bar/with-label.tsx";
import CustomLabel from "examples/progress-bar/custom-label.tsx";
import Customization from "examples/progress-bar/overrides.tsx";
import Steps from "examples/progress-bar/steps.tsx";
import Intent from "examples/progress-bar/intent.tsx";
import ProgressBarRounded from "examples/progress-bar/rounded.tsx";

import { ProgressBar } from "baseui/progress-bar";
Expand All @@ -26,11 +27,13 @@ Indicates a wait time for actions - either within an experience flow or loading

## Accessibility

- The progress bar always sets `role="progressbar"`
- The progress bar always sets `role="progressbar"` and `aria-live="polite"`
- When `infinite` is `false` (the progress bar is determinate), these determinate aria tags are set:
- `aria-valuenow={value}`
- `aria-valuemin={0}`
- `aria-valuemax={100}`
- When `infinite` is `true`, `aria-busy="true"` is set
- Use `ariaLabel` or `aria-label` prop to provide a descriptive label for screen readers

## Examples

Expand All @@ -45,6 +48,10 @@ Indicates a wait time for actions - either within an experience flow or loading
<Negative />
</Example>

<Example title="Progress bar with Intent" path="progress-bar/intent.tsx">
<Intent />
</Example>

<Example
title="Progress bar with Visible Label"
path="progress-bar/with-label.tsx"
Expand Down
2 changes: 2 additions & 0 deletions src/progress-bar/__tests__/progress-bar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import { Scenario as ProgressbarIntentScenario } from './progressbar-intent.scenario';
import { Scenario as ProgressbarNegativeScenario } from './progressbar-negative.scenario';
import { Scenario as ProgressbarRoundedAnimatedScenario } from './progressbar-rounded-animated.scenario';
import { Scenario as ProgressbarRoundedOverridesScenario } from './progressbar-rounded-overrides.scenario';
import { Scenario as ProgressbarRoundedScenario } from './progressbar-rounded.scenario';
import { Scenario as ProgressbarScenario } from './progressbar.scenario';

export const ProgressbarIntent = () => <ProgressbarIntentScenario />;
export const ProgressbarNegative = () => <ProgressbarNegativeScenario />;
export const ProgressbarRoundedAnimated = () => <ProgressbarRoundedAnimatedScenario />;

Expand Down
54 changes: 54 additions & 0 deletions src/progress-bar/__tests__/progressbar-intent.scenario.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
Copyright (c) Uber Technologies, Inc.

This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
import * as React from 'react';

import { ProgressBar, SIZE, INTENT } from '..';

export function Scenario() {
return (
<>
<h3>Determinate - All Intents</h3>
<ProgressBar value={50} showLabel intent={INTENT.default} />
<br />
<ProgressBar value={50} showLabel intent={INTENT.positive} />
<br />
<ProgressBar value={50} showLabel intent={INTENT.warning} />
<br />
<ProgressBar value={50} showLabel intent={INTENT.negative} />
<br />
<ProgressBar value={50} showLabel intent={INTENT.brand} />
<br />

<h3>Indeterminate - All Intents</h3>
<ProgressBar infinite showLabel intent={INTENT.default} />
<br />
<ProgressBar infinite showLabel intent={INTENT.positive} />
<br />
<ProgressBar infinite showLabel intent={INTENT.warning} />
<br />
<ProgressBar infinite showLabel intent={INTENT.negative} />
<br />
<ProgressBar infinite showLabel intent={INTENT.brand} />
<br />

<h3>Different Sizes with Intent</h3>
<ProgressBar value={60} showLabel size={SIZE.small} intent={INTENT.positive} />
<br />
<ProgressBar value={60} showLabel size={SIZE.medium} intent={INTENT.warning} />
<br />
<ProgressBar value={60} showLabel size={SIZE.large} intent={INTENT.negative} />
<br />

<h3>Segmented with Intent</h3>
<ProgressBar value={60} showLabel steps={5} intent={INTENT.positive} />
<br />
<ProgressBar value={60} showLabel steps={5} intent={INTENT.warning} />
<br />
<ProgressBar value={60} showLabel steps={5} intent={INTENT.negative} />
</>
);
}
28 changes: 27 additions & 1 deletion src/progress-bar/__tests__/progressbar-rounded.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,35 @@ describe('Rounded progress bar', function () {
getByText(container, '50%');
});

it('should render component and set a correct aria-valuenow value (rounded and formatted)', async () => {
it('should render component and set a correct aria-valuenow value (numeric)', async () => {
const { container } = render(<ProgressBarRounded progress={0.5} size={SIZE.medium} />);
const progressBar = getByRole(container, 'progressbar');
expect(progressBar).toHaveAttribute('aria-valuenow', '0.5');
});

it('should have aria-live attribute for accessibility', () => {
const { container } = render(<ProgressBarRounded progress={0.5} />);
const progressBar = getByRole(container, 'progressbar');
expect(progressBar).toHaveAttribute('aria-live', 'polite');
});

it('should have default aria-label with percentage', () => {
const { container } = render(<ProgressBarRounded progress={0.75} />);
const progressBar = getByRole(container, 'progressbar', { name: '75% complete' });
expect(progressBar).toBeInTheDocument();
});

it('should accept custom aria-label prop', () => {
const { container } = render(<ProgressBarRounded progress={0.5} ariaLabel="Uploading file" />);
const progressBar = getByRole(container, 'progressbar', { name: 'Uploading file' });
expect(progressBar).toBeInTheDocument();
});

it('should accept custom aria-label via kebab-case prop', () => {
const { container } = render(
<ProgressBarRounded progress={0.5} aria-label="Processing data" />
);
const progressBar = getByRole(container, 'progressbar', { name: 'Processing data' });
expect(progressBar).toBeInTheDocument();
});
});
19 changes: 19 additions & 0 deletions src/progress-bar/__tests__/progressbar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ LICENSE file in the root directory of this source tree.
*/
import * as React from 'react';
import { render, getByRole, getByText } from '@testing-library/react';
import '@testing-library/jest-dom';

import { ProgressBar } from '..';

Expand Down Expand Up @@ -37,4 +38,22 @@ describe('Stateless progress bar', function () {
const { container } = render(<ProgressBar aria-label="test" steps={2} />);
getByRole(container, 'progressbar', { name: 'test' });
});

it('should have aria-live attribute for accessibility', () => {
const { container } = render(<ProgressBar value={75} />);
const progressBar = getByRole(container, 'progressbar');
expect(progressBar).toHaveAttribute('aria-live', 'polite');
});

it('should have aria-busy true for infinite progress bar', () => {
const { container } = render(<ProgressBar infinite />);
const progressBar = getByRole(container, 'progressbar');
expect(progressBar).toHaveAttribute('aria-busy', 'true');
});

it('should not have aria-busy for determinate progress bar', () => {
const { container } = render(<ProgressBar value={75} />);
const progressBar = getByRole(container, 'progressbar');
expect(progressBar).not.toHaveAttribute('aria-busy');
});
});
8 changes: 8 additions & 0 deletions src/progress-bar/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@ export const SIZE = {
medium: 'medium',
large: 'large',
} as const;

export const INTENT = {
default: 'default',
positive: 'positive',
warning: 'warning',
negative: 'negative',
brand: 'brand',
} as const;
2 changes: 1 addition & 1 deletion src/progress-bar/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LICENSE file in the root directory of this source tree.
*/
export { default as ProgressBar } from './progressbar';
export { default as ProgressBarRounded } from './progressbar-rounded';
export { SIZE } from './constants';
export { SIZE, INTENT } from './constants';
// Styled elements
export {
StyledRoot,
Expand Down
14 changes: 10 additions & 4 deletions src/progress-bar/progressbar-rounded.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ function ProgressBarRounded({
animate = true,
inline = false,
overrides = {},
ariaLabel,
...restProps
}: ProgressBarRoundedProps) {
const {
Expand Down Expand Up @@ -97,13 +98,19 @@ function ProgressBarRounded({
loop();
}, [progress]); // We want *only* `progress` to trigger this effect

const propsAriaLabel = restProps['aria-label'] || ariaLabel;
const progressPercentage = `${roundTo(Math.min(progress * 100, 100))}%`;
const defaultAriaLabel = propsAriaLabel || `${progressPercentage} complete`;

return (
<Root
data-baseweb="progressbar-rounded"
role="progressbar"
aria-valuenow={progress.toFixed(2).replace(/\.?0+$/, '')}
aria-label={defaultAriaLabel}
aria-valuenow={progress}
aria-valuemin={0}
aria-valuemax={1}
aria-live="polite"
$size={size}
$inline={inline}
{...restProps}
Expand All @@ -122,9 +129,8 @@ function ProgressBarRounded({
/>
</Svg>

<Text $size={size} {...textProps}>
{roundTo(Math.min(progress * 100, 100))}%
</Text>
{/* eslint-disable-next-line react/no-children-prop */}
<Text $size={size} children={progressPercentage} {...textProps} />
</Root>
);
}
Expand Down
33 changes: 16 additions & 17 deletions src/progress-bar/progressbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,6 @@ class ProgressBar extends React.Component<
value: 0,
};

componentDidMount() {
// TODO(v11): remove warning when switching default Spinner
if (__DEV__) {
if (this.props.errorMessage) {
console.warn('baseui:ProgressBar The `errorMessage` prop is deprecated in WAI-ARIA v1.2.');
}
}
}

render() {
const {
overrides = {},
Expand All @@ -59,7 +50,8 @@ class ProgressBar extends React.Component<
maxValue,
showLabel,
infinite,
errorMessage,
intent,
errorMessage, // Deprecated, ignored
forwardedRef,
...restProps
} = this.props;
Expand All @@ -85,6 +77,7 @@ class ProgressBar extends React.Component<
const [InfiniteBar, infiniteBarProps] = getOverrides(overrides.InfiniteBar, StyledInfiniteBar);
const sharedProps = {
$infinite: infinite,
$intent: intent,
$size: size,
$steps: steps,
$successValue: maximumValue,
Expand Down Expand Up @@ -113,8 +106,6 @@ class ProgressBar extends React.Component<
return `Step ${Math.ceil(((value - minValue) / (maxValue - minValue)) * steps)} of ${steps}`;
}
return (
/* eslint-disable jsx-a11y/role-supports-aria-props */

<Root
ref={forwardedRef}
data-baseweb="progress-bar"
Expand All @@ -123,18 +114,27 @@ class ProgressBar extends React.Component<
aria-valuenow={infinite ? null : value}
aria-valuemin={infinite ? null : minValue}
aria-valuemax={infinite ? null : maximumValue}
aria-invalid={errorMessage ? true : null}
aria-errormessage={errorMessage}
aria-live="polite"
aria-busy={infinite ? true : null}
{...restProps}
{...sharedProps}
{...rootProps}
>
<BarContainer {...sharedProps} {...barContainerProps}>
{infinite ? (
<React.Fragment>
<InfiniteBar $isLeft={true} $size={sharedProps.$size} {...infiniteBarProps} />
<InfiniteBar
$isLeft={true}
$size={sharedProps.$size}
$intent={sharedProps.$intent}
{...infiniteBarProps}
/>

<InfiniteBar $size={sharedProps.$size} {...infiniteBarProps} />
<InfiniteBar
$size={sharedProps.$size}
$intent={sharedProps.$intent}
{...infiniteBarProps}
/>
</React.Fragment>
) : (
renderProgressBar()
Expand All @@ -147,7 +147,6 @@ class ProgressBar extends React.Component<
)}
</Root>
);
/* eslint-enable jsx-a11y/role-supports-aria-props */
}
}

Expand Down
Loading
Loading