diff --git a/packages/@react-aria/slider/src/useSliderThumb.ts b/packages/@react-aria/slider/src/useSliderThumb.ts index bf8f5cfe753..8d3429a14fb 100644 --- a/packages/@react-aria/slider/src/useSliderThumb.ts +++ b/packages/@react-aria/slider/src/useSliderThumb.ts @@ -4,9 +4,9 @@ import {getSliderThumbId, sliderIds} from './utils'; import React, {ChangeEvent, HTMLAttributes, InputHTMLAttributes, LabelHTMLAttributes, RefObject, useCallback, useEffect, useRef} from 'react'; import {SliderState} from '@react-stately/slider'; import {useFocusable} from '@react-aria/focus'; +import {useKeyboard, useMove} from '@react-aria/interactions'; import {useLabel} from '@react-aria/label'; import {useLocale} from '@react-aria/i18n'; -import {useMove} from '@react-aria/interactions'; interface SliderThumbAria { /** Props for the root thumb element; handles the dragging motion. */ @@ -77,24 +77,52 @@ export function useSliderThumb( stateRef.current = state; let reverseX = direction === 'rtl'; let currentPosition = useRef(null); + + let {keyboardProps} = useKeyboard({ + onKeyDown(e) { + let { + getThumbMaxValue, + getThumbMinValue, + pageSize + } = stateRef.current; + // these are the cases that useMove or useSlider don't handle + if (!/^(PageUp|PageDown|Home|End)$/.test(e.key)) { + e.continuePropagation(); + return; + } + // same handling as useMove, stopPropagation to prevent useSlider from handling the event as well. + e.preventDefault(); + // remember to set this so that onChangeEnd is fired + state.setThumbDragging(index, true); + switch (e.key) { + case 'PageUp': + stateRef.current.incrementThumb(index, pageSize); + break; + case 'PageDown': + stateRef.current.decrementThumb(index, pageSize); + break; + case 'Home': + state.setThumbValue(index, getThumbMinValue(index)); + break; + case 'End': + state.setThumbValue(index, getThumbMaxValue(index)); + break; + } + state.setThumbDragging(index, false); + } + }); + let {moveProps} = useMove({ - onMoveStart({pointerType}) { + onMoveStart() { currentPosition.current = null; - // Don't start dragging for keyboard events unless the value has changed. - if (pointerType !== 'keyboard') { - stateRef.current.setThumbDragging(index, true); - } + stateRef.current.setThumbDragging(index, true); }, - onMove({deltaX, deltaY, pointerType}) { + onMove({deltaX, deltaY, pointerType, shiftKey}) { const { - getThumbPercent, - getThumbMaxValue, - getThumbMinValue, - getThumbValue, - setThumbDragging, - setThumbPercent, - setThumbValue, - step + getThumbPercent, + setThumbPercent, + step, + pageSize } = stateRef.current; let size = isVertical ? trackRef.current.offsetHeight : trackRef.current.offsetWidth; @@ -102,19 +130,23 @@ export function useSliderThumb( currentPosition.current = getThumbPercent(index) * size; } if (pointerType === 'keyboard') { - const currentValue = getThumbValue(index); - // (invert left/right according to language direction) + (according to vertical) - const delta = ((reverseX ? -deltaX : deltaX) + (isVertical ? -deltaY : -deltaY)) * step; - if (delta > 0 && currentValue === getThumbMaxValue(index)) { - return; - } - if (delta < 0 && currentValue === getThumbMinValue(index)) { - return; + if (deltaX > 0) { + if (reverseX) { + stateRef.current.decrementThumb(index, shiftKey ? pageSize : step); + } else { + stateRef.current.incrementThumb(index, shiftKey ? pageSize : step); + } + } else if (deltaY < 0) { + stateRef.current.incrementThumb(index, shiftKey ? pageSize : step); + } else if (deltaX < 0) { + if (reverseX) { + stateRef.current.incrementThumb(index, shiftKey ? pageSize : step); + } else { + stateRef.current.decrementThumb(index, shiftKey ? pageSize : step); + } + } else if (deltaY > 0) { + stateRef.current.decrementThumb(index, shiftKey ? pageSize : step); } - currentPosition.current += delta * size; - setThumbDragging(index, true); - setThumbValue(index, currentValue + delta); - setThumbDragging(index, false); } else { let delta = isVertical ? deltaY : deltaX; if (isVertical || reverseX) { @@ -125,11 +157,8 @@ export function useSliderThumb( setThumbPercent(index, clamp(currentPosition.current / size, 0, 1)); } }, - onMoveEnd({pointerType}) { - // Don't end dragging, triggering onChangeEnd, for keyboard events unless the value has changed. - if (pointerType !== 'keyboard') { - stateRef.current.setThumbDragging(index, false); - } + onMoveEnd() { + stateRef.current.setThumbDragging(index, false); } }); @@ -167,8 +196,6 @@ export function useSliderThumb( } }; - let inputting = false; - // We install mouse handlers for the drag motion on the thumb div, but // not the key handler for moving the thumb with the slider. Instead, // we focus the range input, and let the browser handle the keyboard @@ -187,22 +214,12 @@ export function useSliderThumb( 'aria-required': isRequired || undefined, 'aria-invalid': validationState === 'invalid' || undefined, 'aria-errormessage': opts['aria-errormessage'], - onInput: () => { - // Flag native keyboard input. - inputting = true; - }, onChange: (e: ChangeEvent) => { - if (parseFloat(e.target.value) !== state.getThumbValue(index)) { - // On native keyboard input, set thumb dragging so that onChangeEnd will be fired. - inputting && state.setThumbDragging(index, true); - state.setThumbValue(index, parseFloat(e.target.value)); - // After native keyboard input, unset thumb dragging so that onChangeEnd will be fired. - inputting && state.setThumbDragging(index, false); - } - inputting = false; + stateRef.current.setThumbValue(index, parseFloat(e.target.value)); } }), thumbProps: !isDisabled ? mergeProps( + keyboardProps, moveProps, { onMouseDown: (e: React.MouseEvent) => { diff --git a/packages/@react-aria/slider/test/useSliderThumb.test.js b/packages/@react-aria/slider/test/useSliderThumb.test.js index 6117b29407f..6b0a8ed5d95 100644 --- a/packages/@react-aria/slider/test/useSliderThumb.test.js +++ b/packages/@react-aria/slider/test/useSliderThumb.test.js @@ -1,4 +1,4 @@ -import {act, fireEvent, render, screen} from '@testing-library/react'; +import {fireEvent, render, screen} from '@testing-library/react'; import {installMouseEvent, installPointerEvent} from '@react-spectrum/test-utils'; import * as React from 'react'; import {renderHook} from '@testing-library/react-hooks'; @@ -384,6 +384,7 @@ describe('useSliderThumb', () => { // Drag thumb let thumb0 = screen.getByTestId('thumb').firstChild; fireEvent.keyDown(thumb0, {key: 'ArrowRight'}); + fireEvent.keyUp(thumb0, {key: 'ArrowRight'}); expect(onChangeSpy).toHaveBeenLastCalledWith([11]); expect(onChangeSpy).toHaveBeenCalledTimes(1); expect(onChangeEndSpy).toHaveBeenLastCalledWith([11]); @@ -391,47 +392,51 @@ describe('useSliderThumb', () => { expect(stateRef.current.values).toEqual([11]); fireEvent.keyDown(thumb0, {key: 'ArrowLeft'}); + fireEvent.keyUp(thumb0, {key: 'ArrowLeft'}); expect(onChangeSpy).toHaveBeenLastCalledWith([10]); expect(onChangeSpy).toHaveBeenCalledTimes(2); expect(onChangeEndSpy).toHaveBeenLastCalledWith([10]); expect(onChangeEndSpy).toHaveBeenCalledTimes(2); expect(stateRef.current.values).toEqual([10]); + }); - // Note: Unlike ArrowLeft/ArrowRight/ArrowUp/ArrowDown added by the useMove hook, - // Home/End/PageUp/PageDown events are executed natively by the HTML input[type="range"], - // without the need for an additional keyboard event handlers in React Aria. - // This make it harder to test using the testing-library. - - act(() => stateRef.current.setThumbPercent(0, 0)); - - onChangeSpy.mockClear(); - onChangeEndSpy.mockClear(); + it('can be moved with keys at the beginning of the slider', () => { + let onChangeSpy = jest.fn(); + let onChangeEndSpy = jest.fn(); + render(); + let thumb0 = screen.getByTestId('thumb').firstChild; fireEvent.keyDown(thumb0, {key: 'ArrowLeft'}); + fireEvent.keyUp(thumb0, {key: 'ArrowLeft'}); expect(onChangeSpy).not.toHaveBeenCalled(); - expect(onChangeEndSpy).not.toHaveBeenCalled(); + expect(onChangeEndSpy).toHaveBeenCalledWith([0]); fireEvent.keyDown(thumb0, {key: 'ArrowRight'}); + fireEvent.keyUp(thumb0, {key: 'ArrowRight'}); expect(onChangeSpy).toHaveBeenLastCalledWith([1]); expect(onChangeSpy).toHaveBeenCalledTimes(1); expect(onChangeEndSpy).toHaveBeenLastCalledWith([1]); - expect(onChangeEndSpy).toHaveBeenCalledTimes(1); + expect(onChangeEndSpy).toHaveBeenCalledTimes(2); expect(stateRef.current.values).toEqual([1]); + }); - act(() => stateRef.current.setThumbPercent(0, 1)); - - onChangeSpy.mockClear(); - onChangeEndSpy.mockClear(); + it('can be moved with keys at the end of the slider', () => { + let onChangeSpy = jest.fn(); + let onChangeEndSpy = jest.fn(); + render(); + let thumb0 = screen.getByTestId('thumb').firstChild; fireEvent.keyDown(thumb0, {key: 'ArrowRight'}); + fireEvent.keyUp(thumb0, {key: 'ArrowRight'}); expect(onChangeSpy).not.toHaveBeenCalled(); - expect(onChangeEndSpy).not.toHaveBeenCalled(); + expect(onChangeEndSpy).toHaveBeenCalledWith([100]); fireEvent.keyDown(thumb0, {key: 'ArrowLeft'}); + fireEvent.keyUp(thumb0, {key: 'ArrowLeft'}); expect(onChangeSpy).toHaveBeenLastCalledWith([99]); expect(onChangeSpy).toHaveBeenCalledTimes(1); expect(onChangeEndSpy).toHaveBeenLastCalledWith([99]); - expect(onChangeEndSpy).toHaveBeenCalledTimes(1); + expect(onChangeEndSpy).toHaveBeenCalledTimes(2); expect(stateRef.current.values).toEqual([99]); }); @@ -443,53 +448,62 @@ describe('useSliderThumb', () => { // Drag thumb let thumb0 = screen.getByTestId('thumb').firstChild; fireEvent.keyDown(thumb0, {key: 'ArrowRight'}); + fireEvent.keyUp(thumb0, {key: 'ArrowRight'}); expect(onChangeSpy).toHaveBeenLastCalledWith([11]); expect(onChangeSpy).toHaveBeenCalledTimes(1); fireEvent.keyDown(thumb0, {key: 'ArrowUp'}); + fireEvent.keyUp(thumb0, {key: 'ArrowUp'}); expect(onChangeSpy).toHaveBeenLastCalledWith([12]); expect(onChangeSpy).toHaveBeenCalledTimes(2); fireEvent.keyDown(thumb0, {key: 'ArrowDown'}); + fireEvent.keyUp(thumb0, {key: 'ArrowDown'}); expect(onChangeSpy).toHaveBeenLastCalledWith([11]); expect(onChangeSpy).toHaveBeenCalledTimes(3); fireEvent.keyDown(thumb0, {key: 'ArrowLeft'}); + fireEvent.keyUp(thumb0, {key: 'ArrowLeft'}); expect(onChangeSpy).toHaveBeenLastCalledWith([10]); expect(onChangeSpy).toHaveBeenCalledTimes(4); + }); - // Note: Unlike ArrowLeft/ArrowRight/ArrowUp/ArrowDown added by the useMove hook, - // Home/End/PageUp/PageDown events are executed natively by the HTML input[type="range"], - // without the need for an additional keyboard event handlers in React Aria. - // This make it harder to test using the testing-library. - - act(() => stateRef.current.setThumbPercent(0, 0)); - - onChangeSpy.mockClear(); - onChangeEndSpy.mockClear(); + it('can be moved with keys (vertical) at the bottom of the slider', () => { + let onChangeSpy = jest.fn(); + let onChangeEndSpy = jest.fn(); + render(); + // Drag thumb + let thumb0 = screen.getByTestId('thumb').firstChild; fireEvent.keyDown(thumb0, {key: 'ArrowDown'}); + fireEvent.keyUp(thumb0, {key: 'ArrowDown'}); expect(onChangeSpy).not.toHaveBeenCalled(); - expect(onChangeEndSpy).not.toHaveBeenCalled(); + expect(onChangeEndSpy).toHaveBeenCalledWith([0]); fireEvent.keyDown(thumb0, {key: 'ArrowUp'}); + fireEvent.keyUp(thumb0, {key: 'ArrowUp'}); expect(onChangeSpy).toHaveBeenLastCalledWith([1]); expect(onChangeSpy).toHaveBeenCalledTimes(1); expect(onChangeEndSpy).toHaveBeenLastCalledWith([1]); - expect(onChangeEndSpy).toHaveBeenCalledTimes(1); + expect(onChangeEndSpy).toHaveBeenCalledTimes(2); expect(stateRef.current.values).toEqual([1]); + }); - act(() => stateRef.current.setThumbPercent(0, 1)); - - onChangeSpy.mockClear(); - onChangeEndSpy.mockClear(); + it('can be moved with keys (vertical) at the top of the slider', () => { + let onChangeSpy = jest.fn(); + let onChangeEndSpy = jest.fn(); + render(); + // Drag thumb + let thumb0 = screen.getByTestId('thumb').firstChild; fireEvent.keyDown(thumb0, {key: 'ArrowUp'}); + fireEvent.keyUp(thumb0, {key: 'ArrowUp'}); expect(onChangeSpy).not.toHaveBeenCalled(); - expect(onChangeEndSpy).not.toHaveBeenCalled(); + expect(onChangeEndSpy).toHaveBeenCalledWith([100]); fireEvent.keyDown(thumb0, {key: 'ArrowDown'}); + fireEvent.keyUp(thumb0, {key: 'ArrowDown'}); expect(onChangeSpy).toHaveBeenLastCalledWith([99]); expect(onChangeSpy).toHaveBeenCalledTimes(1); expect(onChangeEndSpy).toHaveBeenLastCalledWith([99]); - expect(onChangeEndSpy).toHaveBeenCalledTimes(1); + expect(onChangeEndSpy).toHaveBeenCalledTimes(2); expect(stateRef.current.values).toEqual([99]); }); }); diff --git a/packages/@react-spectrum/slider/test/Slider.test.tsx b/packages/@react-spectrum/slider/test/Slider.test.tsx index 4f0828cb6e4..a038fbc95cc 100644 --- a/packages/@react-spectrum/slider/test/Slider.test.tsx +++ b/packages/@react-spectrum/slider/test/Slider.test.tsx @@ -207,13 +207,17 @@ describe('Slider', function () { }); describe('keyboard interactions', () => { - // Can't test arrow/page up/down, home/end arrows because they are handled by the browser and JSDOM doesn't feel like it. - it.each` Name | props | commands ${'(left/right arrows, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.ArrowRight, result: +1}, {left: press.ArrowLeft, result: -1}]} ${'(left/right arrows, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.ArrowRight, result: -1}, {left: press.ArrowLeft, result: +1}]} ${'(left/right arrows, isDisabled)'} | ${{locale: 'de-DE', isDisabled: true}}| ${[{left: press.ArrowRight, result: 0}, {left: press.ArrowLeft, result: 0}]} + ${'(up/down arrows, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.ArrowUp, result: +1}, {left: press.ArrowDown, result: -1}]} + ${'(up/down arrows, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.ArrowUp, result: +1}, {left: press.ArrowDown, result: -1}]} + ${'(page up/down, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.PageUp, result: +10}, {left: press.PageDown, result: -10}]} + ${'(page up/down, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.PageUp, result: +10}, {left: press.PageDown, result: -10}]} + ${'(home/end, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.Home, result: -50}, {left: press.End, result: +100}]} + ${'(home/end, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.Home, result: -50}, {left: press.End, result: +100}]} `('$Name moves the slider in the correct direction', function ({props, commands}) { let tree = render( @@ -224,14 +228,53 @@ describe('Slider', function () { testKeypresses([slider, slider], commands); }); + it.each` + Name | props | commands + ${'(left/right arrows, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.ArrowRight, result: +1}, {left: press.ArrowLeft, result: -1}]} + ${'(left/right arrows, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.ArrowRight, result: -1}, {left: press.ArrowLeft, result: +1}]} + ${'(left/right arrows, isDisabled)'} | ${{locale: 'de-DE', isDisabled: true}}| ${[{left: press.ArrowRight, result: 0}, {left: press.ArrowLeft, result: 0}]} + ${'(up/down arrows, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.ArrowUp, result: +1}, {left: press.ArrowDown, result: -1}]} + ${'(up/down arrows, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.ArrowUp, result: +1}, {left: press.ArrowDown, result: -1}]} + ${'(page up/down, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.PageUp, result: +10}, {left: press.PageDown, result: -10}]} + ${'(page up/down, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.PageUp, result: +10}, {left: press.PageDown, result: -10}]} + ${'(home/end, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.Home, result: -50}, {left: press.End, result: +100}]} + ${'(home/end, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.Home, result: -50}, {left: press.End, result: +100}]} + `('$Name moves the slider in the correct direction orientation vertical', function ({props, commands}) { + let tree = render( + + + + ); + let slider = tree.getByRole('slider'); + testKeypresses([slider, slider], commands); + }); + it.each` Name | props | commands - ${'(left/right arrows, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.ArrowRight, result: +10}, {left: press.ArrowLeft, result: -10}]} - ${'(left/right arrows, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.ArrowRight, result: -10}, {left: press.ArrowLeft, result: +10}]} + ${'(left/right arrows, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.ArrowRight, result: +20}, {left: press.ArrowLeft, result: -20}]} + ${'(left/right arrows, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.ArrowRight, result: -20}, {left: press.ArrowLeft, result: +20}]} + ${'(page up/down, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.PageUp, result: +20}, {left: press.PageDown, result: -20}]} + ${'(page up/down, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.PageUp, result: +20}, {left: press.PageDown, result: -20}]} `('$Name respects the step size', function ({props, commands}) { let tree = render( - + + + ); + let slider = tree.getByRole('slider'); + testKeypresses([slider, slider], commands); + }); + + it.each` + Name | props | commands + ${'(left/right arrows, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.ArrowRight, result: +1}, {left: press.ArrowLeft, result: -1}]} + ${'(left/right arrows, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.ArrowRight, result: -1}, {left: press.ArrowLeft, result: +1}]} + ${'(page up/down, ltr)'} | ${{locale: 'de-DE'}} | ${[{left: press.PageUp, result: +20}, {left: press.PageDown, result: -20}]} + ${'(page up/down, rtl)'} | ${{locale: 'ar-AE'}} | ${[{left: press.PageUp, result: +20}, {left: press.PageDown, result: -20}]} + `('$Name respects the page size', function ({props, commands}) { + let tree = render( + + ); let slider = tree.getByRole('slider'); diff --git a/packages/@react-spectrum/slider/test/utils.ts b/packages/@react-spectrum/slider/test/utils.ts index 9419dbbdf1f..0fe322f77ab 100644 --- a/packages/@react-spectrum/slider/test/utils.ts +++ b/packages/@react-spectrum/slider/test/utils.ts @@ -14,12 +14,17 @@ import {act, fireEvent} from '@testing-library/react'; function pressKeyOnButton(key, button) { fireEvent.keyDown(button, {key}); + fireEvent.keyUp(button, {key}); } export const press = { ArrowRight: (button: HTMLElement) => pressKeyOnButton('ArrowRight', button), ArrowLeft: (button: HTMLElement) => pressKeyOnButton('ArrowLeft', button), + ArrowUp: (button: HTMLElement) => pressKeyOnButton('ArrowUp', button), + ArrowDown: (button: HTMLElement) => pressKeyOnButton('ArrowDown', button), Home: (button: HTMLElement) => pressKeyOnButton('Home', button), - End: (button: HTMLElement) => pressKeyOnButton('End', button) + End: (button: HTMLElement) => pressKeyOnButton('End', button), + PageUp: (button: HTMLElement) => pressKeyOnButton('PageUp', button), + PageDown: (button: HTMLElement) => pressKeyOnButton('PageDown', button) }; export function testKeypresses([sliderLeft, sliderRight], commands: any[]) { diff --git a/packages/@react-stately/slider/src/useSliderState.ts b/packages/@react-stately/slider/src/useSliderState.ts index 15686e46ddb..64ac5262973 100644 --- a/packages/@react-stately/slider/src/useSliderState.ts +++ b/packages/@react-stately/slider/src/useSliderState.ts @@ -119,10 +119,24 @@ export interface SliderState { */ setThumbEditable(index: number, editable: boolean): void, + /** + * Increments the value of the thumb by the step or page amount. + */ + incrementThumb(index: number, stepSize?: number): void, + /** + * Decrements the value of the thumb by the step or page amount. + */ + decrementThumb(index: number, stepSize?: number): void, + /** * The step amount for the slider. */ - readonly step: number + readonly step: number, + + /** + * The page size for the slider, used to do a bigger step. + */ + readonly pageSize: number } const DEFAULT_MIN_VALUE = 0; @@ -140,7 +154,14 @@ interface SliderStateOptions extends SliderProps { * @param props */ export function useSliderState(props: SliderStateOptions): SliderState { - const {isDisabled, minValue = DEFAULT_MIN_VALUE, maxValue = DEFAULT_MAX_VALUE, numberFormatter: formatter, step = DEFAULT_STEP_VALUE} = props; + const { + isDisabled, + minValue = DEFAULT_MIN_VALUE, + maxValue = DEFAULT_MAX_VALUE, + numberFormatter: formatter, + step = DEFAULT_STEP_VALUE, + pageSize = Math.max((maxValue - minValue) / 10, step) + } = props; const [values, setValues] = useControlledState( props.value as any, @@ -220,6 +241,16 @@ export function useSliderState(props: SliderStateOptions): SliderState { return clamp(getRoundedValue(val), minValue, maxValue); } + function incrementThumb(index: number, stepSize: number = 1) { + let s = Math.max(stepSize, step); + updateValue(index, snapValueToStep(values[index] + s, minValue, maxValue, step)); + } + + function decrementThumb(index: number, stepSize: number = 1) { + let s = Math.max(stepSize, step); + updateValue(index, snapValueToStep(values[index] - s, minValue, maxValue, step)); + } + return { values: values, getThumbValue: (index: number) => values[index], @@ -238,7 +269,10 @@ export function useSliderState(props: SliderStateOptions): SliderState { getPercentValue, isThumbEditable, setThumbEditable, - step + incrementThumb, + decrementThumb, + step, + pageSize }; } diff --git a/packages/@react-types/slider/src/index.d.ts b/packages/@react-types/slider/src/index.d.ts index 146232518f4..ceeed40627d 100644 --- a/packages/@react-types/slider/src/index.d.ts +++ b/packages/@react-types/slider/src/index.d.ts @@ -39,7 +39,12 @@ export interface SliderProps extends RangeInputBase, Value * The slider's step value. * @default 1 */ - step?: number + step?: number, + /** + * The slider's page step value, used when incrementing and decrementing. + * @default 1 + */ + pageSize?: number } export interface SliderThumbProps extends FocusableProps, Validation, LabelableProps {