From 6d3b6e58fe1985823c2f200880af33b10738ceec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Mon, 2 Oct 2023 16:28:38 +0200 Subject: [PATCH 1/2] Adding readOnly prop to NumberInput --- lib/src/date-input/types.ts | 2 + lib/src/number-input/NumberInput.stories.tsx | 49 ++++++++++++++++++- lib/src/number-input/NumberInput.test.js | 22 +++++++++ lib/src/number-input/NumberInput.tsx | 2 + lib/src/number-input/types.ts | 6 +++ lib/src/text-input/TextInput.stories.tsx | 1 + lib/src/text-input/TextInput.test.js | 14 +++++- lib/src/text-input/TextInput.tsx | 16 +++--- lib/src/text-input/types.ts | 2 + .../number-input/code/NumberInputCodePage.tsx | 16 ++++++ 10 files changed, 119 insertions(+), 11 deletions(-) diff --git a/lib/src/date-input/types.ts b/lib/src/date-input/types.ts index 967dc7415..f2d6a9909 100644 --- a/lib/src/date-input/types.ts +++ b/lib/src/date-input/types.ts @@ -47,6 +47,8 @@ type Props = { disabled?: boolean; /** * If true, the component will not be mutable, meaning the user can not edit the control. + * The date picker cannot be opened either. In addition, the clear action will not be displayed + * even if the flag is set to true. */ readOnly?: boolean; /** diff --git a/lib/src/number-input/NumberInput.stories.tsx b/lib/src/number-input/NumberInput.stories.tsx index a3e71034b..19778c809 100644 --- a/lib/src/number-input/NumberInput.stories.tsx +++ b/lib/src/number-input/NumberInput.stories.tsx @@ -32,6 +32,39 @@ export const Chromatic = () => ( <DxcNumberInput label="Disabled number input" helperText="Help message" disabled optional defaultValue="10" /> </ExampleContainer> + <ExampleContainer> + <Title title="Read only" theme="light" level={4} /> + <DxcNumberInput + label="Example label" + helperText="Help message" + readOnly + optional + prefix="€" + defaultValue="33" + /> + </ExampleContainer> + <ExampleContainer pseudoState="pseudo-hover"> + <Title title="Hovered read only" theme="light" level={4} /> + <DxcNumberInput + label="Example label" + helperText="Help message" + readOnly + optional + prefix="€" + defaultValue="1" + /> + </ExampleContainer> + <ExampleContainer pseudoState="pseudo-active"> + <Title title="Active read only" theme="light" level={4} /> + <DxcNumberInput + label="Example label" + helperText="Help message" + readOnly + optional + prefix="€" + placeholder="Placeholder" + /> + </ExampleContainer> <ExampleContainer> <Title title="Prefix" theme="light" level={4} /> <DxcNumberInput label="With prefix" prefix="+34" /> @@ -42,7 +75,13 @@ export const Chromatic = () => ( </ExampleContainer> <ExampleContainer> <Title title="Invalid" theme="light" level={4} /> - <DxcNumberInput label="Error number input" helperText="Help message" error="Error message." defaultValue="23" optional /> + <DxcNumberInput + label="Error number input" + helperText="Help message" + error="Error message." + defaultValue="23" + optional + /> </ExampleContainer> <BackgroundColorProvider color="#333333"> <DarkContainer> @@ -61,7 +100,13 @@ export const Chromatic = () => ( </ExampleContainer> <ExampleContainer> <Title title="Disabled, helper text, optional and value" theme="dark" level={4} /> - <DxcNumberInput label="Disabled number input" helperText="Help message" disabled optional defaultValue="1232454" /> + <DxcNumberInput + label="Disabled number input" + helperText="Help message" + disabled + optional + defaultValue="1232454" + /> </ExampleContainer> </DarkContainer> </BackgroundColorProvider> diff --git a/lib/src/number-input/NumberInput.test.js b/lib/src/number-input/NumberInput.test.js index 98395e746..d5c05f45b 100644 --- a/lib/src/number-input/NumberInput.test.js +++ b/lib/src/number-input/NumberInput.test.js @@ -27,6 +27,28 @@ describe("Number input component tests", () => { expect(number.disabled).toBeTruthy(); }); + test("Number input is read only and cannot be incremented or decremented using the actions", () => { + const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number label" readOnly />); + const number = getByLabelText("Number label"); + expect(number.readOnly).toBeTruthy(); + const decrement = getAllByRole("button")[0]; + userEvent.click(decrement); + expect(number.value).toBe(""); + const increment = getAllByRole("button")[1]; + 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"); + expect(number.readOnly).toBeTruthy(); + fireEvent.keyDown(number, { keyCode: 38 }); + expect(number.value).toBe(""); + 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(); diff --git a/lib/src/number-input/NumberInput.tsx b/lib/src/number-input/NumberInput.tsx index eed8ac5ef..af4122fe0 100644 --- a/lib/src/number-input/NumberInput.tsx +++ b/lib/src/number-input/NumberInput.tsx @@ -23,6 +23,7 @@ const DxcNumberInput = React.forwardRef<RefType, NumberInputPropsType>( placeholder, disabled, optional, + readOnly, prefix, suffix, min, @@ -49,6 +50,7 @@ const DxcNumberInput = React.forwardRef<RefType, NumberInputPropsType>( placeholder={placeholder} disabled={disabled} optional={optional} + readOnly={readOnly} prefix={prefix} suffix={suffix} error={error} diff --git a/lib/src/number-input/types.ts b/lib/src/number-input/types.ts index 00c753570..923ead464 100644 --- a/lib/src/number-input/types.ts +++ b/lib/src/number-input/types.ts @@ -42,6 +42,12 @@ type Props = { * functions when it has not been filled. */ optional?: boolean; + /** + * If true, the component will not be mutable, meaning the user can + * not edit the control. The value won't change when pressing on the + * up or down arrows and neither on the spin buttons. + */ + readOnly?: boolean; /** * Prefix to be placed before the number value. */ diff --git a/lib/src/text-input/TextInput.stories.tsx b/lib/src/text-input/TextInput.stories.tsx index bda13b8f7..7bc83bd4c 100644 --- a/lib/src/text-input/TextInput.stories.tsx +++ b/lib/src/text-input/TextInput.stories.tsx @@ -457,6 +457,7 @@ const AutosuggestListbox = () => { optional placeholder="Choose an option" size="fillParent" + readOnly /> <button style={{ zIndex: "1", width: "100px" }}>Submit</button> </div> diff --git a/lib/src/text-input/TextInput.test.js b/lib/src/text-input/TextInput.test.js index b54684e16..2f012beb8 100644 --- a/lib/src/text-input/TextInput.test.js +++ b/lib/src/text-input/TextInput.test.js @@ -493,7 +493,7 @@ describe("TextInput component synchronous autosuggest tests", () => { <DxcTextInput label="Autocomplete Countries" suggestions={countries} onChange={onChange} /> ); const input = getByRole("combobox"); - fireEvent.focus(input); + userEvent.click(input); const list = getByRole("listbox"); expect(list).toBeTruthy(); expect(getByText("Afghanistan")).toBeTruthy(); @@ -522,6 +522,18 @@ describe("TextInput component synchronous autosuggest tests", () => { expect(getByText("arbados")).toBeTruthy(); }); + test("Read-only text input does not open the suggestions list", () => { + const onChange = jest.fn(); + const { getByRole, queryByRole } = render( + <DxcTextInput label="Autocomplete Countries" suggestions={countries} onChange={onChange} readOnly /> + ); + const input = getByRole("combobox"); + fireEvent.focus(input); + expect(queryByRole("listbox")).toBeFalsy(); + userEvent.click(input); + expect(queryByRole("listbox")).toBeFalsy(); + }); + test("Autosuggest displays filtered when the input has a default value", () => { const { getByRole, getByText, getAllByText } = render( <DxcTextInput diff --git a/lib/src/text-input/TextInput.tsx b/lib/src/text-input/TextInput.tsx index ab77ad245..92ba1215a 100644 --- a/lib/src/text-input/TextInput.tsx +++ b/lib/src/text-input/TextInput.tsx @@ -186,9 +186,9 @@ const DxcTextInput = React.forwardRef<RefType, TextInputPropsType>( case "Down": case "ArrowDown": event.preventDefault(); - if (numberInputContext?.typeNumber === "number") { + if (numberInputContext?.typeNumber === "number") decrementNumber(); - } else { + else { openSuggestions(); if (!isAutosuggestError && !isSearching && filteredSuggestions.length > 0) { changeVisualFocusIndex((visualFocusedSuggIndex) => { @@ -201,9 +201,9 @@ const DxcTextInput = React.forwardRef<RefType, TextInputPropsType>( case "Up": case "ArrowUp": event.preventDefault(); - if (numberInputContext?.typeNumber === "number") { + if (numberInputContext?.typeNumber === "number") incrementNumber(); - } else { + else { openSuggestions(); if (!isAutosuggestError && !isSearching && filteredSuggestions.length > 0) { changeVisualFocusIndex((visualFocusedSuggIndex) => { @@ -402,8 +402,8 @@ const DxcTextInput = React.forwardRef<RefType, TextInputPropsType>( placeholder={placeholder} onBlur={handleInputOnBlur} onChange={handleInputOnChange} - onFocus={openSuggestions} - onKeyDown={handleInputOnKeyDown} + onFocus={!readOnly ? openSuggestions : undefined} + onKeyDown={!readOnly ? handleInputOnKeyDown : undefined} onMouseDown={(event) => { event.stopPropagation(); }} @@ -457,7 +457,7 @@ const DxcTextInput = React.forwardRef<RefType, TextInputPropsType>( <Action aria-label={translatedLabels.numberInput.decrementValueTitle} disabled={disabled} - onClick={handleDecrementActionOnClick} + onClick={!readOnly ? handleDecrementActionOnClick : undefined} onMouseDown={(event) => { event.stopPropagation(); }} @@ -472,7 +472,7 @@ const DxcTextInput = React.forwardRef<RefType, TextInputPropsType>( <Action aria-label={translatedLabels.numberInput.incrementValueTitle} disabled={disabled} - onClick={handleIncrementActionOnClick} + onClick={!readOnly ? handleIncrementActionOnClick : undefined} onMouseDown={(event) => { event.stopPropagation(); }} diff --git a/lib/src/text-input/types.ts b/lib/src/text-input/types.ts index 45790c045..118b90802 100644 --- a/lib/src/text-input/types.ts +++ b/lib/src/text-input/types.ts @@ -62,6 +62,8 @@ type Props = { disabled?: boolean; /** * If true, the component will not be mutable, meaning the user can not edit the control. + * In addition, the clear action will not be displayed even if the flag is set to true + * and the custom action will not execute its onClick event. */ readOnly?: boolean; /** diff --git a/website/screens/components/number-input/code/NumberInputCodePage.tsx b/website/screens/components/number-input/code/NumberInputCodePage.tsx index cfaa92348..d5ed363e3 100644 --- a/website/screens/components/number-input/code/NumberInputCodePage.tsx +++ b/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 HeaderDescriptionCell from "@/common/HeaderDescriptionCell"; +import StatusTag from "@/common/StatusTag"; const sections = [ { @@ -78,6 +79,21 @@ const sections = [ been filled. </td> </tr> + <tr> + <td> + <DxcFlex direction="column" gap="0.25rem" alignItems="baseline"> + <StatusTag status="Information">New</StatusTag>readOnly: boolean + </DxcFlex> + </td> + <td> + <Code>false</Code> + </td> + <td> + If true, the component will not be mutable, meaning the user can + not edit the control. The value won't change when pressing on the + up or down arrows and neither on the spin buttons. + </td> + </tr> <tr> <td>prefix: string</td> <td></td> From ef23b2999faf18638935696e8242281219481e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:28:06 +0200 Subject: [PATCH 2/2] Removing a mistake on Text Input stories --- lib/src/text-input/TextInput.stories.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/text-input/TextInput.stories.tsx b/lib/src/text-input/TextInput.stories.tsx index 7bc83bd4c..bda13b8f7 100644 --- a/lib/src/text-input/TextInput.stories.tsx +++ b/lib/src/text-input/TextInput.stories.tsx @@ -457,7 +457,6 @@ const AutosuggestListbox = () => { optional placeholder="Choose an option" size="fillParent" - readOnly /> <button style={{ zIndex: "1", width: "100px" }}>Submit</button> </div>