Skip to content

Commit 3c238fe

Browse files
authored
fix(numberfield): preserve validation errors on blur when value unchanged (adobe#9798)
1 parent f8bb8ae commit 3c238fe

File tree

2 files changed

+41
-2
lines changed

2 files changed

+41
-2
lines changed

packages/@react-stately/numberfield/src/useNumberFieldState.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,14 @@ export function useNumberFieldState(
175175
}
176176

177177
clampedValue = numberParser.parse(format(clampedValue));
178+
let shouldValidate = clampedValue !== numberValue;
178179
setNumberValue(clampedValue);
179180

180181
// in a controlled state, the numberValue won't change, so we won't go back to our old input without help
181182
setInputValue(format(value === undefined ? clampedValue : numberValue));
182-
validation.commitValidation();
183+
if (shouldValidate) {
184+
validation.commitValidation();
185+
}
183186
};
184187

185188
let safeNextStep = (operation: '+' | '-', minMax: number = 0) => {

packages/react-aria-components/test/NumberField.test.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
jest.mock('@react-aria/live-announcer');
1414
import {act, pointerMap, render} from '@react-spectrum/test-utils-internal';
1515
import {announce} from '@react-aria/live-announcer';
16-
import {Button, FieldError, Group, Input, Label, NumberField, NumberFieldContext, Text} from '../';
16+
import {Button, FieldError, Form, Group, Input, Label, NumberField, NumberFieldContext, Text} from '../';
1717
import React from 'react';
1818
import userEvent from '@testing-library/user-event';
1919

@@ -250,4 +250,40 @@ describe('NumberField', () => {
250250
await user.keyboard('{Enter}');
251251
expect(input).toHaveValue('200');
252252
});
253+
254+
it('should not reset validation errors on blur when value has not changed', async () => {
255+
let {getByRole} = render(
256+
<Form validationErrors={{testNumber: 'This field has an error.'}}>
257+
<NumberField name="testNumber" defaultValue={5}>
258+
<Label>Test Number</Label>
259+
<Group>
260+
<Button slot="decrement">-</Button>
261+
<Input />
262+
<Button slot="increment">+</Button>
263+
</Group>
264+
<FieldError />
265+
</NumberField>
266+
</Form>
267+
);
268+
269+
let input = getByRole('textbox');
270+
let numberfield = input.closest('.react-aria-NumberField');
271+
272+
// Validation error should be displayed
273+
expect(numberfield).toHaveAttribute('data-invalid');
274+
expect(input).toHaveAttribute('aria-describedby');
275+
expect(document.getElementById(input.getAttribute('aria-describedby').split(' ')[0])).toHaveTextContent('This field has an error.');
276+
277+
// Focus the field without changing the value
278+
act(() => { input.focus(); });
279+
expect(numberfield).toHaveAttribute('data-invalid');
280+
281+
// Blur the field without changing the value
282+
act(() => { input.blur(); });
283+
284+
// Validation error should still be displayed because the value didn't change
285+
expect(numberfield).toHaveAttribute('data-invalid');
286+
expect(input).toHaveAttribute('aria-describedby');
287+
expect(document.getElementById(input.getAttribute('aria-describedby').split(' ')[0])).toHaveTextContent('This field has an error.');
288+
});
253289
});

0 commit comments

Comments
 (0)