/* * Copyright 2020 Adobe. All rights reserved. * This file is licensed to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. You may obtain a copy * of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ import {AriaCheckboxGroupItemProps} from '@react-types/checkbox'; import {CheckboxAria, useCheckbox} from './useCheckbox'; import {checkboxGroupData} from './utils'; import {CheckboxGroupState} from '@react-stately/checkbox'; import {DEFAULT_VALIDATION_RESULT, privateValidationStateProp, useFormValidationState} from '@react-stately/form'; import {RefObject, ValidationResult} from '@react-types/shared'; import {useEffect, useRef} from 'react'; import {useToggleState} from '@react-stately/toggle'; /** * Provides the behavior and accessibility implementation for a checkbox component contained within a checkbox group. * Checkbox groups allow users to select multiple items from a list of options. * @param props - Props for the checkbox. * @param state - State for the checkbox, as returned by `useCheckboxGroupState`. * @param inputRef - A ref for the HTML input element. */ export function useCheckboxGroupItem(props: AriaCheckboxGroupItemProps, state: CheckboxGroupState, inputRef: RefObject): CheckboxAria { const toggleState = useToggleState({ isReadOnly: props.isReadOnly || state.isReadOnly, isSelected: state.isSelected(props.value), defaultSelected: state.defaultValue.includes(props.value), onChange(isSelected) { if (isSelected) { state.addValue(props.value); } else { state.removeValue(props.value); } if (props.onChange) { props.onChange(isSelected); } } }); let {name, form, descriptionId, errorMessageId, validationBehavior} = checkboxGroupData.get(state)!; validationBehavior = props.validationBehavior ?? validationBehavior; // Local validation for this checkbox. let {realtimeValidation} = useFormValidationState({ ...props, value: toggleState.isSelected, // Server validation is handled at the group level. name: undefined, validationBehavior: 'aria' }); // Update the checkbox group state when realtime validation changes. let nativeValidation = useRef(DEFAULT_VALIDATION_RESULT); let updateValidation = () => { state.setInvalid(props.value, realtimeValidation.isInvalid ? realtimeValidation : nativeValidation.current); }; useEffect(updateValidation); // Combine group and checkbox level validation. let combinedRealtimeValidation = state.realtimeValidation.isInvalid ? state.realtimeValidation : realtimeValidation; let displayValidation = validationBehavior === 'native' ? state.displayValidation : combinedRealtimeValidation; let res = useCheckbox({ ...props, isReadOnly: props.isReadOnly || state.isReadOnly, isDisabled: props.isDisabled || state.isDisabled, name: props.name || name, form: props.form || form, isRequired: props.isRequired ?? state.isRequired, validationBehavior, [privateValidationStateProp]: { realtimeValidation: combinedRealtimeValidation, displayValidation, resetValidation: state.resetValidation, commitValidation: state.commitValidation, updateValidation(v: ValidationResult) { nativeValidation.current = v; updateValidation(); } } }, toggleState, inputRef); return { ...res, inputProps: { ...res.inputProps, 'aria-describedby': [ props['aria-describedby'], state.isInvalid ? errorMessageId : null, descriptionId ].filter(Boolean).join(' ') || undefined } }; }