Skip to content

Commit 427a80a

Browse files
authored
Merge pull request #1667 from dxc-technology/gomezivann/number-input-fix
Fixes to the Number Input
2 parents db5d8dc + 28c1e5e commit 427a80a

File tree

9 files changed

+313
-275
lines changed

9 files changed

+313
-275
lines changed

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

Lines changed: 141 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,14 @@ import userEvent from "@testing-library/user-event";
44
import DxcNumberInput from "./NumberInput.tsx";
55

66
describe("Number input component tests", () => {
7-
test("Number input renders with label", () => {
8-
const { getByText } = render(<DxcNumberInput label="Number input label" />);
7+
test("Number input renders with label, helper text, placeholder and increment/decrement action buttons", () => {
8+
const { getByLabelText, getByText, queryAllByRole } = render(
9+
<DxcNumberInput label="Number input label" helperText="Helper text" placeholder="Placeholder" />
10+
);
11+
const number = getByLabelText("Number input label");
912
expect(getByText("Number input label")).toBeTruthy();
10-
});
11-
12-
test("Number input renders with helper text", () => {
13-
const { getByText } = render(<DxcNumberInput helperText="Helper text" />);
1413
expect(getByText("Helper text")).toBeTruthy();
15-
});
16-
17-
test("Number input renders with placeholder", () => {
18-
const { getByLabelText } = render(<DxcNumberInput label="Number label" placeholder="Placeholder" />);
19-
const number = getByLabelText("Number label");
2014
expect(number.getAttribute("placeholder")).toBe("Placeholder");
21-
});
22-
23-
test("Number input renders increment and decrement buttons", () => {
24-
const { queryAllByRole } = render(<DxcNumberInput label="Number label" />);
2515
expect(queryAllByRole("button").length).toBe(2);
2616
});
2717

@@ -45,19 +35,18 @@ describe("Number input component tests", () => {
4535
test("Number input is not optional: required field, displays error if not filled in", () => {
4636
const onBlur = jest.fn();
4737
const onChange = jest.fn();
48-
const { getByRole } = render(<DxcNumberInput onBlur={onBlur} onChange={onChange} />);
49-
const input = getByRole("spinbutton");
50-
51-
userEvent.type(input, "1");
52-
userEvent.clear(input);
53-
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);
5443
expect(onBlur).toHaveBeenCalled();
5544
expect(onBlur).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, enter a value." });
5645
expect(onChange).toHaveBeenCalled();
5746
expect(onChange).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, enter a value." });
5847
});
5948

60-
test("Suffix and prefix must be shown)", () => {
49+
test("Suffix and prefix must be shown", () => {
6150
const { getByText } = render(<DxcNumberInput label="Number input label" prefix="+34" suffix="USD" />);
6251
expect(getByText("+34")).toBeTruthy();
6352
expect(getByText("USD")).toBeTruthy();
@@ -80,20 +69,20 @@ describe("Number input component tests", () => {
8069
expect(number.value).toBe("1");
8170
});
8271

83-
test("Error message is shown if the value is less than the min value", () => {
72+
test("Error message is shown if the typed value is less than the min value", () => {
8473
const onChange = jest.fn(({ value, error }) => {
85-
expect(value).toBe("1");
86-
expect(error).toBe("Value must be greater than or equal to 5.");
74+
expect(value).toBe("-1");
75+
expect(error).toBe("Value must be greater than or equal to 0.");
8776
});
8877
const onBlur = jest.fn(({ value, error }) => {
89-
expect(value).toBe("1");
90-
expect(error).toBe("Value must be greater than or equal to 5.");
78+
expect(value).toBe("-1");
79+
expect(error).toBe("Value must be greater than or equal to 0.");
9180
});
9281
const { getByLabelText } = render(
93-
<DxcNumberInput label="Number input label" min={5} onBlur={onBlur} onChange={onChange} />
82+
<DxcNumberInput label="Number input label" min={0} onBlur={onBlur} onChange={onChange} />
9483
);
9584
const number = getByLabelText("Number input label");
96-
userEvent.type(number, "1");
85+
userEvent.type(number, "-1");
9786
fireEvent.blur(number);
9887
});
9988

@@ -119,7 +108,7 @@ describe("Number input component tests", () => {
119108
expect(number.value).toBe("5");
120109
});
121110

122-
test("Error message is shown if the value is greater than the max value", () => {
111+
test("Error message is shown if the typed value is greater than the max value", () => {
123112
const onChange = jest.fn();
124113
const onBlur = jest.fn();
125114
const { getByLabelText } = render(
@@ -148,9 +137,9 @@ describe("Number input component tests", () => {
148137
test("Decrement the value when it is greater than the max value", () => {
149138
const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" max={10} />);
150139
const number = getByLabelText("Number input label");
151-
userEvent.type(number, "12");
140+
userEvent.type(number, "120");
152141
fireEvent.blur(number);
153-
expect(number.value).toBe("12");
142+
expect(number.value).toBe("120");
154143
const decrement = getAllByRole("button")[0];
155144
userEvent.click(decrement);
156145
expect(number.value).toBe("10");
@@ -178,7 +167,7 @@ describe("Number input component tests", () => {
178167
expect(number.value).toBe("10");
179168
});
180169

181-
test("Increment and decrement the value with step", () => {
170+
test("Increment and decrement the value with an integer step", () => {
182171
const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" step={5} />);
183172
const number = getByLabelText("Number input label");
184173
userEvent.type(number, "10");
@@ -196,6 +185,26 @@ describe("Number input component tests", () => {
196185
expect(number.value).toBe("10");
197186
});
198187

188+
test("Increment and decrement the value with a decimal step", () => {
189+
const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" step={0.5} />);
190+
const number = getByLabelText("Number input label");
191+
userEvent.type(number, "-9");
192+
fireEvent.blur(number);
193+
expect(number.value).toBe("-9");
194+
const increment = getAllByRole("button")[1];
195+
userEvent.click(increment);
196+
expect(number.value).toBe("-8.5");
197+
userEvent.click(increment);
198+
expect(number.value).toBe("-8");
199+
const decrement = getAllByRole("button")[0];
200+
userEvent.click(decrement);
201+
userEvent.click(decrement);
202+
userEvent.click(decrement);
203+
expect(number.value).toBe("-9.5");
204+
userEvent.click(decrement);
205+
expect(number.value).toBe("-10");
206+
});
207+
199208
test("Increment and decrement the value with min, max and step", () => {
200209
const onBlur = jest.fn();
201210
const { getByLabelText, getAllByRole } = render(
@@ -211,16 +220,108 @@ describe("Number input component tests", () => {
211220
userEvent.click(increment);
212221
expect(number.value).toBe("13");
213222
userEvent.click(increment);
214-
expect(number.value).toBe("20");
223+
expect(number.value).toBe("13");
215224
userEvent.click(increment);
216-
expect(number.value).toBe("20");
225+
expect(number.value).toBe("13");
217226
const decrement = getAllByRole("button")[0];
218227
userEvent.click(decrement);
219-
expect(number.value).toBe("12");
220-
userEvent.click(decrement);
221228
expect(number.value).toBe("5");
222229
userEvent.click(decrement);
223230
expect(number.value).toBe("5");
231+
userEvent.click(decrement);
232+
});
233+
234+
test("Start incrementing from 0 when the min value is less than 0 and the max value is bigger than 0", () => {
235+
const onBlur = jest.fn();
236+
const { getByLabelText, getAllByRole } = render(
237+
<DxcNumberInput label="Number input label" min={-10} max={10} step={1} onBlur={onBlur} />
238+
);
239+
const number = getByLabelText("Number input label");
240+
const increment = getAllByRole("button")[1];
241+
userEvent.click(increment);
242+
expect(number.value).toBe("1");
243+
userEvent.click(increment);
244+
expect(number.value).toBe("2");
245+
});
246+
247+
test("Start incrementing from 0 when the min value is less than 0 and the max is 0", () => {
248+
const { getByLabelText, getAllByRole } = render(
249+
<DxcNumberInput label="Number input label" min={-10} max={0} step={1} />
250+
);
251+
const number = getByLabelText("Number input label");
252+
const increment = getAllByRole("button")[1];
253+
userEvent.click(increment);
254+
expect(number.value).toBe("0");
255+
userEvent.click(increment);
256+
expect(number.value).toBe("0");
257+
});
258+
259+
test("Start incrementing from the min value when it is bigger than 0", () => {
260+
const { getByLabelText, getAllByRole } = render(
261+
<DxcNumberInput label="Number input label" min={2} max={10} step={0.5} />
262+
);
263+
const number = getByLabelText("Number input label");
264+
const increment = getAllByRole("button")[1];
265+
userEvent.click(increment);
266+
expect(number.value).toBe("2");
267+
userEvent.click(increment);
268+
expect(number.value).toBe("2.5");
269+
});
270+
271+
test("Start incrementing from the max value when it is less than 0", () => {
272+
const { getByLabelText, getAllByRole } = render(
273+
<DxcNumberInput label="Number input label" min={-10} max={-1} step={0.5} />
274+
);
275+
const number = getByLabelText("Number input label");
276+
const increment = getAllByRole("button")[1];
277+
userEvent.click(increment);
278+
expect(number.value).toBe("-1");
279+
userEvent.click(increment);
280+
expect(number.value).toBe("-1");
281+
});
282+
283+
test("Start decrementing from 0 when the min value is less than 0 and the max value is bigger than 0", () => {
284+
const { getByLabelText, getAllByRole } = render(
285+
<DxcNumberInput label="Number input label" min={-10} max={10} step={1} />
286+
);
287+
const number = getByLabelText("Number input label");
288+
const decrement = getAllByRole("button")[0];
289+
userEvent.click(decrement);
290+
expect(number.value).toBe("-1");
291+
});
292+
293+
test("Start decrementing from 0 when the min value is 0 and the max value is bigger than 0", () => {
294+
const { getByLabelText, getAllByRole } = render(
295+
<DxcNumberInput label="Number input label" min={0} max={10} step={1} />
296+
);
297+
const number = getByLabelText("Number input label");
298+
const decrement = getAllByRole("button")[0];
299+
userEvent.click(decrement);
300+
expect(number.value).toBe("0");
301+
});
302+
303+
test("Start decrementing from the min value when it is bigger than 0", () => {
304+
const { getByLabelText, getAllByRole } = render(
305+
<DxcNumberInput label="Number input label" min={2} max={10} step={0.5} />
306+
);
307+
const number = getByLabelText("Number input label");
308+
const decrement = getAllByRole("button")[0];
309+
userEvent.click(decrement);
310+
expect(number.value).toBe("2");
311+
userEvent.click(decrement);
312+
expect(number.value).toBe("2");
313+
});
314+
315+
test("Start decrementing from the max value when it is less than 0", () => {
316+
const { getByLabelText, getAllByRole } = render(
317+
<DxcNumberInput label="Number input label" min={-10} max={-1} step={0.5} />
318+
);
319+
const number = getByLabelText("Number input label");
320+
const decrement = getAllByRole("button")[0];
321+
userEvent.click(decrement);
322+
expect(number.value).toBe("-1");
323+
userEvent.click(decrement);
324+
expect(number.value).toBe("-1.5");
224325
});
225326

226327
test("Increment and decrement the value with min, max and step using the arrows in keyboard", () => {
@@ -247,9 +348,10 @@ describe("Number input component tests", () => {
247348
expect(number.value).toBe("5");
248349
});
249350

250-
test("Number has correct accesibility attributes", () => {
351+
test("Number has correct accessibility attributes", () => {
251352
const { getByLabelText, getAllByRole } = render(<DxcNumberInput label="Number input label" />);
252353
const number = getByLabelText("Number input label");
354+
expect(number.getAttribute("type")).toBe("number");
253355
expect(number.getAttribute("aria-autocomplete")).toBeNull();
254356
expect(number.getAttribute("aria-controls")).toBeNull();
255357
expect(number.getAttribute("aria-expanded")).toBeNull();
@@ -259,7 +361,7 @@ describe("Number input component tests", () => {
259361
expect(increment.getAttribute("aria-label")).toBe("Increment value");
260362
});
261363

262-
test("Number input submits correct values in a form", () => {
364+
test("Number input submits correct values inside a form and actions don't trigger the submit event", () => {
263365
const handlerOnSubmit = jest.fn((e) => {
264366
e.preventDefault();
265367
const formData = new FormData(e.target);

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.

0 commit comments

Comments
 (0)