From f6f9823b15d84362d093703cb15ecba64c73c2c2 Mon Sep 17 00:00:00 2001 From: Benjamin Canac Date: Fri, 11 Oct 2024 14:21:03 +0200 Subject: [PATCH] feat(InputMenu/RadioGroup/Select/SelectMenu): handle `labelKey` and use `get` to support dot notation --- src/runtime/components/InputMenu.vue | 24 ++-- src/runtime/components/RadioGroup.vue | 19 ++- src/runtime/components/Select.vue | 15 ++- src/runtime/components/SelectMenu.vue | 22 ++-- test/components/InputMenu.spec.ts | 7 ++ test/components/RadioGroup.spec.ts | 3 + test/components/Select.spec.ts | 2 + test/components/SelectMenu.spec.ts | 2 + .../__snapshots__/InputMenu.spec.ts.snap | 70 +++++++++++ .../__snapshots__/RadioGroup.spec.ts.snap | 105 +++++++++++++++++ .../__snapshots__/Select.spec.ts.snap | 110 ++++++++++++++++++ .../__snapshots__/SelectMenu.spec.ts.snap | 78 +++++++++++++ 12 files changed, 436 insertions(+), 21 deletions(-) diff --git a/src/runtime/components/InputMenu.vue b/src/runtime/components/InputMenu.vue index 3633bc2f..17289458 100644 --- a/src/runtime/components/InputMenu.vue +++ b/src/runtime/components/InputMenu.vue @@ -86,6 +86,11 @@ export interface InputMenuProps extends Pick, 'modelValu * @defaultValue undefined */ valueKey?: keyof T + /** + * When `items` is an array of objects, select the field to use as the label. + * @defaultValue 'label' + */ + labelKey?: keyof T items?: T[] | T[][] /** Highlight the ring color like a focus state. */ highlight?: boolean @@ -124,10 +129,10 @@ import { useAppConfig } from '#imports' import { useButtonGroup } from '../composables/useButtonGroup' import { useComponentIcons } from '../composables/useComponentIcons' import { useFormField } from '../composables/useFormField' +import { get, escapeRegExp } from '../utils' import UIcon from './Icon.vue' import UAvatar from './Avatar.vue' import UChip from './Chip.vue' -import { get, escapeRegExp } from '../utils' defineOptions({ inheritAttrs: false }) @@ -135,7 +140,8 @@ const props = withDefaults(defineProps>(), { type: 'text', autofocusDelay: 0, portal: true, - filter: () => ['label'] + filter: () => ['label'], + labelKey: 'label' as keyof T }) const emits = defineEmits>() const slots = defineSlots>() @@ -164,9 +170,9 @@ const ui = computed(() => inputMenu({ })) function displayValue(value: AcceptableValue): string { - const item = items.value.find(item => props.valueKey ? isEqual(item[props.valueKey], value) : isEqual(item, value)) + const item = items.value.find(item => props.valueKey ? isEqual(get(item as Record, props.valueKey as string), value) : isEqual(item, value)) - return item && (typeof item === 'object' ? item.label : item) + return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item) } function filterFunction(items: ArrayOrWrapped, searchTerm: string): ArrayOrWrapped { @@ -174,7 +180,7 @@ function filterFunction(items: ArrayOrWrapped, searchTerm: stri return items } - const fields = Array.isArray(props.filter) ? props.filter : ['label'] + const fields = Array.isArray(props.filter) ? props.filter : [props.labelKey] const escapedSearchTerm = escapeRegExp(searchTerm) return items.filter((item) => { @@ -183,7 +189,7 @@ function filterFunction(items: ArrayOrWrapped, searchTerm: stri } return fields.some((field) => { - const child = get(item, field) + const child = get(item, field as string) return child !== null && child !== undefined && String(child).search(new RegExp(escapedSearchTerm, 'i')) !== -1 }) @@ -325,7 +331,7 @@ defineExpose({