Merge pull request #33112 from AmbrishRamachandiran/range-slider-component

BUI - Add new range slider component
This commit is contained in:
Charles de Dreuille
2026-03-26 14:37:00 +00:00
committed by GitHub
18 changed files with 918 additions and 3 deletions
@@ -0,0 +1,85 @@
'use client';
import { Slider } from '../../../../../packages/ui/src/components/Slider';
export const SingleValue = () => {
return (
<Slider label="Volume" minValue={0} maxValue={100} defaultValue={50} />
);
};
export const Default = () => {
return (
<Slider
label="Price Range"
minValue={0}
maxValue={1000}
defaultValue={[200, 800]}
/>
);
};
export const WithCustomRange = () => {
return (
<Slider
label="Temperature (°C)"
minValue={-20}
maxValue={40}
defaultValue={[0, 20]}
step={5}
/>
);
};
export const WithFormattedValues = () => {
return (
<Slider
label="Budget"
minValue={0}
maxValue={10000}
defaultValue={[2000, 8000]}
step={100}
formatOptions={{
style: 'currency',
currency: 'USD',
maximumFractionDigits: 0,
}}
/>
);
};
export const WithDescription = () => {
return (
<Slider
label="Age Range"
description="Select the age range for your target audience"
minValue={0}
maxValue={100}
defaultValue={[18, 65]}
/>
);
};
export const Required = () => {
return (
<Slider
label="Score Range"
minValue={0}
maxValue={100}
defaultValue={[20, 80]}
isRequired
/>
);
};
export const Disabled = () => {
return (
<Slider
label="Disabled Range"
minValue={0}
maxValue={100}
defaultValue={[30, 70]}
isDisabled
/>
);
};
+109
View File
@@ -0,0 +1,109 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import { ReactAriaLink } from '@/components/ReactAriaLink';
import { sliderPropDefs } from './props-definition';
import {
snippetUsage,
singleValueSnippet,
defaultSnippet,
withCustomRangeSnippet,
withFormattedValuesSnippet,
withDescriptionSnippet,
requiredSnippet,
disabledSnippet,
} from './snippets';
import {
SingleValue,
Default,
WithCustomRange,
WithFormattedValues,
WithDescription,
Required,
Disabled,
} from './components';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { SliderDefinition } from '../../../utils/definitions';
export const reactAriaUrls = {
slider: 'https://react-spectrum.adobe.com/react-aria/Slider.html',
};
<PageTitle
title="Slider"
description="A slider for selecting numeric values, supporting both single values and ranges with customizable formatting and validation."
/>
<Snippet align="center" py={4} preview={<Default />} code={defaultSnippet} />
## Usage
<CodeBlock code={snippetUsage} />
## API reference
<PropsTable data={sliderPropDefs} />
<ReactAriaLink component="Slider" href={reactAriaUrls.slider} />
## Examples
### Single value
Use a single number as the default value to create a single-thumb slider.
<Snippet
align="center"
py={4}
preview={<SingleValue />}
code={singleValueSnippet}
/>
### Custom range
Define custom minimum, maximum, and step values for specific use cases.
<Snippet
align="center"
py={4}
preview={<WithCustomRange />}
code={withCustomRangeSnippet}
/>
### Formatted values
Use the `formatOptions` prop with standard Intl.NumberFormat options to customize how values are displayed.
<Snippet
align="center"
py={4}
preview={<WithFormattedValues />}
code={withFormattedValuesSnippet}
/>
### With description
Add helpful context with a description below the label.
<Snippet
align="center"
py={4}
preview={<WithDescription />}
code={withDescriptionSnippet}
/>
### Required
Mark a field as required to show a "Required" indicator in the label.
<Snippet align="center" py={4} preview={<Required />} code={requiredSnippet} />
### Disabled
<Snippet align="center" py={4} preview={<Disabled />} code={disabledSnippet} />
<Theming definition={SliderDefinition} />
<ChangelogComponent component="slider" />
@@ -0,0 +1,92 @@
import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
import type { PropDef } from '@/utils/propDefs';
export const sliderPropDefs: Record<string, PropDef> = {
label: {
type: 'string',
description: 'The label text for the slider.',
},
description: {
type: 'string',
description: 'Additional description text displayed below the label.',
},
secondaryLabel: {
type: 'string',
description:
'Optional secondary label displayed next to the main label (e.g., "Optional").',
},
isRequired: {
type: 'boolean',
description:
'Whether the field is required. Displays "Required" in the label if true.',
},
minValue: {
type: 'number',
description: 'The minimum value of the slider.',
default: '0',
},
maxValue: {
type: 'number',
description: 'The maximum value of the slider.',
default: '100',
},
step: {
type: 'number',
description: 'The step increment for slider values.',
default: '1',
},
value: {
type: 'enum',
values: ['number', '[number, number]'],
description:
'Controlled value. Use a single number for a single-thumb slider, or an array [min, max] for a range slider. Use with onChange for controlled behavior.',
},
defaultValue: {
type: 'enum',
values: ['number', '[number, number]'],
description:
'Initial value for uncontrolled usage. Use a single number for a single-thumb slider, or an array [min, max] for a range slider.',
default: 'minValue or [minValue, maxValue]',
},
onChange: {
type: 'enum',
values: ['(value: number | [number, number]) => void'],
description: 'Called when the slider value changes.',
},
onChangeEnd: {
type: 'enum',
values: ['(value: number | [number, number]) => void'],
description:
'Called when the user stops dragging, useful for triggering actions only on final values.',
},
formatOptions: {
type: 'object',
description:
'Intl.NumberFormat options for formatting the displayed value (e.g., { style: "currency", currency: "USD" }).',
},
isDisabled: {
type: 'boolean',
description: 'Prevents user interaction when true.',
},
orientation: {
type: 'enum',
values: ['horizontal', 'vertical'],
description: 'The orientation of the slider.',
default: 'horizontal',
},
name: {
type: 'string',
description: 'Form field name for form submission.',
},
'aria-label': {
type: 'string',
description:
'Accessible label for screen readers when no visible label is provided.',
},
'aria-labelledby': {
type: 'string',
description: 'ID of an element that labels the slider for accessibility.',
},
...classNamePropDefs,
...stylePropDefs,
};
@@ -0,0 +1,67 @@
export const snippetUsage = `import { Slider } from '@backstage/ui';
<Slider
label="My Range"
minValue={0}
maxValue={100}
defaultValue={[25, 75]}
/>`;
export const singleValueSnippet = `<Slider
label="Volume"
minValue={0}
maxValue={100}
defaultValue={50}
/>`;
export const defaultSnippet = `<Slider
label="Price Range"
minValue={0}
maxValue={1000}
defaultValue={[200, 800]}
/>`;
export const withCustomRangeSnippet = `<Slider
label="Temperature (°C)"
minValue={-20}
maxValue={40}
defaultValue={[0, 20]}
step={5}
/>`;
export const withFormattedValuesSnippet = `<Slider
label="Budget"
minValue={0}
maxValue={10000}
defaultValue={[2000, 8000]}
step={100}
formatOptions={{
style: 'currency',
currency: 'USD',
maximumFractionDigits: 0,
}}
/>`;
export const withDescriptionSnippet = `<Slider
label="Age Range"
description="Select the age range for your target audience"
minValue={0}
maxValue={100}
defaultValue={[18, 65]}
/>`;
export const requiredSnippet = `<Slider
label="Score Range"
minValue={0}
maxValue={100}
defaultValue={[20, 80]}
isRequired
/>`;
export const disabledSnippet = `<Slider
label="Disabled Range"
minValue={0}
maxValue={100}
defaultValue={[30, 70]}
isDisabled
/>`;