Skip to content

Add a standardized Slider component (BBBSlider) #84

Description

@Arthurk12

Is your feature request related to a problem? Please describe.

The library has no slider. Every feature that needs a range control — camera brightness, webcam grid size, volume, zoom — builds its own on top of a raw <input type="range"> or a bare MUI Slider, re-implementing track/thumb styling, tick marks and the value tooltip, and drifting from the palette in src/stylesheets.

Two usages must be covered by one component: those where the exact number matters (volume in %), and those where it does not (brightness, grid size), where the slider only moves a parameter between two ends — and, for the grid, must land on one of a few discrete stages.

Describe the solution you'd like

BBBSlider under src/components/Slider/, wrapping MUI's Slider the way BBBToggle wraps Switch: MUI owns interaction, keyboard handling and accessibility; the wrapper owns the BBB look (tokens from src/stylesheets) and adds:

  • End icons — startIcon / endIcon as React.ReactNode, decorative and aria-hidden, following the icon prop of BBBSelect, BBBHint and BBBNavigation.
  • Value tooltip — MUI's own value label, defaulted to valueLabelDisplay="auto" (shown on hover, focus and drag) and styled with the library tokens. valueLabelFormat covers the non-numeric case ('2x2', 'Low'); "off" hides it entirely.
  • Subdivisions — step snaps the thumb and marks draws the ticks (boolean, or { value, label }[]), exactly as in MUI. The same mechanism serves a continuous volume slider and a four-stage grid size.
  • dataTest — data-test attribute on the slider root, following BBBInput and BBBNavigation.

Scope of the first version is horizontal and single-thumb.

Describe alternatives you've considered

  • Wrapping <input type="range">: drops the MUI dependency, but ticks, the thumb-anchored tooltip and the aria-*/keyboard behaviour would all be hand-written. MUI is already a peer dependency.
  • Tippy for the value tooltip (as in BBButton/BBBAccordion): Tippy anchors to the element, not to the moving thumb, so it would not follow a drag. A static Tippy tooltip describing the whole control can be added later.
  • A stages={['Low', 'Medium', 'High']} prop deriving min/max/step/marks: a second API on top of MUI's, which diverges as soon as a consumer needs something it does not model. marks + valueLabelFormat already covers the case.
  • Clickable end icons that step the value: turns two decorative nodes into focusable buttons with their own labels and focus handling. Can be added later as an opt-in prop.

Affected component(s)

New component: BBBSlider. No existing component changes.

Proposed API / Usage Example

import { BBBSlider } from '@bigbluebutton/bbb-ui-components-react/Slider';
import { MdBrightnessLow, MdBrightnessHigh } from 'react-icons/md';

// The number matters: volume in percent
<BBBSlider
  min={0}
  max={100}
  defaultValue={50}
  valueLabelFormat={(value) => `${value}%`}
  ariaLabel="Volume"
  dataTest="volume-slider"
  onChange={(event, value) => setVolume(value)}
/>

// The number does not matter: brightness between two ends
<BBBSlider
  startIcon={<MdBrightnessLow />}
  endIcon={<MdBrightnessHigh />}
  valueLabelDisplay="off"
  ariaLabel="Camera brightness"
  onChange={(event, value) => setBrightness(value)}
/>

// Discrete stages: webcam grid size snapping to four positions
<BBBSlider
  min={1}
  max={4}
  step={1}
  marks={[
    { value: 1, label: '1x1' },
    { value: 2, label: '2x2' },
    { value: 3, label: '3x3' },
    { value: 4, label: '4x4' },
  ]}
  defaultValue={2}
  valueLabelFormat={(value) => `${value}x${value}`}
  ariaLabel="Webcam grid size"
  onChangeCommitted={(event, value) => setGridSize(value)}
/>

Props added by the wrapper:

Property Type Default Description
startIcon React.ReactNode Decorative icon rendered before the track, at the min end.
endIcon React.ReactNode Decorative icon rendered after the track, at the max end.
dataTest string data-test attribute on the slider root, used for test selectors.
ariaLabel string Accessible name for the slider.
ariaLabelledBy string ID of the element that labels the slider; ignored when ariaLabel is set.
ariaDescribedBy string ID of the element that describes the slider.

Inherited from MUI's SliderProps and worth documenting in the README and argTypes: min, max, step, marks, value/defaultValue, onChange, onChangeCommitted, valueLabelDisplay (defaulted to 'auto'), valueLabelFormat, getAriaValueText and disabled. Any other prop is forwarded to the underlying Slider.

Screenshots or mockups

                ┌──────┐
                │  73  │              valueLabelDisplay="auto"
 [icon]  ────────●──────────  [icon]  startIcon / endIcon

 [icon]  ──┼───●───┼───────┼──────    marks + step (snaps to ticks)
          1x1   2x2  3x3   4x4

A src/components/Slider/assets/example.png screenshot should be added with the implementation, as the other components do.

Acceptance criteria

  • Horizontal MUI Slider styled with the tokens in src/stylesheets (no hardcoded colors or sizes), covering default, hover, focus-visible, drag and disabled states.
  • startIcon/endIcon render at the min/max ends, vertically centered with the track and aria-hidden; the track uses the full width when both are omitted.
  • The value tooltip shows the current value on hover, focus and drag, and honours valueLabelDisplay and valueLabelFormat.
  • step snaps the thumb and marks renders ticks, both as a boolean and as an array with labels.
  • Keyboard interaction works (arrows, Page Up/Down, Home/End) and the tooltip follows the thumb.
  • dataTest lands on the slider root; ariaLabel, ariaLabelledBy and ariaDescribedBy are wired following the precedence used in BBBToggle.
  • Storybook a11y addon reports no violations; lint and TypeScript workflows pass.

Implementation checklist

  • src/components/Slider/: component.tsx, types.ts (public props with one-line JSDoc each), styles.ts ($-prefixed transient props, overriding the .MuiSlider-* slots), constants.ts, index.ts, component.stories.tsx, README.md, assets/example.png.
  • Stories: default, end icons, marks/steps (grid size), value label always on, value label off (brightness), custom valueLabelFormat, disabled.
  • src/components/index.ts — export { BBBSlider } from './Slider'; (alphabetical).
  • Root README.md — add - [BBBSlider](./src/components/Slider/README.md) between BBBSelect and BBBSpinner.
  • webpack.config.babel.js — add Slider: './src/components/Slider/index.ts', to entry.
  • package.json exports — add the "./Slider" block, mirroring "./Select".

src/index.ts needs no change: it already re-exports everything from ./components.

Out of scope (possible follow-ups)

Vertical orientation, range slider with two thumbs, clickable end icons, and label/helperText props following the BBBToggle pattern — until then, consumers render their own label and point ariaLabelledBy at it.

Additional context

MUI is already a peer dependency (@mui/material: ^6.1.4 || ^7.0.0), so nothing new is introduced. The implementation commit is a feat scoped to the exported name: feat(BBBSlider): add slider component. MUI reference: https://mui.com/material-ui/react-slider/

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions