diff --git a/packages/components/autocomplete/src/use-autocomplete.ts b/packages/components/autocomplete/src/use-autocomplete.ts index 65052b4ae3..acc530b20f 100644 --- a/packages/components/autocomplete/src/use-autocomplete.ts +++ b/packages/components/autocomplete/src/use-autocomplete.ts @@ -3,7 +3,6 @@ import type {AutocompleteVariantProps, SlotsToClasses, AutocompleteSlots} from " import {DOMAttributes, HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system"; import {autocomplete} from "@nextui-org/theme"; import {useFilter} from "@react-aria/i18n"; -import {useComboBox} from "@react-aria/combobox"; import {FilterFn, useComboBoxState} from "@react-stately/combobox"; import {ReactRef, useDOMRef} from "@nextui-org/react-utils"; import {ReactNode, useCallback, useEffect, useMemo, useRef} from "react"; @@ -16,6 +15,7 @@ import {ScrollShadowProps} from "@nextui-org/scroll-shadow"; import {chain, mergeProps} from "@react-aria/utils"; import {ButtonProps} from "@nextui-org/button"; import {AsyncLoadable, PressEvent} from "@react-types/shared"; +import {useComboBox} from "@react-aria/combobox"; interface Props extends Omit, keyof ComboBoxProps> { /** @@ -154,6 +154,7 @@ export function useAutocomplete(originalProps: UseAutocomplete allowsCustomValue = false, className, classNames, + errorMessage, onOpenChange, onClose, isReadOnly = false, @@ -193,6 +194,26 @@ export function useAutocomplete(originalProps: UseAutocomplete const inputRef = useDOMRef(ref); const scrollShadowRef = useDOMRef(scrollRefProp); + const { + buttonProps, + inputProps, + listBoxProps, + isInvalid: isAriaInvalid, + validationDetails, + validationErrors, + } = useComboBox( + { + ...originalProps, + inputRef, + buttonRef, + listBoxRef, + popoverRef, + }, + state, + ); + + const isInvalid = originalProps.isInvalid || isAriaInvalid; + const slotsProps: { inputProps: InputProps; popoverProps: UseAutocompleteProps["popoverProps"]; @@ -248,7 +269,7 @@ export function useAutocomplete(originalProps: UseAutocomplete size: "sm", variant: "light", radius: "full", - color: originalProps?.isInvalid ? "danger" : originalProps?.color, + color: isInvalid ? "danger" : originalProps?.color, isIconOnly: true, disableAnimation, }, @@ -259,7 +280,7 @@ export function useAutocomplete(originalProps: UseAutocomplete size: "sm", variant: "light", radius: "full", - color: originalProps?.isInvalid ? "danger" : originalProps?.color, + color: isInvalid ? "danger" : originalProps?.color, isIconOnly: true, disableAnimation, }, @@ -290,17 +311,6 @@ export function useAutocomplete(originalProps: UseAutocomplete } }, [isOpen, allowsCustomValue]); - const {buttonProps, inputProps, listBoxProps} = useComboBox( - { - ...originalProps, - inputRef, - buttonRef, - listBoxRef, - popoverRef, - }, - state, - ); - const Component = as || "div"; const slots = useMemo( @@ -328,7 +338,7 @@ export function useAutocomplete(originalProps: UseAutocomplete ); const getBaseProps: PropGetter = () => ({ - "data-invalid": dataAttr(originalProps?.isInvalid), + "data-invalid": dataAttr(isInvalid), "data-open": dataAttr(state.isOpen), className: slots.base({class: baseStyles}), }); @@ -369,6 +379,11 @@ export function useAutocomplete(originalProps: UseAutocomplete ...otherProps, ...inputProps, ...slotsProps.inputProps, + isInvalid, + errorMessage: + typeof errorMessage === "function" + ? errorMessage({isInvalid, validationErrors, validationDetails}) + : errorMessage || validationErrors.join(" "), onClick: chain(slotsProps.inputProps.onClick, otherProps.onClick), } as unknown as InputProps); diff --git a/packages/components/autocomplete/stories/autocomplete.stories.tsx b/packages/components/autocomplete/stories/autocomplete.stories.tsx index db9186346c..63a5c625fb 100644 --- a/packages/components/autocomplete/stories/autocomplete.stories.tsx +++ b/packages/components/autocomplete/stories/autocomplete.stories.tsx @@ -60,6 +60,12 @@ export default { type: "boolean", }, }, + validationBehavior: { + control: { + type: "select", + }, + options: ["aria", "native"], + }, }, decorators: [ (Story) => ( @@ -762,6 +768,7 @@ export const WithErrorMessage = { args: { ...defaultProps, + isInvalid: true, errorMessage: "Please select an animal", }, };