Skip to content

Commit f5baa6c

Browse files
committed
Number and Password input updates
1 parent 90db865 commit f5baa6c

File tree

8 files changed

+91
-134
lines changed

8 files changed

+91
-134
lines changed

lib/src/number-input/NumberInput.test.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,11 @@ describe("Number input component tests", () => {
3535
test("Number input is not optional: required field, displays error if not filled in", () => {
3636
const onBlur = jest.fn();
3737
const onChange = jest.fn();
38-
const { getByRole } = render(<DxcNumberInput onBlur={onBlur} onChange={onChange} />);
39-
const input = getByRole("spinbutton");
40-
41-
userEvent.type(input, "1");
42-
userEvent.clear(input);
43-
fireEvent.blur(input);
38+
const { getByLabelText } = render(<DxcNumberInput label="Number input label" onBlur={onBlur} onChange={onChange} />);
39+
const number = getByLabelText("Number input label");
40+
userEvent.type(number, "1");
41+
userEvent.clear(number);
42+
fireEvent.blur(number);
4443
expect(onBlur).toHaveBeenCalled();
4544
expect(onBlur).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, enter a value." });
4645
expect(onChange).toHaveBeenCalled();
@@ -352,6 +351,7 @@ describe("Number input component tests", () => {
352351
test("Number has correct accessibility attributes", () => {
353352
const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" />);
354353
const number = getByLabelText("Number input label");
354+
expect(number.getAttribute("type")).toBe("number");
355355
expect(number.getAttribute("aria-autocomplete")).toBeNull();
356356
expect(number.getAttribute("aria-controls")).toBeNull();
357357
expect(number.getAttribute("aria-expanded")).toBeNull();

lib/src/number-input/NumberInput.tsx

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
import React from "react";
22
import styled from "styled-components";
33
import DxcTextInput from "../text-input/TextInput";
4-
import NumberInputContext from "./NumberInputContext";
54
import NumberInputPropsType, { RefType } from "./types";
65

6+
type NumberInputContextProps = {
7+
typeNumber?: string;
8+
minNumber?: number;
9+
maxNumber?: number;
10+
stepNumber?: number;
11+
};
12+
13+
export const NumberInputContext = React.createContext<NumberInputContextProps | null>(null);
14+
715
const DxcNumberInput = React.forwardRef<RefType, NumberInputPropsType>(
816
(
917
{
@@ -29,34 +37,32 @@ const DxcNumberInput = React.forwardRef<RefType, NumberInputPropsType>(
2937
tabIndex,
3038
},
3139
ref
32-
) => {
33-
return (
34-
<NumberInputContext.Provider value={{ typeNumber: "number", minNumber: min, maxNumber: max, stepNumber: step }}>
35-
<NumberInputContainer>
36-
<DxcTextInput
37-
label={label}
38-
name={name}
39-
defaultValue={defaultValue}
40-
value={value}
41-
helperText={helperText}
42-
placeholder={placeholder}
43-
disabled={disabled}
44-
optional={optional}
45-
prefix={prefix}
46-
suffix={suffix}
47-
error={error}
48-
onChange={onChange}
49-
onBlur={onBlur}
50-
autocomplete={autocomplete}
51-
margin={margin}
52-
size={size}
53-
tabIndex={tabIndex}
54-
ref={ref}
55-
/>
56-
</NumberInputContainer>
57-
</NumberInputContext.Provider>
58-
);
59-
}
40+
) => (
41+
<NumberInputContext.Provider value={{ typeNumber: "number", minNumber: min, maxNumber: max, stepNumber: step }}>
42+
<NumberInputContainer>
43+
<DxcTextInput
44+
label={label}
45+
name={name}
46+
defaultValue={defaultValue}
47+
value={value}
48+
helperText={helperText}
49+
placeholder={placeholder}
50+
disabled={disabled}
51+
optional={optional}
52+
prefix={prefix}
53+
suffix={suffix}
54+
error={error}
55+
onChange={onChange}
56+
onBlur={onBlur}
57+
autocomplete={autocomplete}
58+
margin={margin}
59+
size={size}
60+
tabIndex={tabIndex}
61+
ref={ref}
62+
/>
63+
</NumberInputContainer>
64+
</NumberInputContext.Provider>
65+
)
6066
);
6167

6268
const NumberInputContainer = styled.div`

lib/src/number-input/NumberInputContext.tsx

Lines changed: 0 additions & 11 deletions
This file was deleted.

lib/src/number-input/numberInputContextTypes.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

lib/src/password-input/Icons.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from "react";
2+
3+
const icons = {
4+
showPassword: (
5+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor">
6+
<path d="M0 0h24v24H0V0z" fill="none" />
7+
<path d="M12 6c3.79 0 7.17 2.13 8.82 5.5C19.17 14.87 15.79 17 12 17s-7.17-2.13-8.82-5.5C4.83 8.13 8.21 6 12 6m0-2C7 4 2.73 7.11 1 11.5 2.73 15.89 7 19 12 19s9.27-3.11 11-7.5C21.27 7.11 17 4 12 4zm0 5c1.38 0 2.5 1.12 2.5 2.5S13.38 14 12 14s-2.5-1.12-2.5-2.5S10.62 9 12 9m0-2c-2.48 0-4.5 2.02-4.5 4.5S9.52 16 12 16s4.5-2.02 4.5-4.5S14.48 7 12 7z" />
8+
</svg>
9+
),
10+
hidePassword: (
11+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor">
12+
<path d="M0 0h24v24H0V0zm0 0h24v24H0V0zm0 0h24v24H0V0zm0 0h24v24H0V0z" fill="none" />
13+
<path d="M12 6c3.79 0 7.17 2.13 8.82 5.5-.59 1.22-1.42 2.27-2.41 3.12l1.41 1.41c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l1.65 1.65C10.66 6.09 11.32 6 12 6zm-1.07 1.14L13 9.21c.57.25 1.03.71 1.28 1.28l2.07 2.07c.08-.34.14-.7.14-1.07C16.5 9.01 14.48 7 12 7c-.37 0-.72.05-1.07.14zM2.01 3.87l2.68 2.68C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.98-.29 4.32-.82l3.42 3.42 1.41-1.41L3.42 2.45 2.01 3.87zm7.5 7.5l2.61 2.61c-.04.01-.08.02-.12.02-1.38 0-2.5-1.12-2.5-2.5 0-.05.01-.08.01-.13zm-3.4-3.4l1.75 1.75c-.23.55-.36 1.15-.36 1.78 0 2.48 2.02 4.5 4.5 4.5.63 0 1.23-.13 1.77-.36l.98.98c-.88.24-1.8.38-2.75.38-3.79 0-7.17-2.13-8.82-5.5.7-1.43 1.72-2.61 2.93-3.53z" />
14+
</svg>
15+
),
16+
};
17+
18+
export default icons;

lib/src/password-input/PasswordInput.test.js

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@ import userEvent from "@testing-library/user-event";
44
import DxcPasswordInput from "./PasswordInput.tsx";
55

66
describe("Password input component tests", () => {
7-
test("Password input renders with label", () => {
8-
const { getByText } = render(<DxcPasswordInput label="Password input label" />);
7+
test("Password input renders with label and helper text", () => {
8+
const { getByText } = render(<DxcPasswordInput label="Password input label" helperText="Helper text" />);
99
expect(getByText("Password input label")).toBeTruthy();
10-
});
11-
12-
test("Password input renders with helper text", () => {
13-
const { getByText } = render(<DxcPasswordInput helperText="Helper text" />);
1410
expect(getByText("Helper text")).toBeTruthy();
1511
});
1612

@@ -79,15 +75,14 @@ describe("Password input component tests", () => {
7975
expect(queryByTitle("Hide password")).toBeFalsy();
8076
});
8177

82-
test("Password input has correct accesibility attributes", () => {
78+
test("Password input has correct accessibility attributes", () => {
8379
const { getByRole, getByLabelText } = render(<DxcPasswordInput label="Password input" />);
8480
const passwordInput = getByLabelText("Password input");
85-
expect(passwordInput.getAttribute("aria-autocomplete")).toBeNull();
86-
expect(passwordInput.getAttribute("aria-controls")).toBeNull();
87-
expect(passwordInput.getAttribute("aria-expanded")).toBeNull();
8881
const showButton = getByRole("button");
82+
expect(passwordInput.getAttribute("aria-expanded")).toBe("false");
8983
expect(showButton.getAttribute("aria-label")).toBe("Show password");
9084
userEvent.click(showButton);
85+
expect(passwordInput.getAttribute("aria-expanded")).toBe("true");
9186
expect(showButton.getAttribute("aria-label")).toBe("Hide password");
9287
});
9388
});

lib/src/password-input/PasswordInput.tsx

Lines changed: 26 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@ import styled from "styled-components";
33
import DxcTextInput from "../text-input/TextInput";
44
import useTranslatedLabels from "../useTranslatedLabels";
55
import PasswordInputPropsType, { RefType } from "./types";
6+
import icons from "./Icons";
7+
8+
const setInputType = (type: string, element: HTMLDivElement | null) => {
9+
element?.getElementsByTagName("input")[0].setAttribute("type", type);
10+
};
11+
12+
const setAriaAttributes = (ariaExpanded: "true" | "false", ariaLabel: string, element: HTMLDivElement | null) => {
13+
const inputElement = element?.getElementsByTagName("input")[0];
14+
inputElement?.setAttribute("aria-expanded", ariaExpanded);
15+
inputElement?.setAttribute("aria-label", ariaLabel);
16+
};
617

718
const DxcPasswordInput = React.forwardRef<RefType, PasswordInputPropsType>(
819
(
@@ -26,81 +37,38 @@ const DxcPasswordInput = React.forwardRef<RefType, PasswordInputPropsType>(
2637
ref
2738
) => {
2839
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
29-
const inputRef = useRef(null);
30-
const translatedLabels = useTranslatedLabels();
31-
const setInputType = (type) => {
32-
inputRef?.current?.children[label && helperText ? 2 : label || helperText ? 1 : 0]?.children[0].setAttribute(
33-
"type",
34-
type
35-
);
36-
};
37-
38-
const setAriaAttributes = (ariaExpanded, ariaLabel) => {
39-
const inputIndex = label && helperText ? 2 : label || helperText ? 1 : 0;
40-
if (error && clearable && value) {
41-
inputRef?.current?.children[inputIndex]?.children[3]?.setAttribute("aria-expanded", ariaExpanded);
42-
inputRef?.current?.children[inputIndex]?.children[3]?.setAttribute("aria-label", ariaLabel);
43-
} else if (error || (clearable && !value)) {
44-
inputRef?.current?.children[inputIndex]?.children[2]?.setAttribute("aria-expanded", ariaExpanded);
45-
inputRef?.current?.children[inputIndex]?.children[2]?.setAttribute("aria-label", ariaLabel);
46-
} else {
47-
inputRef?.current?.children[inputIndex]?.children[1]?.setAttribute("aria-expanded", ariaExpanded);
48-
inputRef?.current?.children[inputIndex]?.children[1]?.setAttribute("aria-label", ariaLabel);
49-
}
50-
};
51-
52-
const getIconTitle = () => {
53-
return isPasswordVisible
54-
? translatedLabels.passwordInput.inputHidePasswordTitle
55-
: translatedLabels.passwordInput.inputShowPasswordTitle;
56-
};
40+
const inputRef = useRef<HTMLDivElement>(null);
41+
const { passwordInput } = useTranslatedLabels();
5742

5843
useEffect(() => {
59-
setAriaAttributes(false, translatedLabels.passwordInput.inputShowPasswordTitle);
60-
if (isPasswordVisible) {
61-
setInputType("text");
62-
setAriaAttributes(true, translatedLabels.passwordInput.inputHidePasswordTitle);
63-
} else {
64-
setInputType("password");
65-
setAriaAttributes(false, translatedLabels.passwordInput.inputShowPasswordTitle);
66-
}
67-
}, [isPasswordVisible]);
44+
setInputType("password", inputRef?.current);
45+
setAriaAttributes("false", passwordInput.inputShowPasswordTitle, inputRef?.current);
46+
}, [passwordInput]);
6847

6948
const viewPassword = () => {
70-
setInputType("text");
7149
setIsPasswordVisible(true);
50+
setInputType("text", inputRef?.current);
51+
setAriaAttributes("true", passwordInput.inputHidePasswordTitle, inputRef?.current);
7252
};
7353

7454
const hidePassword = () => {
75-
setInputType("password");
7655
setIsPasswordVisible(false);
77-
};
78-
79-
const action = {
80-
onClick: isPasswordVisible ? hidePassword : viewPassword,
81-
icon: isPasswordVisible ? (
82-
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor">
83-
<path d="M0 0h24v24H0V0zm0 0h24v24H0V0zm0 0h24v24H0V0zm0 0h24v24H0V0z" fill="none" />
84-
<path d="M12 6c3.79 0 7.17 2.13 8.82 5.5-.59 1.22-1.42 2.27-2.41 3.12l1.41 1.41c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l1.65 1.65C10.66 6.09 11.32 6 12 6zm-1.07 1.14L13 9.21c.57.25 1.03.71 1.28 1.28l2.07 2.07c.08-.34.14-.7.14-1.07C16.5 9.01 14.48 7 12 7c-.37 0-.72.05-1.07.14zM2.01 3.87l2.68 2.68C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.98-.29 4.32-.82l3.42 3.42 1.41-1.41L3.42 2.45 2.01 3.87zm7.5 7.5l2.61 2.61c-.04.01-.08.02-.12.02-1.38 0-2.5-1.12-2.5-2.5 0-.05.01-.08.01-.13zm-3.4-3.4l1.75 1.75c-.23.55-.36 1.15-.36 1.78 0 2.48 2.02 4.5 4.5 4.5.63 0 1.23-.13 1.77-.36l.98.98c-.88.24-1.8.38-2.75.38-3.79 0-7.17-2.13-8.82-5.5.7-1.43 1.72-2.61 2.93-3.53z" />
85-
</svg>
86-
) : (
87-
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor">
88-
<path d="M0 0h24v24H0V0z" fill="none" />
89-
<path d="M12 6c3.79 0 7.17 2.13 8.82 5.5C19.17 14.87 15.79 17 12 17s-7.17-2.13-8.82-5.5C4.83 8.13 8.21 6 12 6m0-2C7 4 2.73 7.11 1 11.5 2.73 15.89 7 19 12 19s9.27-3.11 11-7.5C21.27 7.11 17 4 12 4zm0 5c1.38 0 2.5 1.12 2.5 2.5S13.38 14 12 14s-2.5-1.12-2.5-2.5S10.62 9 12 9m0-2c-2.48 0-4.5 2.02-4.5 4.5S9.52 16 12 16s4.5-2.02 4.5-4.5S14.48 7 12 7z" />
90-
</svg>
91-
),
92-
title: getIconTitle(),
56+
setInputType("password", inputRef?.current);
57+
setAriaAttributes("false", passwordInput.inputShowPasswordTitle, inputRef?.current);
9358
};
9459

9560
return (
9661
<PasswordInput ref={ref}>
9762
<DxcTextInput
98-
ref={inputRef}
9963
label={label}
10064
name={name}
10165
value={value}
10266
helperText={helperText}
103-
action={action}
67+
action={{
68+
onClick: isPasswordVisible ? hidePassword : viewPassword,
69+
icon: isPasswordVisible ? icons.hidePassword : icons.showPassword,
70+
title: isPasswordVisible ? passwordInput.inputHidePasswordTitle : passwordInput.inputShowPasswordTitle,
71+
}}
10472
error={error}
10573
clearable={clearable}
10674
onChange={onChange}
@@ -111,6 +79,7 @@ const DxcPasswordInput = React.forwardRef<RefType, PasswordInputPropsType>(
11179
minLength={minLength}
11280
maxLength={maxLength}
11381
autocomplete={autocomplete}
82+
ref={inputRef}
11483
tabIndex={tabIndex}
11584
/>
11685
</PasswordInput>

lib/src/text-input/TextInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import useTranslatedLabels from "../useTranslatedLabels";
55
import { spaces } from "../common/variables";
66
import { getMargin } from "../common/utils";
77
import BackgroundColorContext, { BackgroundColors } from "../BackgroundColorContext";
8-
import NumberInputContext from "../number-input/NumberInputContext";
8+
import { NumberInputContext } from "../number-input/NumberInput";
99
import TextInputPropsType, { AutosuggestWrapperProps, RefType } from "./types";
1010
import Suggestions from "./Suggestions";
1111
import * as Popover from "@radix-ui/react-popover";

0 commit comments

Comments
 (0)