packages/react-aria/docs/progress/useProgressBar.mdx
{/* Copyright 2020 Adobe. All rights reserved. This file is licensed to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */}
import {Layout} from '@react-spectrum/docs'; export default Layout;
import docs from 'docs:@react-aria/progress'; import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, PageDescription} from '@react-spectrum/docs'; import packageData from '@react-aria/progress/package.json'; import Anatomy from './anatomy.svg';
<PageDescription>{docs.exports.useProgressBar.description}</PageDescription>
<HeaderInfo packageData={packageData} componentNames={['useProgressBar']} sourceData={[ {type: 'W3C', url: 'https://www.w3.org/TR/wai-aria-1.2/#progressbar'} ]} />
The <progress>
HTML element can be used to build a progress bar, however it is
very difficult to style cross browser. useProgressBar helps achieve accessible
progress bars and spinners that can be styled as needed.
Progress bars consist of a track element showing the full progress of an operation, a fill element showing the current progress, a label, and an optional value label. The track and bar elements represent the progress visually, while a wrapper element represents the progress to assistive technology using the progressbar ARIA role.
useProgressBar returns two sets of props that you should spread onto the appropriate element:
<TypeContext.Provider value={docs.links}> <InterfaceType properties={docs.links[docs.exports.useProgressBar.return.id].properties} /> </TypeContext.Provider>
If there is no visual label, an aria-label or aria-labelledby prop must be passed instead
to identify the element to screen readers.
import {useProgressBar} from '@react-aria/progress';
function ProgressBar(props) {
let {
label,
showValueLabel = !!label,
value,
minValue = 0,
maxValue = 100
} = props;
let {
progressBarProps,
labelProps
} = useProgressBar(props);
// Calculate the width of the progress bar as a percentage
let percentage = (value - minValue) / (maxValue - minValue);
let barWidth = `${Math.round(percentage * 100)}%`;
return (
<div {...progressBarProps} style={{width: 200}}>
<div style={{display: 'flex', justifyContent: 'space-between'}}>
{label &&
<span {...labelProps}>
{label}
</span>
}
{showValueLabel &&
<span>
{progressBarProps['aria-valuetext']}
</span>
}
</div>
<div style={{height: 10, background: 'lightgray'}}>
<div style={{width: barWidth, height: 10, background: 'orange'}} />
</div>
</div>
);
}
<ProgressBar label="Loading…" value={80} />
Progress bars may also be represented using a circular visualization rather than a line. This is often used to represent indeterminate operations, but may also be used for determinate progress indicators when space is limited. The following example shows a progress bar visualized as a circular spinner using SVG.
function ProgressCircle(props) {
let {isIndeterminate, value, minValue = 0, maxValue = 100} = props;
let {progressBarProps} = useProgressBar(props);
let center = 16;
let strokeWidth = 4;
let r = 16 - strokeWidth;
let c = 2 * r * Math.PI;
let percentage = isIndeterminate ? 0.25 : (value - minValue) / (maxValue - minValue);
let offset = c - percentage * c;
return (
<svg
{...progressBarProps}
width={32}
height={32}
viewBox="0 0 32 32"
fill="none"
strokeWidth={strokeWidth}>
<circle
role="presentation"
cx={center}
cy={center}
r={r}
stroke="gray" />
<circle
role="presentation"
cx={center}
cy={center}
r={r}
stroke="orange"
strokeDasharray={`${c} ${c}`}
strokeDashoffset={offset}
transform="rotate(-90 16 16)">
{props.isIndeterminate &&
<animateTransform
attributeName="transform"
type="rotate"
begin="0s"
dur="1s"
from="0 16 16"
to="360 16 16"
repeatCount="indefinite" />
}
</circle>
</svg>
);
}
<ProgressCircle aria-label="Loading…" value={60} />
The following examples show how to use the ProgressBar and ProgressCircle components created in the above example.
By default, the value prop represents the current percentage of progress, as the minimum and maximum values default to 0 and 100, respectively. Alternatively, a different scale can be used by setting the minValue and maxValue props.
<ProgressBar
label="Loading…"
minValue={50}
maxValue={150}
value={100} />
Values are formatted as a percentage by default, but this can be modified by using the formatOptions prop to specify a different format.
formatOptions is compatible with the option parameter of Intl.NumberFormat and is applied based on the current locale.
<ProgressBar
label="Sending…"
formatOptions={{style: 'currency', currency: 'JPY'}}
value={60} />
The valueLabel prop allows the formatted value to be replaced with a custom string.
<ProgressBar
label="Feeding…"
valueLabel="30 of 100 dogs"
value={30} />
The isIndeterminate prop can be used to represent an indeterminate operation. The ProgressCircle component created above
implements an animation to indicate this visually.
<ProgressCircle
aria-label="Loading…"
isIndeterminate />
useProgressBar will handle localized formatting of the value label for accessibility
automatically. This is returned in the aria-valuetext prop in progressBarProps. You
can use this to create a visible label if needed and ensure that it is formatted correctly.
The number formatting can also be controlled using the formatOptions prop.
In right-to-left languages, the progress bar should be mirrored. The label is right-aligned, the value is left-aligned, and the fill progresses from right to left. Ensure that your CSS accounts for this.