Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Slider] Narrow onChange value type #44777

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
4 changes: 2 additions & 2 deletions docs/data/material/components/slider/ContinuousSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import VolumeUp from '@mui/icons-material/VolumeUp';
export default function ContinuousSlider() {
const [value, setValue] = React.useState<number>(30);

const handleChange = (event: Event, newValue: number | number[]) => {
setValue(newValue as number);
const handleChange = (event: Event, newValue: number) => {
setValue(newValue);
};

return (
Expand Down
4 changes: 2 additions & 2 deletions docs/data/material/components/slider/CustomMarks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ const marks = [

export default function CustomMarks() {
const [val, setVal] = React.useState<number>(MIN);
const handleChange = (_: Event, newValue: number | number[]) => {
setVal(newValue as number);
const handleChange = (_: Event, newValue: number) => {
setVal(newValue);
};

return (
Expand Down
4 changes: 2 additions & 2 deletions docs/data/material/components/slider/InputSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const Input = styled(MuiInput)`
export default function InputSlider() {
const [value, setValue] = React.useState(30);

const handleSliderChange = (event: Event, newValue: number | number[]) => {
setValue(newValue as number);
const handleSliderChange = (event: Event, newValue: number) => {
setValue(newValue);
};

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
Expand Down
8 changes: 0 additions & 8 deletions docs/data/material/components/slider/MinimumDistanceSlider.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ export default function MinimumDistanceSlider() {
const [value1, setValue1] = React.useState([20, 37]);

const handleChange1 = (event, newValue, activeThumb) => {
if (!Array.isArray(newValue)) {
return;
}

if (activeThumb === 0) {
setValue1([Math.min(newValue[0], value1[1] - minDistance), value1[1]]);
} else {
Expand All @@ -26,10 +22,6 @@ export default function MinimumDistanceSlider() {
const [value2, setValue2] = React.useState([20, 37]);

const handleChange2 = (event, newValue, activeThumb) => {
if (!Array.isArray(newValue)) {
return;
}

if (newValue[1] - newValue[0] < minDistance) {
if (activeThumb === 0) {
const clamped = Math.min(newValue[0], 100 - minDistance);
Expand Down
22 changes: 3 additions & 19 deletions docs/data/material/components/slider/MinimumDistanceSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,7 @@ const minDistance = 10;
export default function MinimumDistanceSlider() {
const [value1, setValue1] = React.useState<number[]>([20, 37]);

const handleChange1 = (
event: Event,
newValue: number | number[],
activeThumb: number,
) => {
if (!Array.isArray(newValue)) {
return;
}

const handleChange1 = (event: Event, newValue: number[], activeThumb: number) => {
if (activeThumb === 0) {
setValue1([Math.min(newValue[0], value1[1] - minDistance), value1[1]]);
} else {
Expand All @@ -29,15 +21,7 @@ export default function MinimumDistanceSlider() {

const [value2, setValue2] = React.useState<number[]>([20, 37]);

const handleChange2 = (
event: Event,
newValue: number | number[],
activeThumb: number,
) => {
if (!Array.isArray(newValue)) {
return;
}

const handleChange2 = (event: Event, newValue: number[], activeThumb: number) => {
if (newValue[1] - newValue[0] < minDistance) {
if (activeThumb === 0) {
const clamped = Math.min(newValue[0], 100 - minDistance);
Expand All @@ -47,7 +31,7 @@ export default function MinimumDistanceSlider() {
setValue2([clamped - minDistance, clamped]);
}
} else {
setValue2(newValue as number[]);
setValue2(newValue);
}
};

Expand Down
2 changes: 1 addition & 1 deletion docs/data/material/components/slider/MusicPlayerSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export default function MusicPlayerSlider() {
min={0}
step={1}
max={duration}
onChange={(_, value) => setPosition(value as number)}
onChange={(_, value) => setPosition(value)}
sx={(t) => ({
color: 'rgba(0,0,0,0.87)',
height: 4,
Expand Down
4 changes: 1 addition & 3 deletions docs/data/material/components/slider/NonLinearSlider.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ export default function NonLinearSlider() {
const [value, setValue] = React.useState(10);

const handleChange = (event, newValue) => {
if (typeof newValue === 'number') {
setValue(newValue);
}
setValue(newValue);
};

return (
Expand Down
6 changes: 2 additions & 4 deletions docs/data/material/components/slider/NonLinearSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ function calculateValue(value: number) {
export default function NonLinearSlider() {
const [value, setValue] = React.useState<number>(10);

const handleChange = (event: Event, newValue: number | number[]) => {
if (typeof newValue === 'number') {
setValue(newValue);
}
const handleChange = (event: Event, newValue: number) => {
setValue(newValue);
};

return (
Expand Down
4 changes: 2 additions & 2 deletions docs/data/material/components/slider/RangeSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ function valuetext(value: number) {
export default function RangeSlider() {
const [value, setValue] = React.useState<number[]>([20, 37]);

const handleChange = (event: Event, newValue: number | number[]) => {
setValue(newValue as number[]);
const handleChange = (event: Event, newValue: number[]) => {
setValue(newValue);
};

return (
Expand Down
4 changes: 2 additions & 2 deletions docs/pages/material-ui/api/slider.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@
"onChange": {
"type": { "name": "func" },
"signature": {
"type": "function(event: Event, value: number | Array<number>, activeThumb: number) => void",
"type": "function(event: Event, value: Value, activeThumb: number) => void",
"describedArgs": ["event", "value", "activeThumb"]
}
},
"onChangeCommitted": {
"type": { "name": "func" },
"signature": {
"type": "function(event: React.SyntheticEvent | Event, value: number | Array<number>) => void",
"type": "function(event: React.SyntheticEvent | Event, value: Value) => void",
"describedArgs": ["event", "value"]
}
},
Expand Down
2 changes: 1 addition & 1 deletion docs/src/components/productMaterial/MaterialHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ function SlideDemo() {
<Slider
aria-labelledby="temperature-slider"
value={value}
onChange={(_, newValue) => setValue(newValue as number[])}
onChange={(_, newValue) => setValue(newValue)}
/>
<LocalFireDepartment
fontSize="small"
Expand Down
4 changes: 2 additions & 2 deletions docs/src/components/x-grid/EditProgress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export default function EditProgress(props: GridRenderEditCellParams) {
[updateCellEditProps],
);

const handleChange = (event: Event, newValue: number | number[]) => {
setValueState(newValue as number);
const handleChange = (event: Event, newValue: number) => {
setValueState(newValue);
debouncedUpdateCellEditProps(newValue);
};

Expand Down
27 changes: 18 additions & 9 deletions packages/mui-material/src/Slider/Slider.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface SliderOwnerState extends SliderProps {
focusedThumbIndex: number;
}

export interface SliderOwnProps {
export interface SliderOwnProps<Value extends number | number[]> {
/**
* The label of the slider.
*/
Expand Down Expand Up @@ -93,7 +93,7 @@ export interface SliderOwnProps {
/**
* The default value. Use when the component is not controlled.
*/
defaultValue?: number | number[];
defaultValue?: Value;
/**
* If `true`, the component is disabled.
* @default false
Expand Down Expand Up @@ -148,17 +148,17 @@ export interface SliderOwnProps {
* @param {Event} event The event source of the callback.
* You can pull out the new value by accessing `event.target.value` (any).
* **Warning**: This is a generic event not a change event.
* @param {number | number[]} value The new value.
* @param {Value} value The new value.
* @param {number} activeThumb Index of the currently moved thumb.
*/
onChange?: (event: Event, value: number | number[], activeThumb: number) => void;
onChange?: (event: Event, value: Value, activeThumb: number) => void;
/**
* Callback function that is fired when the `mouseup` is triggered.
*
* @param {React.SyntheticEvent | Event} event The event source of the callback. **Warning**: This is a generic event not a change event.
* @param {number | number[]} value The new value.
* @param {Value} value The new value.
*/
onChangeCommitted?: (event: React.SyntheticEvent | Event, value: number | number[]) => void;
onChangeCommitted?: (event: React.SyntheticEvent | Event, value: Value) => void;
/**
* The component orientation.
* @default 'horizontal'
Expand Down Expand Up @@ -246,7 +246,7 @@ export interface SliderOwnProps {
* The value of the slider.
* For ranged sliders, provide an array with two values.
*/
value?: number | number[];
value?: Value;
/**
* Controls when the value label is displayed:
*
Expand Down Expand Up @@ -275,11 +275,20 @@ export interface SliderOwnProps {
export interface SliderTypeMap<
RootComponent extends React.ElementType = 'span',
AdditionalProps = {},
Value extends number | number[] = number | number[],
> {
props: AdditionalProps & SliderOwnProps;
props: AdditionalProps & SliderOwnProps<Value>;
defaultComponent: RootComponent;
}

export type SliderComponent<Value extends number | number[]> = OverridableComponent<
SliderTypeMap<'span', {}, Value>
>;

export type SliderType = SliderComponent<number> &
SliderComponent<number[]> &
SliderComponent<number | number[]>;

export interface SliderValueLabelProps extends React.HTMLAttributes<HTMLSpanElement> {
children: React.ReactElement<unknown>;
index: number;
Expand Down Expand Up @@ -312,7 +321,7 @@ export declare const SliderValueLabel: React.FC<SliderValueLabelProps>;
*
* - [Slider API](https://mui.com/material-ui/api/slider/)
*/
declare const Slider: OverridableComponent<SliderTypeMap>;
declare const Slider: SliderType;

export type SliderProps<
RootComponent extends React.ElementType = SliderTypeMap['defaultComponent'],
Expand Down
4 changes: 2 additions & 2 deletions packages/mui-material/src/Slider/Slider.js
Original file line number Diff line number Diff line change
Expand Up @@ -1037,15 +1037,15 @@ Slider.propTypes /* remove-proptypes */ = {
* @param {Event} event The event source of the callback.
* You can pull out the new value by accessing `event.target.value` (any).
* **Warning**: This is a generic event not a change event.
* @param {number | number[]} value The new value.
* @param {Value} value The new value.
* @param {number} activeThumb Index of the currently moved thumb.
*/
onChange: PropTypes.func,
/**
* Callback function that is fired when the `mouseup` is triggered.
*
* @param {React.SyntheticEvent | Event} event The event source of the callback. **Warning**: This is a generic event not a change event.
* @param {number | number[]} value The new value.
* @param {Value} value The new value.
*/
onChangeCommitted: PropTypes.func,
/**
Expand Down
24 changes: 24 additions & 0 deletions packages/mui-material/src/Slider/Slider.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,27 @@ function testOnChange() {
thumb: ({ orientation }) => ({ className: orientation === 'vertical' ? 'thumb_vertical' : '' }),
}}
/>;

// value, onChange, and onChangeCommitted value type
<Slider
value={5}
onChange={(event, value: number) => {}}
onChangeCommitted={(event, value: number) => {}}
/>;
<Slider
value={[5, 10]}
onChange={(event, value: number[]) => {}}
onChangeCommitted={(event, value: number[]) => {}}
/>;

const CustomComponent: React.FC<{ stringProp: string; numberProp: number }> =
function CustomComponent() {
return <div />;
};

<Slider component="div" />;
<Slider component={CustomComponent} stringProp="a" numberProp={1} />;
/* @ts-expect-error missing stringProp and numberProp */
<Slider component={CustomComponent} />;
/* @ts-expect-error does not allow any prop */
<Slider abc="123" />;
Loading