From c139821469d6cf4383293c35dc7c28762e89f581 Mon Sep 17 00:00:00 2001 From: Mikhail Petrov <44946610+mvp-v@users.noreply.github.com> Date: Sun, 29 Oct 2023 08:00:19 +0100 Subject: [PATCH] feat: support mouse wheel (#597) * feat: support mouse wheel * Fix scroll, add to simple demo --------- Co-authored-by: Mikhail Petrov --- README.md | 10 +++++++ docs/demo/simple.tsx | 5 ++++ src/InputNumber.tsx | 22 ++++++++++++++ tests/wheel.test.tsx | 71 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 tests/wheel.test.tsx diff --git a/README.md b/README.md index ac0d874e..b8085838 100644 --- a/README.md +++ b/README.md @@ -238,6 +238,12 @@ online example: https://input-number.vercel.app/ Specifies the inputmode of input + + wheel + Boolean + true + Allows changing value with mouse wheel + @@ -246,6 +252,10 @@ online example: https://input-number.vercel.app/ * With the Shift key (Shift+⬆, Shift+⬇), the input value will be changed by `10 * step` * With the Ctrl or key (Ctrl+⬆ or ⌘+⬆ or Ctrl+⬇ or ⌘+⬇ ), the input value will be changed by `0.1 * step` +## Mouse Wheel +* When you scroll up or down, the input value will be increased or decreased by `step` +* Scrolling with the Shift key, the input value will be changed by `10 * step` + ## Test Case ``` diff --git a/docs/demo/simple.tsx b/docs/demo/simple.tsx index 1a8da31c..dbfa3762 100644 --- a/docs/demo/simple.tsx +++ b/docs/demo/simple.tsx @@ -7,6 +7,7 @@ export default () => { const [disabled, setDisabled] = React.useState(false); const [readOnly, setReadOnly] = React.useState(false); const [keyboard, setKeyboard] = React.useState(true); + const [wheel, setWheel] = React.useState(true); const [stringMode, setStringMode] = React.useState(false); const [value, setValue] = React.useState(93); @@ -28,6 +29,7 @@ export default () => { readOnly={readOnly} disabled={disabled} keyboard={keyboard} + wheel={wheel} stringMode={stringMode} />

@@ -43,6 +45,9 @@ export default () => { +


diff --git a/src/InputNumber.tsx b/src/InputNumber.tsx index 205096ee..4a64d68c 100644 --- a/src/InputNumber.tsx +++ b/src/InputNumber.tsx @@ -83,6 +83,7 @@ export interface InputNumberProps upHandler?: React.ReactNode; downHandler?: React.ReactNode; keyboard?: boolean; + wheel?: boolean; /** Parse display value to validate number */ parser?: (displayValue: string | undefined) => T; @@ -127,6 +128,7 @@ const InternalInputNumber = React.forwardRef( upHandler, downHandler, keyboard, + wheel, controls = true, classNames, @@ -517,6 +519,26 @@ const InternalInputNumber = React.forwardRef( shiftKeyRef.current = false; }; + React.useEffect(() => { + const onWheel = (event) => { + if (wheel === false) { + return; + }; + // moving mouse wheel rises wheel event with deltaY < 0 + // scroll value grows from top to bottom, as screen Y coordinate + onInternalStep(event.deltaY < 0); + event.preventDefault(); + }; + const input = inputRef.current; + if (input) { + // React onWheel is passive and we can't preventDefault() in it. + // That's why we should subscribe with DOM listener + // https://stackoverflow.com/questions/63663025/react-onwheel-handler-cant-preventdefault-because-its-a-passive-event-listenev + input.addEventListener('wheel', onWheel); + return () => input.removeEventListener('wheel', onWheel); + }; + }, [onInternalStep]); + // >>> Focus & Blur const onBlur = () => { if (changeOnBlur) { diff --git a/tests/wheel.test.tsx b/tests/wheel.test.tsx new file mode 100644 index 00000000..8356129c --- /dev/null +++ b/tests/wheel.test.tsx @@ -0,0 +1,71 @@ +import KeyCode from 'rc-util/lib/KeyCode'; +import InputNumber from '../src'; +import { fireEvent, render } from './util/wrapper'; + +describe('InputNumber.Wheel', () => { + it('wheel up', () => { + const onChange = jest.fn(); + const { container } = render(); + fireEvent.wheel(container.querySelector('input'), {deltaY: -1}); + expect(onChange).toHaveBeenCalledWith(1); + }); + + it('wheel up with pressing shift key', () => { + const onChange = jest.fn(); + const { container } = render(); + fireEvent.keyDown(container.querySelector('input'), { + which: KeyCode.SHIFT, + key: 'Shift', + keyCode: KeyCode.SHIFT, + shiftKey: true, + }); + fireEvent.wheel(container.querySelector('input'), {deltaY: -1}); + expect(onChange).toHaveBeenCalledWith(1.3); + }); + + it('wheel down', () => { + const onChange = jest.fn(); + const { container } = render(); + fireEvent.wheel(container.querySelector('input'), {deltaY: 1}); + expect(onChange).toHaveBeenCalledWith(-1); + }); + + it('wheel down with pressing shift key', () => { + const onChange = jest.fn(); + const { container } = render(); + fireEvent.keyDown(container.querySelector('input'), { + which: KeyCode.SHIFT, + key: 'Shift', + keyCode: KeyCode.SHIFT, + shiftKey: true, + }); + fireEvent.wheel(container.querySelector('input'), {deltaY: 1}); + expect(onChange).toHaveBeenCalledWith(1.1); + }); + + it('disabled wheel', () => { + const onChange = jest.fn(); + const { container } = render(); + + fireEvent.wheel(container.querySelector('input'), {deltaY: -1}); + expect(onChange).not.toHaveBeenCalled(); + + fireEvent.wheel(container.querySelector('input'), {deltaY: 1}); + expect(onChange).not.toHaveBeenCalled(); + }); + + it('wheel is limited to range', () => { + const onChange = jest.fn(); + const { container } = render(); + fireEvent.keyDown(container.querySelector('input'), { + which: KeyCode.SHIFT, + key: 'Shift', + keyCode: KeyCode.SHIFT, + shiftKey: true, + }); + fireEvent.wheel(container.querySelector('input'), {deltaY: -1}); + expect(onChange).toHaveBeenCalledWith(3); + fireEvent.wheel(container.querySelector('input'), {deltaY: 1}); + expect(onChange).toHaveBeenCalledWith(-3); + }); +});