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

(react-slider) - Updating onChange typing #19412

Merged
merged 5 commits into from
Aug 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/react-slider/etc/react-slider.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export interface SliderCommon extends Omit<React_2.HTMLAttributes<HTMLDivElement
disabled?: boolean;
max?: number;
min?: number;
onChange?: (value: number, ev?: React_2.PointerEvent<HTMLDivElement>) => void;
onChange?: (ev: React_2.PointerEvent<HTMLDivElement> | React_2.KeyboardEvent<HTMLDivElement>, data: {
value: number;
}) => void;
origin?: number;
step?: number;
value?: number;
Expand Down
10 changes: 8 additions & 2 deletions packages/react-slider/src/Slider.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ const useStyles = makeStyles({

export const BasicSliderExample = (props: SliderProps) => {
const [sliderValue, setSliderValue] = React.useState(160);
const sliderOnChange = (value: number) => setSliderValue(value);
const sliderOnChange = (
ev: React.PointerEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>,
data: { value: number },
) => setSliderValue(data.value);

const styles = useStyles();

Expand All @@ -42,7 +45,10 @@ export const BasicSliderExample = (props: SliderProps) => {

export const VerticalSliderExample = (props: SliderProps) => {
const [sliderValue, setSliderValue] = React.useState(160);
const sliderOnChange = (value: number) => setSliderValue(value);
const sliderOnChange = (
ev: React.PointerEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>,
data: { value: number },
) => setSliderValue(data.value);

const styles = useStyles();

Expand Down
34 changes: 4 additions & 30 deletions packages/react-slider/src/components/Slider/Slider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,32 +121,6 @@ describe('Slider', () => {
expect(sliderRef.current.value).toEqual(0);
});

it('clamps provided controlled (value) that is out of bounds', () => {
let sliderRef: any;
let imperativeRef: any;

const SliderTestComponent = () => {
const [sliderValue, setSliderValue] = React.useState(-10);
sliderRef = React.useRef(null);
imperativeRef = React.useRef(null);

React.useImperativeHandle(imperativeRef, () => ({
getSliderValue: () => {
return sliderValue;
},
}));

const onChange = (value: number) => setSliderValue(value);

return <Slider value={sliderValue} min={0} max={100} onChange={onChange} ref={sliderRef} />;
};

render(<SliderTestComponent />);

expect(sliderRef.current.value).toEqual(0);
expect(imperativeRef.current.getSliderValue()).toEqual(0);
});

it('calls (onChange) when dragged', () => {
let sliderRef: any;
const onChange = jest.fn();
Expand All @@ -164,7 +138,7 @@ describe('Slider', () => {
fireEvent.pointerDown(sliderRoot, { clientX: 0, clientY: 0 });

expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange.mock.calls[0][0]).toEqual(0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could change this to expect(onChange.mock.calls[0][1]).toEqual({ value: 0 }) and it would still work (similar elsewhere)

expect(onChange.mock.calls[0][1]).toEqual({ value: 0 });
expect(sliderRef.current.value).toBe(0);
});

Expand All @@ -188,13 +162,13 @@ describe('Slider', () => {
sliderRoot.simulate('pointerdown', { type: 'pointermove', clientX: 110, clientY: 0 });

expect(onChange).toHaveBeenCalledTimes(1);
expect(onChange.mock.calls[0][0]).toEqual(10);
expect(onChange.mock.calls[0][1]).toEqual({ value: 10 });
expect(sliderRef.current.value).toBe(10);

sliderRoot.simulate('pointerdown', { type: 'pointermove', clientX: -10, clientY: 0 });

expect(onChange).toHaveBeenCalledTimes(2);
expect(onChange.mock.calls[1][0]).toEqual(0);
expect(onChange.mock.calls[1][1]).toEqual({ value: 0 });
expect(sliderRef.current.value).toBe(0);

wrapper.unmount();
Expand Down Expand Up @@ -284,7 +258,7 @@ describe('Slider', () => {
fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' });

expect(sliderRef.current.value).toEqual(50);
expect(onChange.mock.calls[2][0]).toEqual(51);
expect(onChange.mock.calls[2][1]).toEqual({ value: 51 });
});

it('handles a negative (step) prop', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ export interface SliderCommon extends Omit<React.HTMLAttributes<HTMLDivElement>,
/**
* Triggers a callback when the value has been changed. This will be called on every individual step.
*/
onChange?: (value: number, ev?: React.PointerEvent<HTMLDivElement>) => void;
onChange?: (
ev: React.PointerEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>,
data: { value: number },
) => void;

/**
* The **Slider's** current value label to be read by the screen reader.
Expand Down
25 changes: 4 additions & 21 deletions packages/react-slider/src/components/Slider/useSliderState.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { useId, useControllableState, useMount } from '@fluentui/react-utilities';
import { useId, useControllableState, useEventCallback } from '@fluentui/react-utilities';
import { SliderSlots, SliderState, SliderCommon } from './Slider.types';

/**
Expand Down Expand Up @@ -54,7 +54,6 @@ export const useSliderState = (state: Pick<SliderState, keyof SliderCommon | key
const railRef = React.useRef<HTMLDivElement>(null);
const thumbRef = React.useRef<HTMLDivElement>(null);
const disposables = React.useRef<(() => void)[]>([]);
const onChangeCallback = React.useRef(onChange);
const id = useId('slider-', state.id);

/**
Expand All @@ -63,8 +62,8 @@ export const useSliderState = (state: Pick<SliderState, keyof SliderCommon | key
* @param ev
* @param incomingValue
*/
const updateValue = React.useCallback(
(incomingValue: number, ev): void => {
const updateValue = useEventCallback(
(incomingValue: number, ev: React.PointerEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>): void => {
const clampedValue = clamp(incomingValue, min, max);

if (clampedValue !== min && clampedValue !== max) {
Expand All @@ -74,13 +73,9 @@ export const useSliderState = (state: Pick<SliderState, keyof SliderCommon | key
}
}

if (onChange && onChangeCallback.current) {
onChangeCallback.current(clampedValue, ev);
}

onChange?.(ev, { value: clampedValue });
setCurrentValue(clampedValue);
},
[max, min, onChange, setCurrentValue],
);

/**
Expand Down Expand Up @@ -183,18 +178,6 @@ export const useSliderState = (state: Pick<SliderState, keyof SliderCommon | key
}
}, [currentValue, state.ref]);

useMount(() => {
// If the user passes an out of bounds controlled value, clamp and update their value onMount.
if (value !== undefined && (value < min || value > max) && onChange && onChangeCallback.current) {
onChangeCallback.current(clamp(value, min, max));

if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.warn('It appears that a controlled Slider has received an unclamped value outside of the min/max.');
}
}
});

const valuePercent = getPercent(currentValue!, min, max);

const originPercent = origin ? getPercent(origin, min, max) : 0;
Expand Down