Add/improve slotProps handling for Label & Description
* Add slot props support to Label * Refactor slot props usage in Description * Add missing slot props to RadioGroupLabel and RadioGroupDescription
This commit is contained in:
@@ -12,7 +12,6 @@
|
|||||||
export let as: SupportedAs = "p";
|
export let as: SupportedAs = "p";
|
||||||
export let use: HTMLActionArray = [];
|
export let use: HTMLActionArray = [];
|
||||||
|
|
||||||
$: slotProps = $contextStore?.props?.slotProps ?? {};
|
|
||||||
const id = `headlessui-description-${useId()}`;
|
const id = `headlessui-description-${useId()}`;
|
||||||
let contextStore = useDescriptionContext();
|
let contextStore = useDescriptionContext();
|
||||||
if (!contextStore) {
|
if (!contextStore) {
|
||||||
@@ -22,12 +21,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => $contextStore?.register(id));
|
onMount(() => $contextStore?.register(id));
|
||||||
|
|
||||||
|
$: slotProps = $contextStore!.slotProps;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Render
|
<Render
|
||||||
name={"Description"}
|
name={"Description"}
|
||||||
{...$$restProps}
|
{...$$restProps}
|
||||||
{as}
|
{as}
|
||||||
|
{slotProps}
|
||||||
{...$contextStore?.props}
|
{...$contextStore?.props}
|
||||||
{id}
|
{id}
|
||||||
use={[...use, forwardEvents]}
|
use={[...use, forwardEvents]}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
export interface DescriptionContext {
|
export interface DescriptionContext {
|
||||||
name?: string;
|
name?: string;
|
||||||
props?: { slotProps?: object };
|
slotProps?: object;
|
||||||
|
props?: object;
|
||||||
register: (value: string) => void;
|
register: (value: string) => void;
|
||||||
descriptionIds?: string;
|
descriptionIds?: string;
|
||||||
}
|
}
|
||||||
@@ -19,16 +20,19 @@
|
|||||||
import type { Readable, Writable } from "svelte/store";
|
import type { Readable, Writable } from "svelte/store";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
export let name: string;
|
export let name: string;
|
||||||
|
export let slotProps = {};
|
||||||
let descriptionIds: string[] = [];
|
let descriptionIds: string[] = [];
|
||||||
let contextStore: Writable<DescriptionContext> = writable({
|
let contextStore: Writable<DescriptionContext> = writable({
|
||||||
name,
|
name,
|
||||||
register,
|
slotProps,
|
||||||
props: $$restProps,
|
props: $$restProps,
|
||||||
|
register,
|
||||||
});
|
});
|
||||||
setContext(DESCRIPTION_CONTEXT_NAME, contextStore);
|
setContext(DESCRIPTION_CONTEXT_NAME, contextStore);
|
||||||
|
|
||||||
$: contextStore.set({
|
$: contextStore.set({
|
||||||
name,
|
name,
|
||||||
|
slotProps,
|
||||||
props: $$restProps,
|
props: $$restProps,
|
||||||
register,
|
register,
|
||||||
descriptionIds:
|
descriptionIds:
|
||||||
|
|||||||
@@ -102,3 +102,33 @@ it("should be possible to use a DescriptionProvider and multiple Description com
|
|||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should be possible to use a DescriptionProvider with slot props", async () => {
|
||||||
|
let { container } = render(svelte`
|
||||||
|
<DescriptionProvider name={"test"} slotProps={{num: 12345}} let:describedby>
|
||||||
|
<div aria-describedby={describedby}>
|
||||||
|
<Description let:num>{num}</Description>
|
||||||
|
<span>Contents</span>
|
||||||
|
</div>
|
||||||
|
</DescriptionProvider>
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(container.firstChild?.firstChild).toMatchInlineSnapshot(`
|
||||||
|
<div
|
||||||
|
aria-describedby="headlessui-description-1"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
id="headlessui-description-1"
|
||||||
|
>
|
||||||
|
12345
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<span>
|
||||||
|
Contents
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,9 @@
|
|||||||
|
|
||||||
let allProps: any = {};
|
let allProps: any = {};
|
||||||
$: allProps = { ...$$restProps, ...$contextStore!.props, id };
|
$: allProps = { ...$$restProps, ...$contextStore!.props, id };
|
||||||
|
|
||||||
|
$: slotProps = $contextStore!.slotProps;
|
||||||
|
|
||||||
if (passive) delete allProps["onClick"];
|
if (passive) delete allProps["onClick"];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -32,10 +35,11 @@
|
|||||||
{...allProps}
|
{...allProps}
|
||||||
name={"Label"}
|
name={"Label"}
|
||||||
{as}
|
{as}
|
||||||
|
{slotProps}
|
||||||
use={[...use, forwardEvents]}
|
use={[...use, forwardEvents]}
|
||||||
on:click={(event) => {
|
on:click={(event) => {
|
||||||
if (!passive) allProps["onClick"]?.(event);
|
if (!passive) allProps["onClick"]?.(event);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot {...slotProps} />
|
||||||
</Render>
|
</Render>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
export interface LabelContext {
|
export interface LabelContext {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
slotProps?: object;
|
||||||
props?: object;
|
props?: object;
|
||||||
register: (value: string) => void;
|
register: (value: string) => void;
|
||||||
labelIds?: string;
|
labelIds?: string;
|
||||||
@@ -17,16 +18,19 @@
|
|||||||
import type { Writable } from "svelte/store";
|
import type { Writable } from "svelte/store";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
export let name: string;
|
export let name: string;
|
||||||
|
export let slotProps = {};
|
||||||
let labelIds: string[] = [];
|
let labelIds: string[] = [];
|
||||||
let contextStore: Writable<LabelContext> = writable({
|
let contextStore: Writable<LabelContext> = writable({
|
||||||
name,
|
name,
|
||||||
register,
|
slotProps,
|
||||||
props: $$restProps,
|
props: $$restProps,
|
||||||
|
register,
|
||||||
});
|
});
|
||||||
setContext(LABEL_CONTEXT_NAME, contextStore);
|
setContext(LABEL_CONTEXT_NAME, contextStore);
|
||||||
|
|
||||||
$: contextStore.set({
|
$: contextStore.set({
|
||||||
name,
|
name,
|
||||||
|
slotProps,
|
||||||
props: $$restProps,
|
props: $$restProps,
|
||||||
register,
|
register,
|
||||||
labelIds: labelIds.length > 0 ? labelIds.join(" ") : undefined,
|
labelIds: labelIds.length > 0 ? labelIds.join(" ") : undefined,
|
||||||
|
|||||||
38
src/lib/components/label/label.test.ts
vendored
38
src/lib/components/label/label.test.ts
vendored
@@ -31,7 +31,7 @@ it(
|
|||||||
|
|
||||||
it("should be possible to use a LabelProvider without using a Label", async () => {
|
it("should be possible to use a LabelProvider without using a Label", async () => {
|
||||||
let { container } = render(svelte`
|
let { container } = render(svelte`
|
||||||
<LabelProvider let:labelledby>
|
<LabelProvider name={"test"} let:labelledby>
|
||||||
<div aria-labelledby={labelledby}>
|
<div aria-labelledby={labelledby}>
|
||||||
No label
|
No label
|
||||||
</div>
|
</div>
|
||||||
@@ -46,7 +46,7 @@ it("should be possible to use a LabelProvider without using a Label", async () =
|
|||||||
|
|
||||||
it("should be possible to use a LabelProvider and a single Label, and have them linked", async () => {
|
it("should be possible to use a LabelProvider and a single Label, and have them linked", async () => {
|
||||||
let { container } = render(svelte`
|
let { container } = render(svelte`
|
||||||
<LabelProvider let:labelledby>
|
<LabelProvider name={"test"} let:labelledby>
|
||||||
<div aria-labelledby={labelledby}>
|
<div aria-labelledby={labelledby}>
|
||||||
<Label>I am a label</Label>
|
<Label>I am a label</Label>
|
||||||
<span>Contents</span>
|
<span>Contents</span>
|
||||||
@@ -74,7 +74,7 @@ it("should be possible to use a LabelProvider and a single Label, and have them
|
|||||||
|
|
||||||
it("should be possible to use a LabelProvider and multiple Label components, and have them linked", async () => {
|
it("should be possible to use a LabelProvider and multiple Label components, and have them linked", async () => {
|
||||||
let { container } = render(svelte`
|
let { container } = render(svelte`
|
||||||
<LabelProvider let:labelledby>
|
<LabelProvider name={"test"} let:labelledby>
|
||||||
<div aria-labelledby={labelledby}>
|
<div aria-labelledby={labelledby}>
|
||||||
<Label>I am a label</Label>
|
<Label>I am a label</Label>
|
||||||
<span>Contents</span>
|
<span>Contents</span>
|
||||||
@@ -111,7 +111,7 @@ it("should be possible to use a LabelProvider and multiple Label components, and
|
|||||||
|
|
||||||
it("should be possible to render a Label with an `as` prop", async () => {
|
it("should be possible to render a Label with an `as` prop", async () => {
|
||||||
let { container } = render(svelte`
|
let { container } = render(svelte`
|
||||||
<LabelProvider let:labelledby>
|
<LabelProvider name={"test"} let:labelledby>
|
||||||
<div aria-labelledby={labelledby}>
|
<div aria-labelledby={labelledby}>
|
||||||
<Label as="p">I am a label</Label>
|
<Label as="p">I am a label</Label>
|
||||||
<span>Contents</span>
|
<span>Contents</span>
|
||||||
@@ -140,7 +140,7 @@ it("should be possible to render a Label with an `as` prop", async () => {
|
|||||||
it("should be possible to change the props of a Label", async () => {
|
it("should be possible to change the props of a Label", async () => {
|
||||||
let classStore: Writable<string | null> = writable(null);
|
let classStore: Writable<string | null> = writable(null);
|
||||||
let { container } = render(svelte`
|
let { container } = render(svelte`
|
||||||
<LabelProvider let:labelledby>
|
<LabelProvider name={"test"} let:labelledby>
|
||||||
<div aria-labelledby={labelledby}>
|
<div aria-labelledby={labelledby}>
|
||||||
<Label class={$classStore}>I am a label</Label>
|
<Label class={$classStore}>I am a label</Label>
|
||||||
<span>Contents</span>
|
<span>Contents</span>
|
||||||
@@ -182,6 +182,34 @@ it("should be possible to change the props of a Label", async () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<span>
|
||||||
|
Contents
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be possible to use a LabelProvider with slot props", async () => {
|
||||||
|
let { container } = render(svelte`
|
||||||
|
<LabelProvider name={"test"} slotProps={{num: 12345}} let:labelledby>
|
||||||
|
<div aria-labelledby={labelledby}>
|
||||||
|
<Label let:num>{num}</Label>
|
||||||
|
<span>Contents</span>
|
||||||
|
</div>
|
||||||
|
</LabelProvider>
|
||||||
|
`);
|
||||||
|
expect(container.firstChild?.firstChild).toMatchInlineSnapshot(`
|
||||||
|
<div
|
||||||
|
aria-labelledby="headlessui-label-1"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
id="headlessui-label-1"
|
||||||
|
>
|
||||||
|
12345
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
Contents
|
Contents
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -74,8 +74,8 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<DescriptionProvider name="RadioGroupDescription" let:describedby>
|
<DescriptionProvider name="RadioGroupDescription" {slotProps} let:describedby>
|
||||||
<LabelProvider name="RadioGroupLabel" let:labelledby>
|
<LabelProvider name="RadioGroupLabel" {slotProps} let:labelledby>
|
||||||
<Render
|
<Render
|
||||||
{...{ ...$$restProps, ...propsWeControl }}
|
{...{ ...$$restProps, ...propsWeControl }}
|
||||||
{as}
|
{as}
|
||||||
|
|||||||
Reference in New Issue
Block a user