Repository navigation
Add a standardized Slider component (BBBSlider) #84
Description
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/endIconasReact.ReactNode, decorative andaria-hidden, following theiconprop ofBBBSelect,BBBHintandBBBNavigation. - Value tooltip — MUI's own value label, defaulted to
valueLabelDisplay="auto"(shown on hover, focus and drag) and styled with the library tokens.valueLabelFormatcovers the non-numeric case ('2x2','Low');"off"hides it entirely. - Subdivisions —
stepsnaps the thumb andmarksdraws 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-testattribute on the slider root, followingBBBInputandBBBNavigation.
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 thearia-*/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 derivingmin/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+valueLabelFormatalready 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
Sliderstyled with the tokens insrc/stylesheets(no hardcoded colors or sizes), covering default, hover, focus-visible, drag and disabled states. -
startIcon/endIconrender at themin/maxends, vertically centered with the track andaria-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
valueLabelDisplayandvalueLabelFormat. -
stepsnaps the thumb andmarksrenders 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.
-
dataTestlands on the slider root;ariaLabel,ariaLabelledByandariaDescribedByare wired following the precedence used inBBBToggle. - 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)betweenBBBSelectandBBBSpinner. -
webpack.config.babel.js— addSlider: './src/components/Slider/index.ts',toentry. -
package.jsonexports— 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