Skip to content

Commit 9a4c030

Browse files
authored
Add invalid prop to Combobox component (#3677)
This PR adds the `invalid` prop to the `Combobox` component. This will also expose the `invalid` value as a render prop to the `Combobox.Input` and `Combobox.Button` components. It will also expose the `data-invalid` attribute on these components when the `invalid` prop is set to `true`. ```tsx <Combobox invalid> <Combobox.Input /> <Combobox.Button /> </Combobox> ``` Without `invalid` prop: <img width="916" alt="image" src="https://github.com/user-attachments/assets/2c199691-7b29-450f-89a5-4b84e6704c6a" /> With invalid prop: <img width="913" alt="image" src="https://github.com/user-attachments/assets/4bdde518-39b3-4998-b353-604a818a3c99" /> Notice the `data-invalid` prop on the `<input>` and the `<button>`.
1 parent 0a8de01 commit 9a4c030

File tree

3 files changed

+19
-4
lines changed

3 files changed

+19
-4
lines changed

packages/@headlessui-react/CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
### Fixed
1616

1717
- Use correct `ownerDocument` when using internal `Portal` component ([#3594](https://github.com/tailwindlabs/headlessui/pull/3594))
18-
- Bump `@tanstack/react-virtual` to be fix warnings in React 19 projects ([#3588](https://github.com/tailwindlabs/headlessui/pull/3588))
18+
- Bump `@tanstack/react-virtual` to fix warnings in React 19 projects ([#3588](https://github.com/tailwindlabs/headlessui/pull/3588))
1919
- Fix `aria-invalid` attributes to have a valid `'true'` value ([#3639](https://github.com/tailwindlabs/headlessui/pull/3639))
20+
- Add missing `invalid` prop to `Combobox` component ([#3677](https://github.com/tailwindlabs/headlessui/pull/3677))
2021

2122
## [2.2.0] - 2024-10-25
2223

packages/@headlessui-react/src/components/combobox/combobox.test.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,7 @@ describe('Rendering', () => {
10451045
open: false,
10461046
active: false,
10471047
disabled: false,
1048+
invalid: false,
10481049
value: 'test',
10491050
hover: false,
10501051
focus: false,
@@ -1061,6 +1062,7 @@ describe('Rendering', () => {
10611062
open: true,
10621063
active: true,
10631064
disabled: false,
1065+
invalid: false,
10641066
value: 'test',
10651067
hover: false,
10661068
focus: false,
@@ -1094,6 +1096,7 @@ describe('Rendering', () => {
10941096
open: false,
10951097
active: false,
10961098
disabled: false,
1099+
invalid: false,
10971100
value: 'test',
10981101
hover: false,
10991102
focus: false,
@@ -1110,6 +1113,7 @@ describe('Rendering', () => {
11101113
open: true,
11111114
active: true,
11121115
disabled: false,
1116+
invalid: false,
11131117
value: 'test',
11141118
hover: false,
11151119
focus: false,

packages/@headlessui-react/src/components/combobox/combobox.tsx

+13-3
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@ let ComboboxDataContext = createContext<
577577
value: unknown
578578
defaultValue: unknown
579579
disabled: boolean
580+
invalid: boolean
580581
mode: ValueMode
581582
activeOptionIndex: number | null
582583
immediate: boolean
@@ -619,6 +620,7 @@ let DEFAULT_COMBOBOX_TAG = Fragment
619620
type ComboboxRenderPropArg<TValue, TActive = TValue> = {
620621
open: boolean
621622
disabled: boolean
623+
invalid: boolean
622624
activeIndex: number | null
623625
activeOption: TActive | null
624626
value: TValue
@@ -648,6 +650,7 @@ export type ComboboxProps<
648650

649651
multiple?: TMultiple
650652
disabled?: boolean
653+
invalid?: boolean
651654
form?: string
652655
name?: string
653656
immediate?: boolean
@@ -676,6 +679,7 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
676679
form,
677680
name,
678681
by,
682+
invalid = false,
679683
disabled = providedDisabled || false,
680684
onClose,
681685
__demoMode = false,
@@ -751,6 +755,7 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
751755
value,
752756
defaultValue,
753757
disabled,
758+
invalid,
754759
mode: multiple ? ValueMode.Multi : ValueMode.Single,
755760
virtual: virtual ? state.virtual : null,
756761
get activeOptionIndex() {
@@ -785,7 +790,7 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
785790
isSelected,
786791
isActive,
787792
}),
788-
[value, defaultValue, disabled, multiple, __demoMode, state, virtual]
793+
[value, defaultValue, disabled, invalid, multiple, __demoMode, state, virtual]
789794
)
790795

791796
useIsoMorphicEffect(() => {
@@ -813,6 +818,7 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
813818
return {
814819
open: data.comboboxState === ComboboxState.Open,
815820
disabled,
821+
invalid,
816822
activeIndex: data.activeOptionIndex,
817823
activeOption:
818824
data.activeOptionIndex === null
@@ -822,7 +828,7 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
822828
: (data.options[data.activeOptionIndex]?.dataRef.current.value as TValue) ?? null,
823829
value,
824830
} satisfies ComboboxRenderPropArg<unknown>
825-
}, [data, disabled, value])
831+
}, [data, disabled, value, invalid])
826832

827833
let selectActiveOption = useEvent(() => {
828834
if (data.activeOptionIndex === null) return
@@ -999,6 +1005,7 @@ let DEFAULT_INPUT_TAG = 'input' as const
9991005
type InputRenderPropArg = {
10001006
open: boolean
10011007
disabled: boolean
1008+
invalid: boolean
10021009
hover: boolean
10031010
focus: boolean
10041011
autofocus: boolean
@@ -1398,11 +1405,12 @@ function InputFn<
13981405
return {
13991406
open: data.comboboxState === ComboboxState.Open,
14001407
disabled,
1408+
invalid: data.invalid,
14011409
hover,
14021410
focus,
14031411
autofocus: autoFocus,
14041412
} satisfies InputRenderPropArg
1405-
}, [data, hover, focus, autoFocus, disabled])
1413+
}, [data, hover, focus, autoFocus, disabled, data.invalid])
14061414

14071415
let ourProps = mergeProps(
14081416
{
@@ -1463,6 +1471,7 @@ type ButtonRenderPropArg = {
14631471
open: boolean
14641472
active: boolean
14651473
disabled: boolean
1474+
invalid: boolean
14661475
value: any
14671476
focus: boolean
14681477
hover: boolean
@@ -1587,6 +1596,7 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
15871596
open: data.comboboxState === ComboboxState.Open,
15881597
active: active || data.comboboxState === ComboboxState.Open,
15891598
disabled,
1599+
invalid: data.invalid,
15901600
value: data.value,
15911601
hover,
15921602
focus,

0 commit comments

Comments
 (0)