From 42ea3862ddf9be8dc5737b291829329fd81b9abf Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Mon, 12 Dec 2022 16:37:43 +0100 Subject: [PATCH 1/2] fix regression where `displayValue` crashes It regressed in the sense that it now uses `displayValue` for the `defaultValue` as well, but if nothing is passed it would crash. Right now, it makes sure to only run the displayValue value on the actual value and the actual default value if they are not undefined. Note: if your displayValue is implemented like `(value) => value.name`, and your `value` is passed as `null`, it will still crash (as expected) because then you are in charge of rendering something else than null. If we would "fix" this, then no value can be rendered instead of `null`. Fixes: #2084 --- .../src/components/combobox/combobox.test.tsx | 24 ++++++++++++++++++ .../src/components/combobox/combobox.tsx | 6 +++-- .../src/components/combobox/combobox.test.ts | 25 +++++++++++++++++++ .../src/components/combobox/combobox.ts | 6 +++-- 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx index e3be24ce25..9c5561d896 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx @@ -412,6 +412,30 @@ describe('Rendering', () => { }) ) }) + + it( + 'should not crash when a defaultValue is not given', + suppressConsoleLogs(async () => { + let data = [ + { id: 1, name: 'alice', label: 'Alice' }, + { id: 2, name: 'bob', label: 'Bob' }, + { id: 3, name: 'charlie', label: 'Charlie' }, + ] + + render( + + value.name} /> + + {data.map((person) => ( + + {person.label} + + ))} + + + ) + }) + ) }) describe('Combobox.Input', () => { diff --git a/packages/@headlessui-react/src/components/combobox/combobox.tsx b/packages/@headlessui-react/src/components/combobox/combobox.tsx index 594b95604a..ba84793209 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.tsx @@ -705,7 +705,7 @@ let Input = forwardRefWithAs(function Input< // you don't have to use this at all, a more common UI is a "tag" based UI, which you can render // yourself using the selected option(s). let currentValue = useMemo(() => { - if (typeof displayValue === 'function') { + if (typeof displayValue === 'function' && data.value !== undefined) { return displayValue(data.value as unknown as TType) ?? '' } else if (typeof data.value === 'string') { return data.value @@ -909,7 +909,9 @@ let Input = forwardRefWithAs(function Input< 'aria-labelledby': labelledby, defaultValue: props.defaultValue ?? - displayValue?.(data.defaultValue as unknown as TType) ?? + (data.defaultValue !== undefined + ? displayValue?.(data.defaultValue as unknown as TType) + : null) ?? data.defaultValue, disabled: data.disabled, onCompositionStart: handleCompositionStart, diff --git a/packages/@headlessui-vue/src/components/combobox/combobox.test.ts b/packages/@headlessui-vue/src/components/combobox/combobox.test.ts index 4c608887df..1af8cdc24f 100644 --- a/packages/@headlessui-vue/src/components/combobox/combobox.test.ts +++ b/packages/@headlessui-vue/src/components/combobox/combobox.test.ts @@ -465,6 +465,31 @@ describe('Rendering', () => { }) ) }) + + it( + 'should not crash when a defaultValue is not given', + suppressConsoleLogs(async () => { + let data = [ + { id: 1, name: 'alice', label: 'Alice' }, + { id: 2, name: 'bob', label: 'Bob' }, + { id: 3, name: 'charlie', label: 'Charlie' }, + ] + + renderTemplate({ + template: html` + + + + + {{ person.label }} + + + + `, + setup: () => ({ data }), + }) + }) + ) }) describe('ComboboxInput', () => { diff --git a/packages/@headlessui-vue/src/components/combobox/combobox.ts b/packages/@headlessui-vue/src/components/combobox/combobox.ts index b7c8d17d92..ac9cc2f629 100644 --- a/packages/@headlessui-vue/src/components/combobox/combobox.ts +++ b/packages/@headlessui-vue/src/components/combobox/combobox.ts @@ -676,7 +676,7 @@ export let ComboboxInput = defineComponent({ let value = api.value.value if (!dom(api.inputRef)) return '' - if (typeof props.displayValue !== 'undefined') { + if (typeof props.displayValue !== 'undefined' && value !== undefined) { return props.displayValue(value as unknown) ?? '' } else if (typeof value === 'string') { return value @@ -874,7 +874,9 @@ export let ComboboxInput = defineComponent({ let defaultValue = computed(() => { return ( props.defaultValue ?? - props.displayValue?.(api.defaultValue.value) ?? + (api.defaultValue.value !== undefined + ? props.displayValue?.(api.defaultValue.value) + : null) ?? api.defaultValue.value ?? '' ) From 0e5bab745012bf38dfb8423349fe211720a3e795 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Mon, 12 Dec 2022 16:40:26 +0100 Subject: [PATCH 2/2] update changelog --- packages/@headlessui-react/CHANGELOG.md | 4 +++- packages/@headlessui-vue/CHANGELOG.md | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index d103ab91ff..456689fd55 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Fixed + +- Fix regression where `displayValue` crashes ([#2087](https://github.com/tailwindlabs/headlessui/pull/2087)) ## [1.7.5] - 2022-12-08 diff --git a/packages/@headlessui-vue/CHANGELOG.md b/packages/@headlessui-vue/CHANGELOG.md index 405297c0d6..ecd90681cb 100644 --- a/packages/@headlessui-vue/CHANGELOG.md +++ b/packages/@headlessui-vue/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Fixed + +- Fix regression where `displayValue` crashes ([#2087](https://github.com/tailwindlabs/headlessui/pull/2087)) ## [1.7.5] - 2022-12-08