react-range

repository·main·Indexed 21 days ago

https://github.com/tajo/react-range

A React library for building accessible range sliders that conform to WCAG standards. It uses a render-prop pattern via the <Range /> component, allowing high customization of tracks, thumbs, and marks. The library supports multiple thumbs, various directions (Right, Left, Up, Down), and provides built-in keyboard accessibility and WAI-ARIA slider roles.

Tokens
4.7K
Snippets
15
Records
23
Agent score
72%

What's inside react-range

  1. How to provide accessibility names to <Range />

    main

    The <Range /> component follows the WAI-ARIA slider role. To ensure screen readers can identify the component, you must provide an accessible name using one of two methods:

    1. Using the label prop: This translates to aria-label in the rendered HTML. This is useful for non-visible labels.
    2. Using labelledBy: If you have a visible text element acting as a label, give that element a unique ID and pass that ID to the labelledBy prop. This translates to aria-labelledby in the rendered HTML.

    Note: The default accessibility name is "Accessibility label", which is not visible to sighted users.

  2. Set up a local development environment

    main

    To contribute to react-range, follow these steps to spin up the development environment:

    1. Clone the repository.
    2. Install dependencies using pnpm.
    3. Start the ladle server.
    git clone https://github.com/tajo/react-range
    cd react-range
    pnpm install
    pnpm ladle serve
  3. Run end-to-end tests for react-range

    main

    The library uses Playwright for end-to-end testing to ensure correct DOM interactions and pixel-perfect positioning. You can run tests in two modes:

    1. Dev Mode: Slows down operations and opens a visible browser window. This requires running the ladle server first.
    2. CI Mode: Runs tests in the background using a headless browser. This is faster and more suitable for automated environments.
    # Dev mode (opens browser)
    pnpm ladle serve
    pnpm test:e2e:dev
    
    # CI mode (headless, background)
    pnpm test:e2e
  4. How keyboard accessibility works in Range

    main

    The Range component provides built-in keyboard support for accessibility. When a thumb is focused, the following keys can be used:

    • Increase Value: ArrowRight, ArrowUp, k, PageUp.
    • Decrease Value: ArrowLeft, ArrowDown, j, PageDown.
    • Tab: Moves focus away from the slider. If the value was being changed via keyboard, onFinalChange is triggered.

    Movement increments are determined by the step prop. PageUp and PageDown move the value by step * 10.

  5. Use the Range component

    main

    The Range component is the primary interface for creating range sliders. It uses a render-prop pattern where you provide functions to render the track, thumbs, and optionally marks. The component manages the slider state, accessibility, and interaction logic (mouse, touch, and keyboard).

    To use it, you must provide:

    • values: An array of numbers representing the current slider positions.
    • onChange: A callback function that receives the updated values array.
    • renderTrack: A function that renders the slider track. It receives props (which must be spread onto the track element), isDragged status, and disabled status.
    • renderThumb: A function that renders each thumb. It receives the thumb's index, current value, isDragged status, and props (which must be spread onto the thumb element).

    Important: You must spread the props provided by renderTrack and renderThumb onto your elements to ensure accessibility (ARIA attributes) and event handling work correctly.

    import React from 'react';
    import Range from 'react-range';
    
    const MySlider = () => {
      return (
        <Range
          values={[50]}
          min={0}
          max={100}
          step={1}
          onChange={(values) => console.log(values)}
          renderTrack={({ props, isDragged, disabled }) => (
            <div {...props} style={{ ...props.style, height: '10px', background: 'blue' }}>
              {/* Track content */}
            </div>
          )}
          renderThumb={({ props, isDragged }) => (
            <div
              {...props}
              style={{ ...props.style, backgroundColor: 'white', height: '20px', width: '20px', borderRadius: '50%' }}
            />
          )}
        />
      );
    };
  6. Basic usage of the <Range /> component

    main

    The <Range /> component is a stateless, controlled component used to build range inputs. You must provide values, min, max, and render functions for the track and thumbs. This example demonstrates a simple single-thumb range slider.

    import * as React from "react";
    import { Range } from "react-range";
    
    const SuperSimple: React.FC = () => {
      const [values, setValues] = React.useState([50]);
      return (
        <Range
          label="Select your value"
          step={0.1}
          min={0}
          max={100}
          values={values}
          onChange={(values) => setValues(values)}
          renderTrack={({ props, children }) => (
            <div
              {...props}
              style={{
                ...props.style,
                height: "6px",
                width: "100%",
                backgroundColor: "#ccc",
              }}
            >
              {children}
            </div>
          )}
          renderThumb={({ props }) => (
            <div
              {...props}
              key={props.key}
              style={{
                ...props.style,
                height: "42px",
                width: "42px",
                backgroundColor: "#999",
              }}
            />
          )}
        />
      );
    };
  7. Configure the renderMark (optional) prop

    main

    The renderMark prop allows you to render an element at each step along the track. It receives:

    • props: Must be spread over the mark element. Includes key, style, and ref.
    • index: The index of the mark.

    react-range will automatically position these marks at the correct locations based on the track dimensions.

    renderMark?: (params: {
      props: {
        key: string;
        style: React.CSSProperties;
        ref: React.RefObject<any>;
      };
      index: number;
    }) => React.ReactNode;
  8. Configure the renderThumb prop

    main

    The renderThumb prop defines the draggable thumb elements. It receives an object with the following properties:

    • props: Must be spread over the thumb element. Includes key, style, tabIndex, aria-valuemax, aria-valuemin, aria-valuenow, draggable, role, onKeyDown, and onKeyUp.
    • value: The current numeric value of the thumb (relative to min, max, and step).
    • index: The index of the thumb in the values array.
    • isDragged: Boolean indicating if this specific thumb is being dragged.

    Each thumb in the values array will trigger a call to this function.

    renderThumb: (params: {
      props: {
        key: number;
        style: React.CSSProperties;
        tabIndex?: number;
        "aria-valuemax": number;
        "aria-valuemin": number;
        "aria-valuenow": number;
        draggable: boolean;
        role: string;
        onKeyDown: (e: React.KeyboardEvent) => void;
        onKeyUp: (e: React.KeyboardEvent) => void;
      };
      value: number;
      index: number;
      isDragged: boolean;
    }) => React.ReactNode;
  9. Configure the renderTrack prop

    main

    The renderTrack prop defines the root (track) element of the range. It receives an object with the following properties:

    • props: Must be spread over the root track element. It contains style, ref, onMouseDown, and onTouchStart required to connect mouse/touch events and positioning.
    • children: The rendered thumbs (provided via renderThumb).
    • isDragged: Boolean indicating if any thumb is currently being dragged.
    • disabled: Boolean indicating if the component is disabled.

    Best Practice: Use at least two nested divs. An outer div that is larger (to provide a bigger target for onMouseDown/onTouchStart) and an inner div that represents the visible track. Spread props onto the outer div.

    renderTrack: (params: {
      props: {
        style: React.CSSProperties;
        ref: React.RefObject<any>;
        onMouseDown: (e: React.MouseEvent) => void;
        onTouchStart: (e: React.TouchEvent) => void;
      };
      children: React.ReactNode;
      isDragged: boolean;
      disabled: boolean;
    }) => React.ReactNode;
  10. Reference: <Range /> props

    main

    A complete list of available props for the <Range /> component.

    // values: Array of numbers controlling thumb positions
    values: number[];
    
    // onChange: Called when a thumb is moved
    onChange: (values: number[]) => void;
    
    // onFinalChange: Called when a change is finished (mouse/touch up, or keyup)
    onFinalChange: (values: number[]) => void;
    
    // min: Range start (decimal/negative supported). Default: 0
    min: number;
    
    // max: Range end (decimal/negative supported). Default: 100
    max: number;
    
    // step: Minimal distance between values. Default: 1
    step: number;
    
    // allowOverlap: Whether multiple thumbs can overlap. Default: false
    allowOverlap: boolean;
    
    // draggableTrack: Whether all thumbs can be dragged at once. Default: false
    draggableTrack: boolean;
    
    // direction: Orientation and increase direction. Default: Direction.Right
    direction: Direction;
    
    // disabled: Ignores events and makes component non-focusable. Default: false
    disabled: boolean;
    
    // rtl: Optimized for RTL layouts. Default: false
    rtl: boolean;