Repository: tailwindlabs/headlessui
Pull request #3100 (2024-04-14 13:29:49)

Omit `nullable` prop from `Combobox` component
We deprecated the `nullable` prop because this is now the default behavior. However, we didn't delete the prop from the incoming `props`. This means that the `nullable` prop will end up in the underlying DOM node, or worse the `Fragment` (default tag) and error.

This PR fixes that by eating the `nullable` prop so that it never ends up on the underlying DOM node or `Fragment`.


vercel[bot]: [vc]: #euBG4iCEbOBODsZTyYGMtWmxN9GHTgauGgVwstaQjsE=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJoZWFkbGVzc3VpLXZ1ZSIsInJvb3REaXJlY3RvcnkiOiJwbGF5Z3JvdW5kcy92dWUiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vdGFpbHdpbmRsYWJzL2hlYWRsZXNzdWktdnVlLzJrUzZqUk1NR0syZnZDSDFZUkxvZm1haWhkdnAiLCJwcmV2aWV3VXJsIjoiaGVhZGxlc3N1aS12dWUtZ2l0LWZpeC1kcm9wLWNvbWJvYm94LW51bGxhYmxlLXByb3AtdGFpbHdpbmRsYWJzLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiUEVORElORyIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJoZWFkbGVzc3VpLXZ1ZS1naXQtZml4LWRyb3AtY29tYm9ib3gtbnVsbGFibGUtcHJvcC10YWlsd2luZGxhYnMudmVyY2VsLmFwcCJ9fV19
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **headlessui-vue** | 🔄 Building ([Inspect](https://vercel.com/tailwindlabs/headlessui-vue/2kS6jRMMGK2fvCH1YRLofmaihdvp)) | [Visit Preview](https://vercel.live/open-feedback/headlessui-vue-git-fix-drop-combobox-nullable-prop-tailwindlabs.vercel.app?via=pr-comment-visit-preview-link&passThrough=1) | 💬 [**Add feedback**](https://vercel.live/open-feedback/headlessui-vue-git-fix-drop-combobox-nullable-prop-tailwindlabs.vercel.app?via=pr-comment-feedback-link) | Apr 14, 2024 1:29pm |



Patch:
diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md
index d74d9a8..7b77146 100644
--- a/packages/@headlessui-react/CHANGELOG.md
+++ b/packages/@headlessui-react/CHANGELOG.md
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Fix enter transitions for the `Transition` component ([#3074](https://github.com/tailwindlabs/headlessui/pull/3074))
 - Render hidden form input fields for `Checkbox`, `Switch` and `RadioGroup` components ([#3095](https://github.com/tailwindlabs/headlessui/pull/3095))
 - Ensure the `multiple` prop is typed correctly when passing explicit types to the `Combobox` component ([#3099](https://github.com/tailwindlabs/headlessui/pull/3099))
+- Omit `nullable` prop from `Combobox` component ([#3100](https://github.com/tailwindlabs/headlessui/pull/3100))
 
 ### Changed
 
diff --git a/packages/@headlessui-react/src/components/combobox/combobox.tsx b/packages/@headlessui-react/src/components/combobox/combobox.tsx
index 7eb1362..cacee2b 100644
--- a/packages/@headlessui-react/src/components/combobox/combobox.tsx
+++ b/packages/@headlessui-react/src/components/combobox/combobox.tsx
@@ -608,6 +608,9 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
     multiple = false,
     immediate = false,
     virtual = null,
+    // Deprecated, but let's pluck it from the props such that it doesn't end up
+    // on the `Fragment`
+    nullable: _nullable,
     ...theirProps
   } = props
   let [value = multiple ? [] : undefined, theirOnChange] = useControllable<any>(


Test patch:
diff --git a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx
index 76d4dfc..6da3f5e 100644
--- a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx
+++ b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx
@@ -1,5 +1,5 @@
 import { render } from '@testing-library/react'
-import React, { createElement, useEffect, useState } from 'react'
+import React, { Fragment, createElement, useEffect, useState } from 'react'
 import {
   ComboboxMode,
   ComboboxState,
@@ -575,6 +575,31 @@ describe('Rendering', () => {
         assertComboboxList({ state: ComboboxState.InvisibleUnmounted })
       })
     )
+
+    it(
+      'should not crash when the `Combobox` still contains a `nullable` prop',
+      suppressConsoleLogs(async () => {
+        let data = [
+          { id: 1, name: 'alice', label: 'Alice' },
+          { id: 2, name: 'bob', label: 'Bob' },
+          { id: 3, name: 'charlie', label: 'Charlie' },
+        ]
+
+        render(
+          <Combobox nullable as={Fragment}>
+            <Combobox.Input onChange={NOOP} />
+            <Combobox.Button />
+            <Combobox.Options>
+              {data.map((person) => (
+                <Combobox.Option key={person.id} value={person}>
+                  {person.label}
+                </Combobox.Option>
+              ))}
+            </Combobox.Options>
+          </Combobox>
+        )
+      })
+    )
   })
 
   describe('Combobox.Input', () => {