Skip to content

Commit

Permalink
Merge branch 'BogdanCerovac-accessibility-improvements'
Browse files Browse the repository at this point in the history
  • Loading branch information
tajo committed Aug 7, 2024
2 parents 2c144c4 + 92cd0ff commit 7f2e071
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/witty-bulldogs-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-range": minor
---

Improving a11y of the slider by adding new label properties.
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const SuperSimple: React.FC = () => {
const [values, setValues] = React.useState([50]);
return (
<Range
label="Select your value"
step={0.1}
min={0}
max={100}
Expand Down Expand Up @@ -65,22 +66,51 @@ const SuperSimple: React.FC = () => {
- Range input supporting **vertical and horizontal sliding**
- Unopinionated styling, great for **CSS in JS** too
- No wrapping divs or additional markup, bring your own!
- **Accessible**, made for keyboards and screen readers
- Works with keyboard, uses `aria` patterns for assistive technologies, check a11y part for accessibility info
- **Touchable**, works on mobile devices
- Can handle negative and decimal values
- Stateless and controlled single component
- **No dependencies, 6kB (gzipped)**
- Coverage by [e2e playwright tests](#end-to-end-testing)
- RTL support

## Keyboard support


## A11y (accessibility)

### Keyboard support

- `tab` and `shift+tab` to focus thumbs
- `arrow up` or `arrow right` or `k` to increase the thumb value by one step
- `arrow down` or `arrow left` or `j` to decrease the thumb value by one step
- `page up` to increase the thumb value by ten steps
- `page down` to decrease the thumb value by ten steps

### Assistive technologies (for example screen readers)

*You are responsible for the accessibility name!*

Default accessibility name is Accessibility label, set with code: `aria-label="Accessibility label"`, but is not visible (only for screen-readers and other assistive tech),
so make sure to use correct name by passing it to the prop called `label`.

If you want to have a visible label (best practice), then use `labelledBy`.

You naming options are:

- you can name it by using `label` prop (translates to `aria-label` in the code)
- you can name it by adding a visual element with a unique ID that can be used with `labelledBy` prop (translates to `aria-labellebdy` in the code)

Please check `Basic` and `Basic visible label` examples for more info.

Aria used on the component is following [Accessible Rich Internet Applications (WAI-ARIA) 1.2 `slider` role](https://www.w3.org/TR/wai-aria-1.2/#slider), but please be aware that different assistive technologies provide different support (especially in combination with operating systems and browsers).

Therefore please make sure to test it yourself and with people with disabilities.
We can not provide prompt information about support, but are happy to add your findings to this Readme.

### Conformance to WCAG and other accessibility standards

We need to do more testing to claim any conformance. We did make sure the component is operable with keyboard, that it is respecting ARIA patterns for `slider` role and having possibility to name the component (accessible name). You are welcome to report any accessibility related findings, we look forward to add information about user tests and support for assistive technologies.

## `<Range />` props

### renderTrack
Expand Down
106 changes: 106 additions & 0 deletions examples/BasicVisibleLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import * as React from "react";
import { Range, getTrackBackground } from "../src/index";

const STEP = 0.1;
const MIN = 0;
const MAX = 100;

const BasicVisibleLabelExample: React.FC<{ rtl: boolean }> = ({ rtl }) => {
const [values, setValues] = React.useState([50]);
const rangeRef: any = React.useRef<Range>();

return (
<div
style={{
display: "flex",
justifyContent: "center",
flexWrap: "wrap",
}}
>
<label
onClick={(e) => {
rangeRef.current.thumbRefs[0].current.focus();
}}
id="unique_id"
style={{
flex: "auto",
fontFamily: "sans-serif",
}}
>
Visible accessibility label:
</label>

<Range
labelledBy="unique_id"
ref={rangeRef}
values={values}
step={STEP}
min={MIN}
max={MAX}
rtl={rtl}
onChange={(values) => setValues(values)}
renderTrack={({ props, children }) => (
<div
onMouseDown={props.onMouseDown}
onTouchStart={props.onTouchStart}
style={{
...props.style,
height: "36px",
display: "flex",
width: "100%",
}}
>
<div
ref={props.ref}
style={{
height: "5px",
width: "100%",
borderRadius: "4px",
background: getTrackBackground({
values,
colors: ["#548BF4", "#ccc"],
min: MIN,
max: MAX,
rtl,
}),
alignSelf: "center",
}}
>
{children}
</div>
</div>
)}
renderThumb={({ props, isDragged }) => (
<div
{...props}
key={props.key}
style={{
...props.style,
height: "42px",
width: "42px",
borderRadius: "4px",
backgroundColor: "#FFF",
display: "flex",
justifyContent: "center",
alignItems: "center",
boxShadow: "0px 2px 6px #AAA",
}}
>
<div
style={{
height: "16px",
width: "5px",
backgroundColor: isDragged ? "#548BF4" : "#CCC",
}}
/>
</div>
)}
/>
<output style={{ marginTop: "30px" }} id="output">
{values[0].toFixed(1)}
</output>
</div>
);
};

export default BasicVisibleLabelExample;
6 changes: 6 additions & 0 deletions src/Range.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const DECREASE_KEYS = ["ArrowLeft", "ArrowDown", "j", "PageDown"];

class Range extends React.Component<IProps> {
static defaultProps = {
label: "Accessibility label",
labelledBy: null,
step: 1,
direction: Direction.Right,
rtl: false,
Expand Down Expand Up @@ -619,6 +621,8 @@ class Range extends React.Component<IProps> {

render() {
const {
label,
labelledBy,
renderTrack,
renderThumb,
renderMark = () => null,
Expand Down Expand Up @@ -700,6 +704,8 @@ class Range extends React.Component<IProps> {
"aria-valuenow": value,
draggable: false,
ref: this.thumbRefs[index],
"aria-label": label,
"aria-labelledby": labelledBy,
role: "slider",
onKeyDown: disabled ? voidFn : this.onKeyDown,
onKeyUp: disabled ? voidFn : this.onKeyUp,
Expand Down
4 changes: 4 additions & 0 deletions src/range.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from "react";
import AllowOverlapExample from "../examples/AllowOverlap";
import AnimatingContainerExample from "../examples/AnimatingContainer";
import BasicExample from "../examples/Basic";
import BasicVisibleLabelExample from "../examples/BasicVisibleLabel";
import BasicWithBorderExample from "../examples/BasicWithBorder";
import BigStepsExample from "../examples/BigSteps";
import DisabledExample from "../examples/Disabled";
Expand Down Expand Up @@ -33,6 +34,9 @@ export const AnimatingContainer: React.FC<{ rtl: boolean }> = ({ rtl }) => (
export const Basic: React.FC<{ rtl: boolean }> = ({ rtl }) => (
<BasicExample rtl={rtl} />
);
export const BasicVisibleLabel: React.FC<{ rtl: boolean }> = ({ rtl }) => (
<BasicVisibleLabelExample rtl={rtl} />
);
export const BasicWithBorder: React.FC<{ rtl: boolean }> = ({ rtl }) => (
<BasicWithBorderExample rtl={rtl} />
);
Expand Down
4 changes: 4 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export interface ITrackBackground {
rtl?: boolean;
}
export interface IProps {
label: string;
labelledBy: string;
values: number[];
min: number;
max: number;
Expand Down Expand Up @@ -48,6 +50,8 @@ export interface IThumbProps {
key: number;
style: React.CSSProperties;
tabIndex?: number;
"aria-label": string;
"aria-labelledby": string;
"aria-valuemax": number;
"aria-valuemin": number;
"aria-valuenow": number;
Expand Down

0 comments on commit 7f2e071

Please sign in to comment.