diff --git a/lib/src/number-input/NumberInput.test.js b/lib/src/number-input/NumberInput.test.js index dd337b081..ace5a0d58 100644 --- a/lib/src/number-input/NumberInput.test.js +++ b/lib/src/number-input/NumberInput.test.js @@ -49,7 +49,7 @@ describe("Number input component tests", () => { await userEvent.click(increment); expect(number.value).toBe(""); }); - + test("Number input is read only and cannot be incremented or decremented using the arrow keys", () => { const { getByLabelText } = render(); const number = getByLabelText("Number label"); @@ -68,7 +68,9 @@ describe("Number input component tests", () => { test("Number input is not optional: required field, displays error if not filled in", () => { const onBlur = jest.fn(); const onChange = jest.fn(); - const { getByLabelText } = render(); + const { getByLabelText } = render( + + ); const number = getByLabelText("Number input label"); userEvent.type(number, "1"); userEvent.clear(number); @@ -381,6 +383,90 @@ describe("Number input component tests", () => { expect(number.value).toBe("5"); }); + test("Value is unchanged when using the scroll wheel in mouse in a disabled input", () => { + const { getByLabelText } = render( + + ); + const number = getByLabelText("Number input label"); + fireEvent.wheel(number, { deltaY: -100 }); + expect(number.value).toBe("10"); + fireEvent.wheel(number, { deltaY: 100 }); + expect(number.value).toBe("10"); + fireEvent.wheel(number, { deltaY: -100 }); + expect(number.value).toBe("10"); + fireEvent.wheel(number, { deltaY: 100 }); + expect(number.value).toBe("10"); + }); + + test("Value is unchanged when using the arrows in keyboard in a disabled input", () => { + const { getByLabelText } = render( + + ); + const number = getByLabelText("Number input label"); + fireEvent.keyDown(number, { keyCode: 38 }); + expect(number.value).toBe("10"); + fireEvent.keyDown(number, { keyCode: 40 }); + expect(number.value).toBe("10"); + fireEvent.keyDown(number, { keyCode: 38 }); + expect(number.value).toBe("10"); + fireEvent.keyDown(number, { keyCode: 40 }); + expect(number.value).toBe("10"); + }); + + test("Value is unchanged when using the scroll wheel in mouse in a read-only input", () => { + const { getByLabelText } = render( + + ); + const number = getByLabelText("Number input label"); + fireEvent.wheel(number, { deltaY: -100 }); + expect(number.value).toBe("10"); + fireEvent.wheel(number, { deltaY: 100 }); + expect(number.value).toBe("10"); + fireEvent.wheel(number, { deltaY: -100 }); + expect(number.value).toBe("10"); + fireEvent.wheel(number, { deltaY: 100 }); + expect(number.value).toBe("10"); + }); + + test("Value is unchanged when using the arrows in keyboard in a read-only input", () => { + const { getByLabelText } = render( + + ); + const number = getByLabelText("Number input label"); + fireEvent.keyDown(number, { keyCode: 38 }); + expect(number.value).toBe("10"); + fireEvent.keyDown(number, { keyCode: 40 }); + expect(number.value).toBe("10"); + fireEvent.keyDown(number, { keyCode: 38 }); + expect(number.value).toBe("10"); + fireEvent.keyDown(number, { keyCode: 40 }); + expect(number.value).toBe("10"); + }); + + test("Increment and decrement the value with min, max and step using the scroll wheel in mouse", () => { + const { getByLabelText } = render(); + const number = getByLabelText("Number input label"); + userEvent.type(number, "1"); + fireEvent.wheel(number, { deltaY: -100 }); + expect(number.value).toBe("5"); + fireEvent.wheel(number, { deltaY: -100 }); + expect(number.value).toBe("10"); + fireEvent.wheel(number, { deltaY: -100 }); + expect(number.value).toBe("15"); + fireEvent.wheel(number, { deltaY: -100 }); + expect(number.value).toBe("20"); + fireEvent.wheel(number, { deltaY: -100 }); + expect(number.value).toBe("20"); + fireEvent.wheel(number, { deltaY: 100 }); + expect(number.value).toBe("15"); + fireEvent.wheel(number, { deltaY: 100 }); + expect(number.value).toBe("10"); + fireEvent.wheel(number, { deltaY: 100 }); + expect(number.value).toBe("5"); + fireEvent.wheel(number, { deltaY: 100 }); + expect(number.value).toBe("5"); + }); + test("Number has correct accessibility attributes", () => { const { getByLabelText, getAllByRole } = render(); const number = getByLabelText("Number input label"); diff --git a/lib/src/number-input/NumberInput.tsx b/lib/src/number-input/NumberInput.tsx index af4122fe0..91057a2d1 100644 --- a/lib/src/number-input/NumberInput.tsx +++ b/lib/src/number-input/NumberInput.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import styled from "styled-components"; import DxcTextInput from "../text-input/TextInput"; import NumberInputPropsType, { RefType } from "./types"; @@ -38,33 +38,49 @@ const DxcNumberInput = React.forwardRef( tabIndex, }, ref - ) => ( - - - - - - ) + ) => { + const numberInputRef = React.useRef(null); + + useEffect(() => { + const input = numberInputRef.current?.getElementsByTagName("input")[0] as HTMLInputElement; + const preventDefault = (event: WheelEvent) => { + event.preventDefault(); + }; + + input?.addEventListener("wheel", preventDefault, { passive: false }); + return () => { + input?.removeEventListener("wheel", preventDefault); + }; + }, []); + + return ( + + + + + + ); + } ); const NumberInputContainer = styled.div` diff --git a/lib/src/text-input/TextInput.test.js b/lib/src/text-input/TextInput.test.js index e3588c084..4c8988903 100644 --- a/lib/src/text-input/TextInput.test.js +++ b/lib/src/text-input/TextInput.test.js @@ -462,6 +462,17 @@ describe("TextInput component tests", () => { const options = getAllByRole("option"); expect(options[0].getAttribute("aria-selected")).toBeNull(); }); + + test("Mouse wheel interaction does not affect the text value", () => { + const { getByRole } = render( + + ); + const input = getByRole("textbox"); + fireEvent.wheel(input, { deltaY: -100 }); + expect(input.value).toBe("Example text"); + fireEvent.wheel(input, { deltaY: 100 }); + expect(input.value).toBe("Example text"); + }); }); describe("TextInput component synchronous autosuggest tests", () => { diff --git a/lib/src/text-input/TextInput.tsx b/lib/src/text-input/TextInput.tsx index d5009480d..58b429782 100644 --- a/lib/src/text-input/TextInput.tsx +++ b/lib/src/text-input/TextInput.tsx @@ -168,6 +168,43 @@ const DxcTextInput = React.forwardRef( else onChange?.({ value: formattedValue }); }; + const decrementNumber = (currentValue = value ?? innerValue) => { + if (!disabled && !readOnly) { + const numberValue = Number(currentValue); + const steppedValue = Math.round((numberValue - numberInputContext?.stepNumber + Number.EPSILON) * 100) / 100; + + if (currentValue !== "") { + if (numberValue < numberInputContext?.minNumber || steppedValue < numberInputContext?.minNumber) + changeValue(numberValue); + else if (numberValue > numberInputContext?.maxNumber) changeValue(numberInputContext?.maxNumber); + else if (numberValue === numberInputContext?.minNumber) changeValue(numberInputContext?.minNumber); + else changeValue(steppedValue); + } else { + if (numberInputContext?.minNumber >= 0) changeValue(numberInputContext?.minNumber); + else if (numberInputContext?.maxNumber < 0) changeValue(numberInputContext?.maxNumber); + else changeValue(-numberInputContext.stepNumber); + } + } + }; + const incrementNumber = (currentValue = value ?? innerValue) => { + if (!disabled && !readOnly) { + const numberValue = Number(currentValue); + const steppedValue = Math.round((numberValue + numberInputContext?.stepNumber + Number.EPSILON) * 100) / 100; + + if (currentValue !== "") { + if (numberValue > numberInputContext?.maxNumber || steppedValue > numberInputContext?.maxNumber) + changeValue(numberValue); + else if (numberValue < numberInputContext?.minNumber) changeValue(numberInputContext?.minNumber); + else if (numberValue === numberInputContext?.maxNumber) changeValue(numberInputContext?.maxNumber); + else changeValue(steppedValue); + } else { + if (numberInputContext?.minNumber > 0) changeValue(numberInputContext?.minNumber); + else if (numberInputContext?.maxNumber <= 0) changeValue(numberInputContext?.maxNumber); + else changeValue(numberInputContext.stepNumber); + } + } + }; + const handleInputContainerOnClick = () => { document.activeElement !== actionRef.current && inputRef.current.focus(); }; @@ -248,12 +285,10 @@ const DxcTextInput = React.forwardRef( break; } }; - const handleWheel = useCallback((event: WheelEvent) => { - if (document.activeElement === inputRef.current) { - event.preventDefault(); + const handleNumberInputWheel = (event: React.WheelEvent) => { + if (document.activeElement === inputRef.current) event.deltaY < 0 ? incrementNumber(inputRef.current.value) : decrementNumber(inputRef.current.value); - } - }, []); + }; const handleClearActionOnClick = () => { changeValue(""); @@ -276,38 +311,6 @@ const DxcTextInput = React.forwardRef( inputRef?.current?.setAttribute("step", step); inputRef?.current?.setAttribute("type", type); }; - const decrementNumber = (currentValue = value ?? innerValue) => { - const numberValue = Number(currentValue); - const steppedValue = Math.round((numberValue - numberInputContext?.stepNumber + Number.EPSILON) * 100) / 100; - - if (currentValue !== "") { - if (numberValue < numberInputContext?.minNumber || steppedValue < numberInputContext?.minNumber) - changeValue(numberValue); - else if (numberValue > numberInputContext?.maxNumber) changeValue(numberInputContext?.maxNumber); - else if (numberValue === numberInputContext?.minNumber) changeValue(numberInputContext?.minNumber); - else changeValue(steppedValue); - } else { - if (numberInputContext?.minNumber >= 0) changeValue(numberInputContext?.minNumber); - else if (numberInputContext?.maxNumber < 0) changeValue(numberInputContext?.maxNumber); - else changeValue(-numberInputContext.stepNumber); - } - }; - const incrementNumber = (currentValue = value ?? innerValue) => { - const numberValue = Number(currentValue); - const steppedValue = Math.round((numberValue + numberInputContext?.stepNumber + Number.EPSILON) * 100) / 100; - - if (currentValue !== "") { - if (numberValue > numberInputContext?.maxNumber || steppedValue > numberInputContext?.maxNumber) - changeValue(numberValue); - else if (numberValue < numberInputContext?.minNumber) changeValue(numberInputContext?.minNumber); - else if (numberValue === numberInputContext?.maxNumber) changeValue(numberInputContext?.maxNumber); - else changeValue(steppedValue); - } else { - if (numberInputContext?.minNumber > 0) changeValue(numberInputContext?.minNumber); - else if (numberInputContext?.maxNumber <= 0) changeValue(numberInputContext?.maxNumber); - else changeValue(numberInputContext.stepNumber); - } - }; useEffect(() => { if (typeof suggestions === "function") { @@ -348,16 +351,6 @@ const DxcTextInput = React.forwardRef( ); }, [value, innerValue, suggestions, numberInputContext]); - useEffect(() => { - const input = inputRef.current; - - input.addEventListener('wheel', handleWheel, { passive: false }); - - return () => { - input.removeEventListener('wheel', handleWheel); - }; - }, [handleWheel]); - return ( @@ -428,6 +421,7 @@ const DxcTextInput = React.forwardRef( onMouseDown={(event) => { event.stopPropagation(); }} + onWheel={numberInputContext?.typeNumber === "number" ? handleNumberInputWheel : undefined} disabled={disabled} readOnly={readOnly} ref={inputRef}