diff --git a/apps/website/screens/common/example/Example.tsx b/apps/website/screens/common/example/Example.tsx index f03270bd2a..9f679ba82b 100644 --- a/apps/website/screens/common/example/Example.tsx +++ b/apps/website/screens/common/example/Example.tsx @@ -46,9 +46,10 @@ type ExamplePropTypes = { }; }; -const Example = ({ actionsVisible = true, defaultIsVisible = false, example }: ExamplePropTypes): JSX.Element => { +const Example = ({ actionsVisible = true, defaultIsVisible = false, example }: ExamplePropTypes) => { const toast = useToast(); const [isCodeVisible, changeIsCodeVisible] = useState(defaultIsVisible); + const [liveCode, setLiveCode] = useState(example.code); const handleCodeOnClick = () => { changeIsCodeVisible(!isCodeVisible); @@ -56,7 +57,7 @@ const Example = ({ actionsVisible = true, defaultIsVisible = false, example }: E const handleCopy = () => { navigator.clipboard - .writeText(example.code) + .writeText(liveCode) .then(() => { toast.success({ message: "Code copied to the clipboard." }); }) @@ -67,7 +68,7 @@ const Example = ({ actionsVisible = true, defaultIsVisible = false, example }: E return ( - + @@ -87,7 +88,7 @@ const Example = ({ actionsVisible = true, defaultIsVisible = false, example }: E )} {isCodeVisible && ( - + )} diff --git a/apps/website/screens/components/number-input/code/NumberInputCodePage.tsx b/apps/website/screens/components/number-input/code/NumberInputCodePage.tsx index 3a5e8afb98..9536cda0a0 100644 --- a/apps/website/screens/components/number-input/code/NumberInputCodePage.tsx +++ b/apps/website/screens/components/number-input/code/NumberInputCodePage.tsx @@ -8,6 +8,7 @@ import controlled from "./examples/controlled"; import uncontrolled from "./examples/uncontrolled"; import errorUsage from "./examples/errorHandling"; import TableCode from "@/common/TableCode"; +import StatusBadge from "@/common/StatusBadge"; const sections = [ { @@ -266,7 +267,27 @@ const sections = [ Specifies a string to be used as the name for the number input element when no label is provided. - 'Number input' + + 'Number input' + + + + + + + showControls + + + + boolean + + + Decides whether the number input has actions to increase or decrease the value, following the defined + step. + + + true + diff --git a/packages/lib/src/number-input/NumberInput.stories.tsx b/packages/lib/src/number-input/NumberInput.stories.tsx index 07a818b5f8..f8039f6138 100644 --- a/packages/lib/src/number-input/NumberInput.stories.tsx +++ b/packages/lib/src/number-input/NumberInput.stories.tsx @@ -15,6 +15,10 @@ const NumberInput = () => ( <DxcNumberInput /> </ExampleContainer> + <ExampleContainer> + <Title title="Without controls" theme="light" level={4} /> + <DxcNumberInput showControls={false} /> + </ExampleContainer> <ExampleContainer> <Title title="With label and placeholder" theme="light" level={4} /> <DxcNumberInput label="Number input" placeholder="Placeholder" /> diff --git a/packages/lib/src/number-input/NumberInput.test.tsx b/packages/lib/src/number-input/NumberInput.test.tsx index 7e755de0e5..c49170b2b3 100644 --- a/packages/lib/src/number-input/NumberInput.test.tsx +++ b/packages/lib/src/number-input/NumberInput.test.tsx @@ -24,13 +24,11 @@ describe("Number input component tests", () => { expect(number.getAttribute("placeholder")).toBe("Placeholder"); expect(queryAllByRole("button").length).toBe(2); }); - test("Number input is disabled", () => { const { getByLabelText } = render(<DxcNumberInput label="Number label" disabled />); const number = getByLabelText("Number label") as HTMLInputElement; expect(number.disabled).toBeTruthy(); }); - test("Number input is read only and cannot be incremented or decremented using the actions", async () => { const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number label" readOnly />); const number = getByLabelText("Number label") as HTMLInputElement; @@ -42,7 +40,6 @@ describe("Number input component tests", () => { increment && (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(<DxcNumberInput label="Number label" readOnly />); const number = getByLabelText("Number label") as HTMLInputElement; @@ -52,12 +49,10 @@ describe("Number input component tests", () => { fireEvent.keyDown(number, { keyCode: 40 }); expect(number.value).toBe(""); }); - test("Number input is optional", () => { const { getByText } = render(<DxcNumberInput label="Number label" optional />); expect(getByText("(Optional)")).toBeTruthy(); }); - test("Number input is not optional: required field, displays error if not filled in", () => { const onBlur = jest.fn(); const onChange = jest.fn(); @@ -73,18 +68,19 @@ describe("Number input component tests", () => { expect(onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, enter a value." }); }); - + test("Hiding number input controls", () => { + const { queryByRole } = render(<DxcNumberInput label="Number label" showControls={false} />); + expect(queryByRole("button")).toBeFalsy(); + }); test("Suffix and prefix must be shown", () => { const { getByText } = render(<DxcNumberInput label="Number input label" prefix="+34" suffix="USD" />); expect(getByText("+34")).toBeTruthy(); expect(getByText("USD")).toBeTruthy(); }); - test("Invalid number input renders error", () => { const { getByText } = render(<DxcNumberInput error="Error message." />); expect(getByText("Error message.")).toBeTruthy(); }); - test("onChange function is called correctly", () => { const onChange = jest.fn(); const { getByLabelText } = render(<DxcNumberInput label="Number input label" onChange={onChange} />); @@ -96,7 +92,6 @@ describe("Number input component tests", () => { expect(onChange).toHaveBeenCalledWith({ value: "1" }); expect(number.value).toBe("1"); }); - test("Error message is shown if the typed value is less than the min value", () => { const onChange = jest.fn(({ value, error }) => { expect(value).toBe("-1"); @@ -113,7 +108,6 @@ describe("Number input component tests", () => { userEvent.type(number, "-1"); fireEvent.blur(number); }); - test("Cannot decrement the value if it is less than the min value", async () => { const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" min={5} />); const number = getByLabelText("Number input label") as HTMLInputElement; @@ -124,7 +118,6 @@ describe("Number input component tests", () => { decrement && (await userEvent.click(decrement)); expect(number.value).toBe("1"); }); - test("Increment the value when it is less than the min value", async () => { const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" min={5} />); const number = getByLabelText("Number input label") as HTMLInputElement; @@ -135,7 +128,6 @@ describe("Number input component tests", () => { increment && (await userEvent.click(increment)); expect(number.value).toBe("5"); }); - test("Error message is shown if the typed value is greater than the max value", () => { const onChange = jest.fn(); const onBlur = jest.fn(); @@ -150,7 +142,6 @@ describe("Number input component tests", () => { expect(onBlur).toHaveBeenCalled(); expect(onBlur).toHaveBeenCalledWith({ value: "12", error: "Value must be less than or equal to 10." }); }); - test("Cannot increment the value if it is greater than the max value", async () => { const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" max={10} />); const number = getByLabelText("Number input label") as HTMLInputElement; @@ -161,7 +152,6 @@ describe("Number input component tests", () => { decrement && (await userEvent.click(decrement)); expect(number.value).toBe("12"); }); - test("Decrement the value when it is greater than the max value", async () => { const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" max={10} />); const number = getByLabelText("Number input label") as HTMLInputElement; @@ -172,7 +162,6 @@ describe("Number input component tests", () => { decrement && (await userEvent.click(decrement)); expect(number.value).toBe("10"); }); - test("Increment and decrement the value with min and max values", async () => { const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" min={5} max={10} />); const number = getByLabelText("Number input label") as HTMLInputElement; @@ -194,7 +183,6 @@ describe("Number input component tests", () => { increment && (await userEvent.click(increment)); expect(number.value).toBe("10"); }); - test("Increment and decrement the value with an integer step", async () => { const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" step={5} />); const number = getByLabelText("Number input label") as HTMLInputElement; @@ -212,7 +200,6 @@ describe("Number input component tests", () => { decrement && (await userEvent.click(decrement)); expect(number.value).toBe("10"); }); - test("Increment and decrement the value with a decimal step", async () => { const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" step={0.5} />); const number = getByLabelText("Number input label") as HTMLInputElement; @@ -232,7 +219,6 @@ describe("Number input component tests", () => { decrement && (await userEvent.click(decrement)); expect(number.value).toBe("-10"); }); - test("Increment and decrement the value with min, max and step", async () => { const onBlur = jest.fn(); const { getByLabelText, getAllByRole } = render( @@ -258,7 +244,6 @@ describe("Number input component tests", () => { expect(number.value).toBe("5"); decrement && (await userEvent.click(decrement)); }); - test("Start incrementing from 0 when the min value is less than 0 and the max value is bigger than 0", async () => { const onBlur = jest.fn(); const { getByLabelText, getAllByRole } = render( @@ -271,7 +256,6 @@ describe("Number input component tests", () => { increment && (await userEvent.click(increment)); expect(number.value).toBe("2"); }); - test("Start incrementing from 0 when the min value is less than 0 and the max is 0", async () => { const { getByLabelText, getAllByRole } = render( <DxcNumberInput label="Number input label" min={-10} max={0} step={1} /> @@ -283,7 +267,6 @@ describe("Number input component tests", () => { increment && (await userEvent.click(increment)); expect(number.value).toBe("0"); }); - test("Start incrementing from the min value when it is bigger than 0", async () => { const { getByLabelText, getAllByRole } = render( <DxcNumberInput label="Number input label" min={2} max={10} step={0.5} /> @@ -295,7 +278,6 @@ describe("Number input component tests", () => { increment && (await userEvent.click(increment)); expect(number.value).toBe("2.5"); }); - test("Start incrementing from the max value when it is less than 0", async () => { const { getByLabelText, getAllByRole } = render( <DxcNumberInput label="Number input label" min={-10} max={-1} step={0.5} /> @@ -307,7 +289,6 @@ describe("Number input component tests", () => { increment && (await userEvent.click(increment)); expect(number.value).toBe("-1"); }); - test("Start decrementing from 0 when the min value is less than 0 and the max value is bigger than 0", async () => { const { getByLabelText, getAllByRole } = render( <DxcNumberInput label="Number input label" min={-10} max={10} step={1} /> @@ -317,7 +298,6 @@ describe("Number input component tests", () => { decrement && (await userEvent.click(decrement)); expect(number.value).toBe("-1"); }); - test("Start decrementing from 0 when the min value is 0 and the max value is bigger than 0", async () => { const { getByLabelText, getAllByRole } = render( <DxcNumberInput label="Number input label" min={0} max={10} step={1} /> @@ -327,7 +307,6 @@ describe("Number input component tests", () => { decrement && (await userEvent.click(decrement)); expect(number.value).toBe("0"); }); - test("Start decrementing from the min value when it is bigger than 0", async () => { const { getByLabelText, getAllByRole } = render( <DxcNumberInput label="Number input label" min={2} max={10} step={0.5} /> @@ -339,7 +318,6 @@ describe("Number input component tests", () => { decrement && (await userEvent.click(decrement)); expect(number.value).toBe("2"); }); - test("Start decrementing from the max value when it is less than 0", async () => { const { getByLabelText, getAllByRole } = render( <DxcNumberInput label="Number input label" min={-10} max={-1} step={0.5} /> @@ -351,7 +329,6 @@ describe("Number input component tests", () => { decrement && (await userEvent.click(decrement)); expect(number.value).toBe("-1.5"); }); - test("Increment and decrement the value with min, max and step using the arrows in keyboard", () => { const { getByLabelText } = render(<DxcNumberInput label="Number input label" min={5} max={20} step={5} />); const number = getByLabelText("Number input label") as HTMLInputElement; @@ -375,7 +352,6 @@ describe("Number input component tests", () => { fireEvent.keyDown(number, { keyCode: 40 }); expect(number.value).toBe("5"); }); - test("Value is unchanged when using the scroll wheel in mouse in a disabled input", () => { const { getByLabelText } = render( <DxcNumberInput disabled label="Number input label" min={5} max={20} step={5} defaultValue="10" /> @@ -390,7 +366,6 @@ describe("Number input component tests", () => { 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( <DxcNumberInput disabled label="Number input label" min={5} max={20} step={5} defaultValue="10" /> @@ -405,7 +380,6 @@ describe("Number input component tests", () => { 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( <DxcNumberInput readOnly label="Number input label" min={5} max={20} step={5} defaultValue="10" /> @@ -420,7 +394,6 @@ describe("Number input component tests", () => { 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( <DxcNumberInput readOnly label="Number input label" min={5} max={20} step={5} defaultValue="10" /> @@ -435,7 +408,6 @@ describe("Number input component tests", () => { 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(<DxcNumberInput label="Number input label" min={5} max={20} step={5} />); const number = getByLabelText("Number input label") as HTMLInputElement; @@ -459,7 +431,6 @@ describe("Number input component tests", () => { fireEvent.wheel(number, { deltaY: 100 }); expect(number.value).toBe("5"); }); - test("Number has correct accessibility attributes", () => { const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" />); const number = getByLabelText("Number input label"); @@ -472,7 +443,6 @@ describe("Number input component tests", () => { const increment = getAllByRole("button")[1]; expect(increment?.getAttribute("aria-label")).toBe("Increment value"); }); - test("Number input submits correct values inside a form and actions don't trigger the submit event", async () => { const handlerOnSubmit = jest.fn((e) => { e.preventDefault(); diff --git a/packages/lib/src/number-input/NumberInput.tsx b/packages/lib/src/number-input/NumberInput.tsx index 315012b76d..efd093a35f 100644 --- a/packages/lib/src/number-input/NumberInput.tsx +++ b/packages/lib/src/number-input/NumberInput.tsx @@ -29,6 +29,7 @@ const DxcNumberInput = forwardRef<RefType, NumberInputPropsType>( size, tabIndex, ariaLabel = "Number input", + showControls = true, }, ref ) => { @@ -36,12 +37,13 @@ const DxcNumberInput = forwardRef<RefType, NumberInputPropsType>( const contextValue = useMemo( () => ({ - typeNumber: "number", - minNumber: min, maxNumber: max, + minNumber: min, + showControls, stepNumber: step, + typeNumber: "number", }), - [min, max, step] + [max, min, showControls, step] ); useEffect(() => { diff --git a/packages/lib/src/number-input/types.ts b/packages/lib/src/number-input/types.ts index 1c96009ac9..b1e4ba3416 100644 --- a/packages/lib/src/number-input/types.ts +++ b/packages/lib/src/number-input/types.ts @@ -115,13 +115,18 @@ type Props = { * Specifies a string to be used as the name for the number input element when no `label` is provided. */ ariaLabel?: string; + /** + * Decides whether the number input has actions to increase or decrease the value, following the defined step. + */ + showControls?: boolean; }; export type NumberInputContextProps = { + maxNumber: Props["max"]; + minNumber: Props["min"]; + showControls: Props["showControls"]; + stepNumber: Props["step"]; typeNumber?: string; - minNumber?: number; - maxNumber?: number; - stepNumber?: number; }; /** diff --git a/packages/lib/src/text-input/TextInput.tsx b/packages/lib/src/text-input/TextInput.tsx index 1d73f7f8e2..a28111da97 100644 --- a/packages/lib/src/text-input/TextInput.tsx +++ b/packages/lib/src/text-input/TextInput.tsx @@ -538,7 +538,7 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( numberInputContext.typeNumber, numberInputContext.minNumber, numberInputContext.maxNumber, - numberInputContext.stepNumber + numberInputContext.stepNumber, ); } return undefined; @@ -658,7 +658,7 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( title={translatedLabels.textInput.clearFieldActionTitle} /> )} - {numberInputContext?.typeNumber === "number" && ( + {numberInputContext?.typeNumber === "number" && numberInputContext?.showControls && ( <> <DxcActionIcon onClick={!readOnly ? handleDecrementActionOnClick : undefined}