BUI - Add new range slider component
Signed-off-by: AmbrishRamachandiran <ambrish.r@infosys.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
'use client';
|
||||
|
||||
import { RangeSlider } from '../../../../../packages/ui/src/components/RangeSlider/RangeSlider';
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<RangeSlider
|
||||
label="Price Range"
|
||||
minValue={0}
|
||||
maxValue={1000}
|
||||
defaultValue={[200, 800]}
|
||||
showValueLabel
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithCustomRange = () => {
|
||||
return (
|
||||
<RangeSlider
|
||||
label="Temperature (°C)"
|
||||
minValue={-20}
|
||||
maxValue={40}
|
||||
defaultValue={[0, 20]}
|
||||
step={5}
|
||||
showValueLabel
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithFormattedValues = () => {
|
||||
return (
|
||||
<RangeSlider
|
||||
label="Budget"
|
||||
minValue={0}
|
||||
maxValue={10000}
|
||||
defaultValue={[2000, 8000]}
|
||||
step={100}
|
||||
showValueLabel
|
||||
formatValue={(value: number) => `$${value.toLocaleString()}`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithDescription = () => {
|
||||
return (
|
||||
<RangeSlider
|
||||
label="Age Range"
|
||||
description="Select the age range for your target audience"
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
defaultValue={[18, 65]}
|
||||
showValueLabel
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Required = () => {
|
||||
return (
|
||||
<RangeSlider
|
||||
label="Score Range"
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
defaultValue={[20, 80]}
|
||||
isRequired
|
||||
showValueLabel
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Disabled = () => {
|
||||
return (
|
||||
<RangeSlider
|
||||
label="Disabled Range"
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
defaultValue={[30, 70]}
|
||||
isDisabled
|
||||
showValueLabel
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,101 @@
|
||||
import { PropsTable } from '@/components/PropsTable';
|
||||
import { Snippet } from '@/components/Snippet';
|
||||
import { CodeBlock } from '@/components/CodeBlock';
|
||||
import { ReactAriaLink } from '@/components/ReactAriaLink';
|
||||
import { rangeSliderPropDefs } from './props-definition';
|
||||
import {
|
||||
snippetUsage,
|
||||
defaultSnippet,
|
||||
withCustomRangeSnippet,
|
||||
withFormattedValuesSnippet,
|
||||
withDescriptionSnippet,
|
||||
requiredSnippet,
|
||||
disabledSnippet,
|
||||
} from './snippets';
|
||||
import {
|
||||
Default,
|
||||
WithCustomRange,
|
||||
WithFormattedValues,
|
||||
WithDescription,
|
||||
Required,
|
||||
Disabled,
|
||||
} from './components';
|
||||
import { PageTitle } from '@/components/PageTitle';
|
||||
import { Theming } from '@/components/Theming';
|
||||
import { ChangelogComponent } from '@/components/ChangelogComponent';
|
||||
import { RangeSliderDefinition } from '../../../utils/definitions';
|
||||
|
||||
export const reactAriaUrls = {
|
||||
slider: 'https://react-spectrum.adobe.com/react-aria/Slider.html',
|
||||
};
|
||||
|
||||
<PageTitle
|
||||
title="RangeSlider"
|
||||
description="A dual-thumb slider for selecting a numeric range with customizable formatting and validation."
|
||||
/>
|
||||
|
||||
<Snippet align="center" py={4} preview={<Default />} code={defaultSnippet} />
|
||||
|
||||
## Usage
|
||||
|
||||
<CodeBlock code={snippetUsage} />
|
||||
|
||||
## API reference
|
||||
|
||||
<PropsTable data={rangeSliderPropDefs} />
|
||||
|
||||
<ReactAriaLink component="Slider" href={reactAriaUrls.slider} />
|
||||
|
||||
## Examples
|
||||
|
||||
### 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 `formatValue` prop 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={RangeSliderDefinition} />
|
||||
|
||||
<ChangelogComponent component="range-slider" />
|
||||
@@ -0,0 +1,98 @@
|
||||
import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
|
||||
import type { PropDef } from '@/utils/propDefs';
|
||||
|
||||
export const rangeSliderPropDefs: Record<string, PropDef> = {
|
||||
label: {
|
||||
type: 'string',
|
||||
description: 'The label text for the range 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 range.',
|
||||
default: '0',
|
||||
},
|
||||
maxValue: {
|
||||
type: 'number',
|
||||
description: 'The maximum value of the slider range.',
|
||||
default: '100',
|
||||
},
|
||||
step: {
|
||||
type: 'number',
|
||||
description: 'The step increment for slider values.',
|
||||
default: '1',
|
||||
},
|
||||
value: {
|
||||
type: 'enum',
|
||||
values: ['number[]'],
|
||||
description:
|
||||
'Controlled value as an array [min, max]. Use with onChange for controlled behavior.',
|
||||
},
|
||||
defaultValue: {
|
||||
type: 'enum',
|
||||
values: ['number[]'],
|
||||
description: 'Initial value as an array [min, max] for uncontrolled usage.',
|
||||
default: '[0, 100]',
|
||||
},
|
||||
onChange: {
|
||||
type: 'enum',
|
||||
values: ['(value: number[]) => void'],
|
||||
description: 'Called when the slider range changes.',
|
||||
},
|
||||
onChangeEnd: {
|
||||
type: 'enum',
|
||||
values: ['(value: number[]) => void'],
|
||||
description:
|
||||
'Called when the user stops dragging, useful for triggering actions only on final values.',
|
||||
},
|
||||
showValueLabel: {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Whether to display the current range values above the slider.',
|
||||
default: 'false',
|
||||
},
|
||||
formatValue: {
|
||||
type: 'enum',
|
||||
values: ['(value: number) => string'],
|
||||
description:
|
||||
'Custom formatter function for displaying values (e.g., adding currency symbols).',
|
||||
},
|
||||
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,62 @@
|
||||
export const snippetUsage = `import { RangeSlider } from '@backstage/ui';
|
||||
|
||||
<RangeSlider
|
||||
label="My Range"
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
defaultValue={[25, 75]}
|
||||
/>`;
|
||||
|
||||
export const defaultSnippet = `<RangeSlider
|
||||
label="Price Range"
|
||||
minValue={0}
|
||||
maxValue={1000}
|
||||
defaultValue={[200, 800]}
|
||||
showValueLabel
|
||||
/>`;
|
||||
|
||||
export const withCustomRangeSnippet = `<RangeSlider
|
||||
label="Temperature (°C)"
|
||||
minValue={-20}
|
||||
maxValue={40}
|
||||
defaultValue={[0, 20]}
|
||||
step={5}
|
||||
showValueLabel
|
||||
/>`;
|
||||
|
||||
export const withFormattedValuesSnippet = `<RangeSlider
|
||||
label="Budget"
|
||||
minValue={0}
|
||||
maxValue={10000}
|
||||
defaultValue={[2000, 8000]}
|
||||
step={100}
|
||||
showValueLabel
|
||||
formatValue={(value: number) => \`$\${value.toLocaleString()}\`}
|
||||
/>`;
|
||||
|
||||
export const withDescriptionSnippet = `<RangeSlider
|
||||
label="Age Range"
|
||||
description="Select the age range for your target audience"
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
defaultValue={[18, 65]}
|
||||
showValueLabel
|
||||
/>`;
|
||||
|
||||
export const requiredSnippet = `<RangeSlider
|
||||
label="Score Range"
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
defaultValue={[20, 80]}
|
||||
isRequired
|
||||
showValueLabel
|
||||
/>`;
|
||||
|
||||
export const disabledSnippet = `<RangeSlider
|
||||
label="Disabled Range"
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
defaultValue={[30, 70]}
|
||||
isDisabled
|
||||
showValueLabel
|
||||
/>`;
|
||||
@@ -85,6 +85,11 @@ export const components: Page[] = [
|
||||
title: 'RadioGroup',
|
||||
slug: 'radio-group',
|
||||
},
|
||||
{
|
||||
title: 'RangeSlider',
|
||||
slug: 'range-slider',
|
||||
status: 'new',
|
||||
},
|
||||
{
|
||||
title: 'SearchField',
|
||||
slug: 'search-field',
|
||||
|
||||
Reference in New Issue
Block a user