Skip to content

Commit

Permalink
fix: checkbox validation reset not working (#7268)
Browse files Browse the repository at this point in the history
* commit validation on checkbox click

* add realtime validation test

* remove options from render

* fix duplicate import

* fix import order

* remove unused var

* remove unused var

* sleep before asserting validation state

* remove sleep

* enhance checkbox test render

* fix grouped scenario in tests

* remove useCheckbox from test scenario

* remove import

* fix: test suite and validation reset on press

* fix: excessive state chain

* fix: remove state piggyback in favor of onPress

* chore: fix import eslint

* chore: fix sort imports

* chore: lint

* Update packages/@react-aria/checkbox/src/useCheckbox.ts

Co-authored-by: Daniel Lu <[email protected]>

---------

Co-authored-by: Daniel Lu <[email protected]>
Co-authored-by: Daniel Lu <[email protected]>
  • Loading branch information
3 people authored Nov 13, 2024
1 parent 3949762 commit 262cc75
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 6 deletions.
21 changes: 19 additions & 2 deletions packages/@react-aria/checkbox/src/useCheckbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@

import {AriaCheckboxProps} from '@react-types/checkbox';
import {InputHTMLAttributes, LabelHTMLAttributes, useEffect} from 'react';
import {mergeProps} from '@react-aria/utils';
import {privateValidationStateProp, useFormValidationState} from '@react-stately/form';
import {RefObject, ValidationResult} from '@react-types/shared';
import {ToggleState} from '@react-stately/toggle';
import {useFormValidation} from '@react-aria/form';
import {useFormValidationState} from '@react-stately/form';
import {usePress} from '@react-aria/interactions';
import {useToggle} from '@react-aria/toggle';

export interface CheckboxAria extends ValidationResult {
Expand Down Expand Up @@ -61,8 +63,23 @@ export function useCheckbox(props: AriaCheckboxProps, state: ToggleState, inputR
}
});

// Reset validation state on label press for checkbox with a hidden input.
let {pressProps} = usePress({
isDisabled: isDisabled || isReadOnly,
onPress() {
// @ts-expect-error
let {[privateValidationStateProp]: groupValidationState} = props;

let {commitValidation} = groupValidationState
? groupValidationState
: validationState;

commitValidation();
}
});

return {
labelProps,
labelProps: mergeProps(labelProps, pressProps),
inputProps: {
...inputProps,
checked: isSelected,
Expand Down
36 changes: 32 additions & 4 deletions packages/@react-aria/checkbox/test/useCheckboxGroup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ import userEvent from '@testing-library/user-event';
function Checkbox({checkboxGroupState, ...props}: AriaCheckboxGroupItemProps & { checkboxGroupState: CheckboxGroupState }) {
const ref = useRef<HTMLInputElement>(null);
const {children} = props;
const {inputProps} = useCheckboxGroupItem(props, checkboxGroupState, ref);
return <label><input ref={ref} {...inputProps} />{children}</label>;
props.validationBehavior ??= 'native';
const {inputProps, labelProps} = useCheckboxGroupItem(props, checkboxGroupState, ref);
return <label {...labelProps}><input ref={ref} {...inputProps} />{children}</label>;
}

function CheckboxGroup({groupProps, checkboxProps}: {groupProps: AriaCheckboxGroupProps, checkboxProps: AriaCheckboxGroupItemProps[]}) {
const state = useCheckboxGroupState(groupProps);
const {groupProps: checkboxGroupProps, labelProps} = useCheckboxGroup(groupProps, state);
const {groupProps: checkboxGroupProps, labelProps, isInvalid} = useCheckboxGroup(groupProps, state);
return (
<div {...checkboxGroupProps}>
<div {...checkboxGroupProps} data-invalid={isInvalid || undefined}>
{groupProps.label && <span {...labelProps}>{groupProps.label}</span>}
<Checkbox checkboxGroupState={state} {...checkboxProps[0]} />
<Checkbox checkboxGroupState={state} {...checkboxProps[1]} />
Expand Down Expand Up @@ -281,4 +282,31 @@ describe('useCheckboxGroup', () => {
expect(checkboxOnChangeSpy).toHaveBeenCalledTimes(0);
expect(checkboxes[2].checked).toBeFalsy();
});

it('should re-validate in realtime', async () => {
let {getByRole, getByLabelText} = render(
<CheckboxGroup
groupProps={{label: 'Favorite Pet', isRequired: true}}
checkboxProps={[
{value: 'dogs', children: 'Dogs'},
{value: 'cats', children: 'Cats'},
{value: 'dragons', children: 'Dragons'}
]} />
);

let checkboxGroup = getByRole('group');
let dragons = getByLabelText('Dragons');

await user.click(dragons);

expect(checkboxGroup).not.toHaveAttribute('data-invalid');

await user.click(dragons);

expect(checkboxGroup).toHaveAttribute('data-invalid', 'true');

await user.click(dragons);

expect(checkboxGroup).not.toHaveAttribute('data-invalid');
});
});

1 comment on commit 262cc75

@rspbot
Copy link

@rspbot rspbot commented on 262cc75 Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.