Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add readOnly prop to Number Input component #1696

Merged
merged 2 commits into from Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/src/date-input/types.ts
Expand Up @@ -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;
/**
Expand Down
49 changes: 47 additions & 2 deletions lib/src/number-input/NumberInput.stories.tsx
Expand Up @@ -32,6 +32,39 @@ export const Chromatic = () => (
<Title title="Disabled, helper text, optional and value" theme="light" level={4} />
<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" />
Expand All @@ -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>
Expand All @@ -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>
Expand Down
22 changes: 22 additions & 0 deletions lib/src/number-input/NumberInput.test.js
Expand Up @@ -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();
Expand Down
2 changes: 2 additions & 0 deletions lib/src/number-input/NumberInput.tsx
Expand Up @@ -23,6 +23,7 @@ const DxcNumberInput = React.forwardRef<RefType, NumberInputPropsType>(
placeholder,
disabled,
optional,
readOnly,
prefix,
suffix,
min,
Expand All @@ -49,6 +50,7 @@ const DxcNumberInput = React.forwardRef<RefType, NumberInputPropsType>(
placeholder={placeholder}
disabled={disabled}
optional={optional}
readOnly={readOnly}
prefix={prefix}
suffix={suffix}
error={error}
Expand Down
6 changes: 6 additions & 0 deletions lib/src/number-input/types.ts
Expand Up @@ -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.
*/
Expand Down
14 changes: 13 additions & 1 deletion lib/src/text-input/TextInput.test.js
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
16 changes: 8 additions & 8 deletions lib/src/text-input/TextInput.tsx
Expand Up @@ -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) => {
Expand All @@ -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) => {
Expand Down Expand Up @@ -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();
}}
Expand Down Expand Up @@ -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();
}}
Expand All @@ -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();
}}
Expand Down
2 changes: 2 additions & 0 deletions lib/src/text-input/types.ts
Expand Up @@ -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;
/**
Expand Down
Expand Up @@ -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 = [
{
Expand Down Expand Up @@ -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>
Expand Down