Skip to content

Commit 41e5bf5

Browse files
authored
Only scroll step when NumberField has focus (#1615)
1 parent 887fff7 commit 41e5bf5

File tree

2 files changed

+32
-11
lines changed

2 files changed

+32
-11
lines changed

packages/@react-aria/numberfield/src/useNumberField.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ import {
2020
useCallback,
2121
useEffect,
2222
useMemo,
23-
useRef
23+
useRef,
24+
useState
2425
} from 'react';
2526
// @ts-ignore
2627
import intlMessages from '../intl/*.json';
2728
import {isAndroid, isIOS, isIPhone, mergeProps, useId} from '@react-aria/utils';
2829
import {NumberFieldState} from '@react-stately/numberfield';
2930
import {TextInputDOMProps} from '@react-types/shared';
30-
import {useFocus} from '@react-aria/interactions';
31+
import {useFocus, useFocusWithin} from '@react-aria/interactions';
3132
import {
3233
useMessageFormatter,
3334
useNumberFormatter
@@ -115,10 +116,12 @@ export function useNumberField(props: AriaNumberFieldProps, state: NumberFieldSt
115116
}
116117
);
117118

119+
let [focusWithin, setFocusWithin] = useState(false);
120+
let {focusWithinProps} = useFocusWithin({isDisabled, onFocusWithinChange: setFocusWithin});
121+
118122
let onWheel = useCallback((e) => {
119-
// If the input isn't supposed to receive input, do nothing.
120123
// If the ctrlKey is pressed, this is a zoom event, do nothing.
121-
if (isDisabled || isReadOnly || e.ctrlKey) {
124+
if (e.ctrlKey) {
122125
return;
123126
}
124127

@@ -130,8 +133,10 @@ export function useNumberField(props: AriaNumberFieldProps, state: NumberFieldSt
130133
} else if (e.deltaY < 0) {
131134
decrement();
132135
}
133-
}, [isReadOnly, isDisabled, decrement, increment]);
134-
useScrollWheel({onScroll: onWheel, capture: false}, inputRef);
136+
}, [decrement, increment]);
137+
// If the input isn't supposed to receive input, disable scrolling.
138+
let scrollingDisabled = isDisabled || isReadOnly || !focusWithin;
139+
useScrollWheel({onScroll: onWheel, capture: false, isDisabled: scrollingDisabled}, inputRef);
135140

136141
// The inputMode attribute influences the software keyboard that is shown on touch devices.
137142
// Browsers and operating systems are quite inconsistent about what keys are available, however.
@@ -374,7 +379,8 @@ export function useNumberField(props: AriaNumberFieldProps, state: NumberFieldSt
374379
groupProps: {
375380
role: 'group',
376381
'aria-disabled': isDisabled,
377-
'aria-invalid': validationState === 'invalid' ? 'true' : undefined
382+
'aria-invalid': validationState === 'invalid' ? 'true' : undefined,
383+
...focusWithinProps
378384
},
379385
labelProps,
380386
inputProps,
@@ -384,13 +390,18 @@ export function useNumberField(props: AriaNumberFieldProps, state: NumberFieldSt
384390
}
385391

386392
// scroll wheel needs to be added not passively so it's cancelable, small helper hook to remember that
387-
function useScrollWheel({onScroll, capture}: {onScroll: (e) => void, capture: boolean}, ref: RefObject<HTMLElement>) {
393+
function useScrollWheel({onScroll, capture, isDisabled}: {onScroll: (e) => void, capture: boolean, isDisabled: boolean}, ref: RefObject<HTMLElement>) {
388394
useEffect(() => {
389395
let elem = ref.current;
390-
elem.addEventListener('wheel', onScroll, capture);
396+
397+
if (!isDisabled) {
398+
elem.addEventListener('wheel', onScroll, capture);
399+
}
391400

392401
return () => {
393-
elem.removeEventListener('wheel', onScroll, capture);
402+
if (!isDisabled) {
403+
elem.removeEventListener('wheel', onScroll, capture);
404+
}
394405
};
395-
}, [onScroll, ref, capture]);
406+
}, [onScroll, ref, capture, isDisabled]);
396407
}

packages/@react-spectrum/numberfield/test/NumberField.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,16 @@ describe('NumberField', function () {
395395
act(() => {textField.blur();});
396396
});
397397

398+
it.each`
399+
Name
400+
${'NumberField'}
401+
`('$Name cannot scroll to step when not focused', () => {
402+
let {textField} = renderNumberField({defaultValue: 0, onChange: onChangeSpy});
403+
404+
fireEvent.wheel(textField, {deltaY: -10});
405+
expect(onChangeSpy).not.toHaveBeenCalled();
406+
});
407+
398408
it.each`
399409
Name
400410
${'NumberField'}

0 commit comments

Comments
 (0)