Skip to content

Commit 9466505

Browse files
authored
feat(NumberInput): add support for defaultStartValue (#19963)
* feat(NumberInput): add support for defaultStartValue * chore: updated the stepper button logic * chore: updated test cases * chore: updated based on comments * chore: updated for type number
1 parent c13a748 commit 9466505

File tree

5 files changed

+119
-20
lines changed

5 files changed

+119
-20
lines changed

packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5962,6 +5962,9 @@ Map {
59625962
"step": Object {
59635963
"type": "number",
59645964
},
5965+
"stepStartValue": Object {
5966+
"type": "number",
5967+
},
59655968
"translateWithId": Object {
59665969
"type": "func",
59675970
},

packages/react/src/components/NumberInput/NumberInput.mdx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,18 @@ For example, percent style can be used:
119119
/>
120120
```
121121

122+
### Configurable start value with stepStartValue
123+
124+
A step start value can be configured to provide the user a more beneficial starting point when the input is empty and an increment or decrement button is clicked. This can be used in situations where zero is not within the min/max or there is a more sensible default starting value, such as the current year when asking the user for a year. Since this allows the input to still initially be empty, it ensures on submission of the form that the user has actually interacted with the input and inserted a value.
125+
126+
```
127+
<NumberInput
128+
type="text"
129+
value={1000}
130+
stepStartValue={10}
131+
/>
132+
```
133+
122134
## Component API
123135

124136
<ArgTypes />

packages/react/src/components/NumberInput/NumberInput.stories.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ WithTypeOfText.args = {
212212
};
213213
WithTypeOfText.argTypes = {
214214
locale: { control: { type: 'text' } },
215+
stepStartValue: { control: { type: 'number' } },
215216
formatOptions: { control: { type: 'object' } },
216217
...sharedArgTypes,
217218
};

packages/react/src/components/NumberInput/NumberInput.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ export interface NumberInputProps
109109
*/
110110
formatOptions?: NumberFormatOptions;
111111

112+
/**
113+
* Provide the value stepping should begin at when the input is empty
114+
*/
115+
stepStartValue?: number;
116+
112117
/**
113118
* Provide text that is used alongside the control label for additional help
114119
*/
@@ -309,6 +314,7 @@ const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
309314
defaultValue = type === 'number' ? 0 : NaN,
310315
warn = false,
311316
warnText = '',
317+
stepStartValue = 0,
312318
value: controlledValue,
313319
...rest
314320
} = props;
@@ -526,14 +532,18 @@ const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
526532
type === 'number' ? Number(inputRef.current.value) : numberValue;
527533

528534
let rawValue;
529-
if (Number.isNaN(currentValue)) {
530-
// When the field is empty (NaN), incrementing begins at min,
531-
// decrementing begins at max.
532-
// When there's no min or max to use, it begins at 0.
533-
if (direction === `up` && min) {
535+
if (Number.isNaN(currentValue) || !currentValue) {
536+
if (typeof stepStartValue === 'number' && stepStartValue) {
537+
rawValue = stepStartValue;
538+
} else if (
539+
(min && min < 0 && max && max > 0) ||
540+
(!max && !min) ||
541+
max
542+
) {
543+
if (direction === `up`) rawValue = 1;
544+
if (direction === `down`) rawValue = -1;
545+
} else if ((min && min > 0 && max && max > 0) || min) {
534546
rawValue = min;
535-
} else if (direction === `down` && max) {
536-
rawValue = max;
537547
} else {
538548
rawValue = 0;
539549
}
@@ -909,6 +919,11 @@ NumberInput.propTypes = {
909919
*/
910920
min: PropTypes.number,
911921

922+
/**
923+
* Provide the value stepping should begin at when the input is empty
924+
*/
925+
stepStartValue: PropTypes.number,
926+
912927
/**
913928
* Provide an optional handler that is called when the input or stepper
914929
* buttons are blurred.

packages/react/src/components/NumberInput/__tests__/NumberInput-test.js

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ describe('NumberInput', () => {
920920
expect(screen.getByLabelText('test-label')).toHaveValue('0');
921921
});
922922

923-
it('should begin incrementing from min when input is empty', async () => {
923+
it('should begin incrementing from 1 when input is empty and 0 is in between of min and max', async () => {
924924
render(
925925
<NumberInput
926926
type="text"
@@ -935,9 +935,27 @@ describe('NumberInput', () => {
935935
);
936936
expect(screen.getByLabelText('test-label')).toHaveValue('');
937937
await userEvent.click(screen.getByLabelText('increment'));
938-
expect(screen.getByLabelText('test-label')).toHaveValue('-100');
938+
expect(screen.getByLabelText('test-label')).toHaveValue('1');
939939
});
940-
it('should begin decrementing from max when input is empty', async () => {
940+
941+
it('should begin incrementing from min when input is empty and min is positive', async () => {
942+
render(
943+
<NumberInput
944+
type="text"
945+
label="test-label"
946+
id="test"
947+
allowEmpty
948+
min={10}
949+
max={100}
950+
step={2}
951+
translateWithId={translateWithId}
952+
/>
953+
);
954+
expect(screen.getByLabelText('test-label')).toHaveValue('');
955+
await userEvent.click(screen.getByLabelText('increment'));
956+
expect(screen.getByLabelText('test-label')).toHaveValue('10');
957+
});
958+
it('should begin decrementing from max when input is empty and when min is negative', async () => {
941959
render(
942960
<NumberInput
943961
type="text"
@@ -951,35 +969,85 @@ describe('NumberInput', () => {
951969
);
952970
expect(screen.getByLabelText('test-label')).toHaveValue('');
953971
await userEvent.click(screen.getByLabelText('decrement'));
954-
expect(screen.getByLabelText('test-label')).toHaveValue('100');
972+
expect(screen.getByLabelText('test-label')).toHaveValue('-1');
973+
});
974+
975+
it('should begin decrementing from min when input is empty and when min and max is greater than 0', async () => {
976+
render(
977+
<NumberInput
978+
type="text"
979+
label="test-label"
980+
id="test"
981+
min={10}
982+
max={100}
983+
step={2}
984+
translateWithId={translateWithId}
985+
/>
986+
);
987+
expect(screen.getByLabelText('test-label')).toHaveValue('');
988+
await userEvent.click(screen.getByLabelText('decrement'));
989+
expect(screen.getByLabelText('test-label')).toHaveValue('10');
990+
});
991+
992+
it('should begin incrementing from 1 when no min is provided and input is empty', async () => {
993+
render(
994+
<NumberInput
995+
type="text"
996+
label="test-label"
997+
id="test"
998+
step={2}
999+
translateWithId={translateWithId}
1000+
/>
1001+
);
1002+
expect(screen.getByLabelText('test-label')).toHaveValue('');
1003+
await userEvent.click(screen.getByLabelText('increment'));
1004+
expect(screen.getByLabelText('test-label')).toHaveValue('1');
1005+
});
1006+
it('should begin decrementing from -1 when no max is provided and input is empty', async () => {
1007+
render(
1008+
<NumberInput
1009+
type="text"
1010+
label="test-label"
1011+
id="test"
1012+
step={2}
1013+
translateWithId={translateWithId}
1014+
/>
1015+
);
1016+
expect(screen.getByLabelText('test-label')).toHaveValue('');
1017+
await userEvent.click(screen.getByLabelText('decrement'));
1018+
expect(screen.getByLabelText('test-label')).toHaveValue('-1');
9551019
});
956-
it('should begin incrementing from 0 when no min is provided and input is empty', async () => {
1020+
1021+
it('should begin incrementing from stepStartValue when input is empty and stepStartValue is provided', async () => {
9571022
render(
9581023
<NumberInput
9591024
type="text"
9601025
label="test-label"
9611026
id="test"
9621027
step={2}
1028+
stepStartValue={10}
9631029
translateWithId={translateWithId}
9641030
/>
9651031
);
9661032
expect(screen.getByLabelText('test-label')).toHaveValue('');
9671033
await userEvent.click(screen.getByLabelText('increment'));
968-
expect(screen.getByLabelText('test-label')).toHaveValue('0');
1034+
expect(screen.getByLabelText('test-label')).toHaveValue('10');
9691035
});
970-
it('should begin decrementing from 0 when no max is provided and input is empty', async () => {
1036+
1037+
it('should begin decrementing from stepStartValue when input is empty and stepStartValue is provided', async () => {
9711038
render(
9721039
<NumberInput
9731040
type="text"
9741041
label="test-label"
9751042
id="test"
9761043
step={2}
1044+
stepStartValue={10}
9771045
translateWithId={translateWithId}
9781046
/>
9791047
);
9801048
expect(screen.getByLabelText('test-label')).toHaveValue('');
9811049
await userEvent.click(screen.getByLabelText('decrement'));
982-
expect(screen.getByLabelText('test-label')).toHaveValue('0');
1050+
expect(screen.getByLabelText('test-label')).toHaveValue('10');
9831051
});
9841052
});
9851053
it('should increase by the value of large step and format to the default locale', async () => {
@@ -1122,7 +1190,7 @@ describe('NumberInput', () => {
11221190
type="text"
11231191
label="NumberInput label"
11241192
id="number-input"
1125-
min={-100}
1193+
min={10}
11261194
max={100}
11271195
value={value}
11281196
onChange={(event, state) => {
@@ -1147,17 +1215,17 @@ describe('NumberInput', () => {
11471215
expect(input).toHaveValue('');
11481216

11491217
await userEvent.click(screen.getByLabelText('increment'));
1150-
expect(input).toHaveValue('-100');
1218+
expect(input).toHaveValue('10');
11511219
await userEvent.click(screen.getByLabelText('increment'));
1152-
expect(input).toHaveValue('-99');
1220+
expect(input).toHaveValue('11');
11531221

11541222
await userEvent.clear(input);
11551223
expect(input).toHaveValue('');
11561224

11571225
await userEvent.click(screen.getByLabelText('decrement'));
1158-
expect(input).toHaveValue('100');
1226+
expect(input).toHaveValue('10');
11591227
await userEvent.click(screen.getByLabelText('decrement'));
1160-
expect(input).toHaveValue('99');
1228+
expect(input).toHaveValue('10');
11611229

11621230
await userEvent.click(screen.getByText('set to 50'));
11631231
expect(input).toHaveValue('50');

0 commit comments

Comments
 (0)