fix(CheckboxGroup): proxy slots & ui prop

This commit is contained in:
Benjamin Canac
2025-04-24 12:26:48 +02:00
parent 6e27304d8c
commit bc06185282
5 changed files with 38 additions and 30 deletions

View File

@@ -50,14 +50,14 @@ export interface CheckboxGroupProps<T extends CheckboxGroupItem = CheckboxGroupI
*/ */
orientation?: CheckboxGroupRootProps['orientation'] orientation?: CheckboxGroupRootProps['orientation']
class?: any class?: any
ui?: CheckboxGroup['slots'] ui?: CheckboxGroup['slots'] & CheckboxProps['ui']
} }
export type CheckboxGroupEmits = CheckboxGroupRootEmits & { export type CheckboxGroupEmits = CheckboxGroupRootEmits & {
change: [payload: Event] change: [payload: Event]
} }
type SlotProps<T extends CheckboxGroupItem> = (props: { item: T & { id: string }, modelValue?: CheckboxGroupValue }) => any type SlotProps<T extends CheckboxGroupItem> = (props: { item: T & { id: string } }) => any
export interface CheckboxGroupSlots<T extends CheckboxGroupItem = CheckboxGroupItem> { export interface CheckboxGroupSlots<T extends CheckboxGroupItem = CheckboxGroupItem> {
legend(props?: {}): any legend(props?: {}): any
@@ -72,7 +72,7 @@ import { CheckboxGroupRoot, useForwardProps, useForwardPropsEmits } from 'reka-u
import { reactivePick } from '@vueuse/core' import { reactivePick } from '@vueuse/core'
import { useAppConfig } from '#imports' import { useAppConfig } from '#imports'
import { useFormField } from '../composables/useFormField' import { useFormField } from '../composables/useFormField'
import { get } from '../utils' import { get, omit } from '../utils'
import { tv } from '../utils/tv' import { tv } from '../utils/tv'
const props = withDefaults(defineProps<CheckboxGroupProps<T>>(), { const props = withDefaults(defineProps<CheckboxGroupProps<T>>(), {
@@ -88,6 +88,7 @@ const appConfig = useAppConfig() as CheckboxGroup['AppConfig']
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'orientation', 'loop', 'required'), emits) const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'orientation', 'loop', 'required'), emits)
const checkboxProps = useForwardProps(reactivePick(props, 'variant', 'indicator', 'icon')) const checkboxProps = useForwardProps(reactivePick(props, 'variant', 'indicator', 'icon'))
const proxySlots = omit(slots, ['legend'])
const { emitFormChange, emitFormInput, color, name, size, id: _id, disabled, ariaAttrs } = useFormField<CheckboxGroupProps<T>>(props, { bind: false }) const { emitFormChange, emitFormInput, color, name, size, id: _id, disabled, ariaAttrs } = useFormField<CheckboxGroupProps<T>>(props, { bind: false })
const id = _id.value ?? useId() const id = _id.value ?? useId()
@@ -169,7 +170,13 @@ function onUpdate(value: any) {
:size="size" :size="size"
:name="name" :name="name"
:disabled="item.disabled || disabled" :disabled="item.disabled || disabled"
/> :ui="props.ui ? omit(props.ui, ['root']) : undefined"
:class="ui.item({ class: props.ui?.item })"
>
<template v-for="(_, name) in proxySlots" #[name]>
<slot :name="(name as keyof CheckboxGroupSlots<T>)" :item="item" />
</template>
</UCheckbox>
</fieldset> </fieldset>
</CheckboxGroupRoot> </CheckboxGroupRoot>
</template> </template>

View File

@@ -2,7 +2,8 @@ export default {
slots: { slots: {
root: 'relative', root: 'relative',
fieldset: 'flex gap-x-2', fieldset: 'flex gap-x-2',
legend: 'mb-1 block font-medium text-default' legend: 'mb-1 block font-medium text-default',
item: ''
}, },
variants: { variants: {
orientation: { orientation: {

View File

@@ -38,11 +38,11 @@ describe('CheckboxGroup', () => {
['with ariaLabel', { props, attrs: { 'aria-label': 'Aria label' } }], ['with ariaLabel', { props, attrs: { 'aria-label': 'Aria label' } }],
['with as', { props: { ...props, as: 'section' } }], ['with as', { props: { ...props, as: 'section' } }],
['with class', { props: { ...props, class: 'absolute' } }], ['with class', { props: { ...props, class: 'absolute' } }],
['with ui', { props: { ...props, ui: { wrapper: 'ms-4' } } }], ['with ui', { props: { ...props, ui: { fieldset: 'gap-x-4', label: 'text-red' } } }],
// Slots // Slots
['with legend slot', { props, slots: { label: () => 'Legend slot' } }], ['with legend slot', { props, slots: { legend: () => 'Legend slot' } }],
['with label slot', { props, slots: { label: () => 'Label slot' } }], ['with label slot', { props, slots: { label: () => 'Label slot' } }],
['with description slot', { props, slots: { label: () => 'Description slot' } }] ['with description slot', { props, slots: { description: () => 'Description slot' } }]
])('renders %s correctly', async (nameOrHtml: string, options: { props?: CheckboxGroupProps, slots?: Partial<CheckboxGroupSlots> }) => { ])('renders %s correctly', async (nameOrHtml: string, options: { props?: CheckboxGroupProps, slots?: Partial<CheckboxGroupSlots> }) => {
const html = await ComponentRender(nameOrHtml, options, CheckboxGroup) const html = await ComponentRender(nameOrHtml, options, CheckboxGroup)
expect(html).toMatchSnapshot() expect(html).toMatchSnapshot()

View File

@@ -189,7 +189,7 @@ exports[`CheckboxGroup > renders with description slot correctly 1`] = `
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0:1" class="block font-medium text-default">Option 1</p> <p for="v-0:1" class="block font-medium text-default">Option 1</p>
<!--v-if--> <p class="text-muted">Description slot</p>
</div> </div>
</label><label class="relative flex items-start flex-row"> </label><label class="relative flex items-start flex-row">
<div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0:2" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item=""> <div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0:2" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item="">
@@ -198,7 +198,7 @@ exports[`CheckboxGroup > renders with description slot correctly 1`] = `
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0:2" class="block font-medium text-default">Option 2</p> <p for="v-0:2" class="block font-medium text-default">Option 2</p>
<!--v-if--> <p class="text-muted">Description slot</p>
</div> </div>
</label><label class="relative flex items-start flex-row"> </label><label class="relative flex items-start flex-row">
<div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0:3" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item=""> <div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0:3" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item="">
@@ -207,7 +207,7 @@ exports[`CheckboxGroup > renders with description slot correctly 1`] = `
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0:3" class="block font-medium text-default">Option 3</p> <p for="v-0:3" class="block font-medium text-default">Option 3</p>
<!--v-if--> <p class="text-muted">Description slot</p>
</div> </div>
</label> </label>
</fieldset> </fieldset>
@@ -507,7 +507,7 @@ exports[`CheckboxGroup > renders with label slot correctly 1`] = `
<!----> <!---->
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0:1" class="block font-medium text-default">Option 1</p> <p for="v-0:1" class="block font-medium text-default">Label slot</p>
<!--v-if--> <!--v-if-->
</div> </div>
</label><label class="relative flex items-start flex-row"> </label><label class="relative flex items-start flex-row">
@@ -516,7 +516,7 @@ exports[`CheckboxGroup > renders with label slot correctly 1`] = `
<!----> <!---->
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0:2" class="block font-medium text-default">Option 2</p> <p for="v-0:2" class="block font-medium text-default">Label slot</p>
<!--v-if--> <!--v-if-->
</div> </div>
</label><label class="relative flex items-start flex-row"> </label><label class="relative flex items-start flex-row">
@@ -525,7 +525,7 @@ exports[`CheckboxGroup > renders with label slot correctly 1`] = `
<!----> <!---->
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0:3" class="block font-medium text-default">Option 3</p> <p for="v-0:3" class="block font-medium text-default">Label slot</p>
<!--v-if--> <!--v-if-->
</div> </div>
</label> </label>
@@ -573,7 +573,7 @@ exports[`CheckboxGroup > renders with labelKey correctly 1`] = `
exports[`CheckboxGroup > renders with legend slot correctly 1`] = ` exports[`CheckboxGroup > renders with legend slot correctly 1`] = `
"<div tabindex="0" data-orientation="vertical" dir="ltr" style="outline-color: none; outline-style: none; outline-width: initial;" id="v-0" class="relative"> "<div tabindex="0" data-orientation="vertical" dir="ltr" style="outline-color: none; outline-style: none; outline-width: initial;" id="v-0" class="relative">
<fieldset class="flex gap-x-2 flex-col gap-y-1"> <fieldset class="flex gap-x-2 flex-col gap-y-1">
<!--v-if--><label class="relative flex items-start flex-row"> <legend class="mb-1 block font-medium text-default text-sm">Legend slot</legend><label class="relative flex items-start flex-row">
<div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0:1" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item=""> <div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0:1" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item="">
<!----> <!---->
<!----> <!---->
@@ -994,14 +994,14 @@ exports[`CheckboxGroup > renders with size xs correctly 1`] = `
exports[`CheckboxGroup > renders with ui correctly 1`] = ` exports[`CheckboxGroup > renders with ui correctly 1`] = `
"<div tabindex="0" data-orientation="vertical" dir="ltr" style="outline-color: none; outline-style: none; outline-width: initial;" id="v-0" class="relative"> "<div tabindex="0" data-orientation="vertical" dir="ltr" style="outline-color: none; outline-style: none; outline-width: initial;" id="v-0" class="relative">
<fieldset class="flex gap-x-2 flex-col gap-y-1"> <fieldset class="flex flex-col gap-y-1 gap-x-4">
<!--v-if--><label class="relative flex items-start flex-row"> <!--v-if--><label class="relative flex items-start flex-row">
<div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0:1" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item=""> <div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0:1" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item="">
<!----> <!---->
<!----> <!---->
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0:1" class="block font-medium text-default">Option 1</p> <p for="v-0:1" class="block font-medium text-red">Option 1</p>
<!--v-if--> <!--v-if-->
</div> </div>
</label><label class="relative flex items-start flex-row"> </label><label class="relative flex items-start flex-row">
@@ -1010,7 +1010,7 @@ exports[`CheckboxGroup > renders with ui correctly 1`] = `
<!----> <!---->
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0:2" class="block font-medium text-default">Option 2</p> <p for="v-0:2" class="block font-medium text-red">Option 2</p>
<!--v-if--> <!--v-if-->
</div> </div>
</label><label class="relative flex items-start flex-row"> </label><label class="relative flex items-start flex-row">
@@ -1019,7 +1019,7 @@ exports[`CheckboxGroup > renders with ui correctly 1`] = `
<!----> <!---->
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0:3" class="block font-medium text-default">Option 3</p> <p for="v-0:3" class="block font-medium text-red">Option 3</p>
<!--v-if--> <!--v-if-->
</div> </div>
</label> </label>

View File

@@ -189,7 +189,7 @@ exports[`CheckboxGroup > renders with description slot correctly 1`] = `
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0-0:1" class="block font-medium text-default">Option 1</p> <p for="v-0-0:1" class="block font-medium text-default">Option 1</p>
<!--v-if--> <p class="text-muted">Description slot</p>
</div> </div>
</label><label class="relative flex items-start flex-row"> </label><label class="relative flex items-start flex-row">
<div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0-0:2" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item=""> <div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0-0:2" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item="">
@@ -198,7 +198,7 @@ exports[`CheckboxGroup > renders with description slot correctly 1`] = `
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0-0:2" class="block font-medium text-default">Option 2</p> <p for="v-0-0:2" class="block font-medium text-default">Option 2</p>
<!--v-if--> <p class="text-muted">Description slot</p>
</div> </div>
</label><label class="relative flex items-start flex-row"> </label><label class="relative flex items-start flex-row">
<div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0-0:3" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item=""> <div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0-0:3" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item="">
@@ -207,7 +207,7 @@ exports[`CheckboxGroup > renders with description slot correctly 1`] = `
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0-0:3" class="block font-medium text-default">Option 3</p> <p for="v-0-0:3" class="block font-medium text-default">Option 3</p>
<!--v-if--> <p class="text-muted">Description slot</p>
</div> </div>
</label> </label>
</fieldset> </fieldset>
@@ -507,7 +507,7 @@ exports[`CheckboxGroup > renders with label slot correctly 1`] = `
<!----> <!---->
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0-0:1" class="block font-medium text-default">Option 1</p> <p for="v-0-0:1" class="block font-medium text-default">Label slot</p>
<!--v-if--> <!--v-if-->
</div> </div>
</label><label class="relative flex items-start flex-row"> </label><label class="relative flex items-start flex-row">
@@ -516,7 +516,7 @@ exports[`CheckboxGroup > renders with label slot correctly 1`] = `
<!----> <!---->
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0-0:2" class="block font-medium text-default">Option 2</p> <p for="v-0-0:2" class="block font-medium text-default">Label slot</p>
<!--v-if--> <!--v-if-->
</div> </div>
</label><label class="relative flex items-start flex-row"> </label><label class="relative flex items-start flex-row">
@@ -525,7 +525,7 @@ exports[`CheckboxGroup > renders with label slot correctly 1`] = `
<!----> <!---->
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0-0:3" class="block font-medium text-default">Option 3</p> <p for="v-0-0:3" class="block font-medium text-default">Label slot</p>
<!--v-if--> <!--v-if-->
</div> </div>
</label> </label>
@@ -573,7 +573,7 @@ exports[`CheckboxGroup > renders with labelKey correctly 1`] = `
exports[`CheckboxGroup > renders with legend slot correctly 1`] = ` exports[`CheckboxGroup > renders with legend slot correctly 1`] = `
"<div tabindex="0" data-orientation="vertical" dir="ltr" style="outline-color: none; outline-style: none; outline-width: initial;" id="v-0-0" class="relative"> "<div tabindex="0" data-orientation="vertical" dir="ltr" style="outline-color: none; outline-style: none; outline-width: initial;" id="v-0-0" class="relative">
<fieldset class="flex gap-x-2 flex-col gap-y-1"> <fieldset class="flex gap-x-2 flex-col gap-y-1">
<!--v-if--><label class="relative flex items-start flex-row"> <legend class="mb-1 block font-medium text-default text-sm">Legend slot</legend><label class="relative flex items-start flex-row">
<div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0-0:1" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item=""> <div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0-0:1" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item="">
<!----> <!---->
<!----> <!---->
@@ -994,14 +994,14 @@ exports[`CheckboxGroup > renders with size xs correctly 1`] = `
exports[`CheckboxGroup > renders with ui correctly 1`] = ` exports[`CheckboxGroup > renders with ui correctly 1`] = `
"<div tabindex="0" data-orientation="vertical" dir="ltr" style="outline-color: none; outline-style: none; outline-width: initial;" id="v-0-0" class="relative"> "<div tabindex="0" data-orientation="vertical" dir="ltr" style="outline-color: none; outline-style: none; outline-width: initial;" id="v-0-0" class="relative">
<fieldset class="flex gap-x-2 flex-col gap-y-1"> <fieldset class="flex flex-col gap-y-1 gap-x-4">
<!--v-if--><label class="relative flex items-start flex-row"> <!--v-if--><label class="relative flex items-start flex-row">
<div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0-0:1" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item=""> <div class="flex items-center h-5"><button tabindex="-1" data-orientation="vertical" class="rounded-sm ring ring-inset ring-accented overflow-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary size-4" id="v-0-0:1" role="checkbox" type="button" aria-checked="false" aria-required="false" data-state="unchecked" data-reka-collection-item="">
<!----> <!---->
<!----> <!---->
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0-0:1" class="block font-medium text-default">Option 1</p> <p for="v-0-0:1" class="block font-medium text-red">Option 1</p>
<!--v-if--> <!--v-if-->
</div> </div>
</label><label class="relative flex items-start flex-row"> </label><label class="relative flex items-start flex-row">
@@ -1010,7 +1010,7 @@ exports[`CheckboxGroup > renders with ui correctly 1`] = `
<!----> <!---->
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0-0:2" class="block font-medium text-default">Option 2</p> <p for="v-0-0:2" class="block font-medium text-red">Option 2</p>
<!--v-if--> <!--v-if-->
</div> </div>
</label><label class="relative flex items-start flex-row"> </label><label class="relative flex items-start flex-row">
@@ -1019,7 +1019,7 @@ exports[`CheckboxGroup > renders with ui correctly 1`] = `
<!----> <!---->
</button></div> </button></div>
<div class="w-full ms-2 text-sm"> <div class="w-full ms-2 text-sm">
<p for="v-0-0:3" class="block font-medium text-default">Option 3</p> <p for="v-0-0:3" class="block font-medium text-red">Option 3</p>
<!--v-if--> <!--v-if-->
</div> </div>
</label> </label>