From db8111d7835d030ced79899e826ff1eb74cf1cf4 Mon Sep 17 00:00:00 2001 From: Yasser Lahbibi Date: Mon, 28 Oct 2024 18:08:24 +0100 Subject: [PATCH] fix(InputMenu/Select/SelectMenu): improve types (#2471) --- .../app/pages/components/input-menu.vue | 4 +- .../app/pages/components/select-menu.vue | 2 +- src/runtime/components/InputMenu.vue | 22 ++++--- src/runtime/components/Select.vue | 29 +++++---- src/runtime/components/SelectMenu.vue | 33 +++++----- src/runtime/types/utils.ts | 8 +++ test/components/InputMenu.spec.ts | 62 +++++++++++++++++++ test/components/Select.spec.ts | 29 +++++++++ test/components/SelectMenu.spec.ts | 62 +++++++++++++++++++ test/utils/types.ts | 13 ++++ 10 files changed, 226 insertions(+), 38 deletions(-) create mode 100644 test/utils/types.ts diff --git a/playground/app/pages/components/input-menu.vue b/playground/app/pages/components/input-menu.vue index 9a7476b4d4..64e9f05887 100644 --- a/playground/app/pages/components/input-menu.vue +++ b/playground/app/pages/components/input-menu.vue @@ -11,7 +11,7 @@ const fruits = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple'] const vegetables = ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek'] const items = [[{ label: 'Fruits', type: 'label' }, ...fruits], [{ label: 'Vegetables', type: 'label' }, ...vegetables]] -const selectedItems = ref([fruits[0], vegetables[0]]) +const selectedItems = ref([fruits[0]!, vegetables[0]!]) const statuses = [{ label: 'Backlog', @@ -135,7 +135,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode v-for="size in sizes" :key="size" :items="items" - :model-value="[fruits[0]]" + :model-value="[fruits[0]!]" multiple icon="i-heroicons-magnifying-glass" placeholder="Search..." diff --git a/playground/app/pages/components/select-menu.vue b/playground/app/pages/components/select-menu.vue index d8242cdaa2..7a9df69cd8 100644 --- a/playground/app/pages/components/select-menu.vue +++ b/playground/app/pages/components/select-menu.vue @@ -11,7 +11,7 @@ const fruits = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple'] const vegetables = ['Aubergine', 'Broccoli', 'Carrot', 'Courgette', 'Leek'] const items = [[{ label: 'Fruits', type: 'label' }, ...fruits], [{ label: 'Vegetables', type: 'label' }, ...vegetables]] -const selectedItems = ref([fruits[0], vegetables[0]]) +const selectedItems = ref([fruits[0]!, vegetables[0]!]) const statuses = [{ label: 'Backlog', diff --git a/src/runtime/components/InputMenu.vue b/src/runtime/components/InputMenu.vue index b67495fc66..a14407707a 100644 --- a/src/runtime/components/InputMenu.vue +++ b/src/runtime/components/InputMenu.vue @@ -7,7 +7,7 @@ import _appConfig from '#build/app.config' import theme from '#build/ui/input-menu' import type { UseComponentIconsProps } from '../composables/useComponentIcons' import type { AvatarProps, ChipProps, InputProps } from '../types' -import type { AcceptableValue, ArrayOrWrapped, PartialString } from '../types/utils' +import type { AcceptableValue, ArrayOrWrapped, PartialString, SelectItems, SelectItemType, SelectModelValue, SelectModelValueEmits, SelectItemKey } from '../types/utils' const appConfig = _appConfig as AppConfig & { ui: { inputMenu: Partial } } @@ -29,7 +29,7 @@ export interface InputMenuItem { type InputMenuVariants = VariantProps -export interface InputMenuProps extends Pick, 'modelValue' | 'defaultValue' | 'selectedValue' | 'open' | 'defaultOpen' | 'searchTerm' | 'multiple' | 'disabled' | 'name' | 'resetSearchTermOnBlur'>, UseComponentIconsProps { +export interface InputMenuProps, I extends SelectItems = SelectItems, V extends SelectItemKey | undefined = undefined, M extends boolean = false> extends Pick, 'defaultValue' | 'selectedValue' | 'open' | 'defaultOpen' | 'searchTerm' | 'disabled' | 'name' | 'resetSearchTermOnBlur'>, UseComponentIconsProps { /** * The element or component this component should render as. * @defaultValue 'div' @@ -86,24 +86,28 @@ export interface InputMenuProps extends Pick, 'modelValu * When `items` is an array of objects, select the field to use as the value instead of the object itself. * @defaultValue undefined */ - valueKey?: keyof T + valueKey?: V /** * When `items` is an array of objects, select the field to use as the label. * @defaultValue 'label' */ labelKey?: keyof T - items?: T[] | T[][] + items?: I /** Highlight the ring color like a focus state. */ highlight?: boolean class?: any ui?: PartialString + /** The controlled value of the Combobox. Can be binded-with with `v-model`. */ + modelValue?: SelectModelValue + /** Whether multiple options can be selected or not. */ + multiple?: M } -export type InputMenuEmits = ComboboxRootEmits & { +export type InputMenuEmits = Omit, 'update:modelValue'> & { change: [payload: Event] blur: [payload: FocusEvent] focus: [payload: FocusEvent] -} +} & SelectModelValueEmits type SlotProps = (props: { item: T, index: number }) => any @@ -120,7 +124,7 @@ export interface InputMenuSlots { } - - -