From 495fa64bf905d40f78b2eec3292eca4defec1cf3 Mon Sep 17 00:00:00 2001 From: Ryan Gossiaux Date: Tue, 1 Feb 2022 12:38:35 -0800 Subject: [PATCH] Support `style` as a function Seems useful to add. Also improve the prop typing of , marginally, though as these types do not carry over to end users yet, it's just cosmetic for now. --- .../components/description/Description.svelte | 2 +- .../components/popover/PopoverGroup.svelte | 1 + .../components/radio-group/RadioGroup.svelte | 1 + src/lib/components/switch/SwitchGroup.svelte | 1 + .../transitions/TransitionChild.svelte | 1 + src/lib/utils/Render.svelte | 24 ++++++--- src/lib/utils/render.test.ts | 52 +++++++++++++++++++ 7 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 src/lib/utils/render.test.ts diff --git a/src/lib/components/description/Description.svelte b/src/lib/components/description/Description.svelte index 574c5ac..0929da2 100644 --- a/src/lib/components/description/Description.svelte +++ b/src/lib/components/description/Description.svelte @@ -22,7 +22,7 @@ onMount(() => $contextStore?.register(id)); - $: slotProps = $contextStore!.slotProps; + $: slotProps = $contextStore!.slotProps || {}; diff --git a/src/lib/components/radio-group/RadioGroup.svelte b/src/lib/components/radio-group/RadioGroup.svelte index 4a0d9c8..1b4ac05 100644 --- a/src/lib/components/radio-group/RadioGroup.svelte +++ b/src/lib/components/radio-group/RadioGroup.svelte @@ -208,6 +208,7 @@ {...{ ...$$restProps, ...propsWeControl }} {as} use={[...use, forwardEvents]} + slotProps={{}} name={"RadioGroup"} bind:el={radioGroupRef} aria-labelledby={labelledby} diff --git a/src/lib/components/switch/SwitchGroup.svelte b/src/lib/components/switch/SwitchGroup.svelte index a0b07f0..b9f0d68 100644 --- a/src/lib/components/switch/SwitchGroup.svelte +++ b/src/lib/components/switch/SwitchGroup.svelte @@ -42,6 +42,7 @@ {...$$restProps} {as} use={[...use, forwardEvents]} + slotProps={{}} name={"SwitchGroup"} > diff --git a/src/lib/components/transitions/TransitionChild.svelte b/src/lib/components/transitions/TransitionChild.svelte index e10477b..b323b02 100644 --- a/src/lib/components/transitions/TransitionChild.svelte +++ b/src/lib/components/transitions/TransitionChild.svelte @@ -195,6 +195,7 @@ {...$$restProps} {as} use={[...use, forwardEvents]} + slotProps={{}} name={"TransitionChild"} bind:el={container} class={classes} diff --git a/src/lib/utils/Render.svelte b/src/lib/utils/Render.svelte index e4bf0ba..266d2cd 100644 --- a/src/lib/utils/Render.svelte +++ b/src/lib/utils/Render.svelte @@ -34,16 +34,27 @@ import { forwardEventsBuilder } from "$lib/internal/forwardEventsBuilder"; const forwardEvents = forwardEventsBuilder(get_current_component()); + type TSlotProps = $$Generic<{}>; + export let name: string; export let as: SvelteComponent | SupportedElement; + export let slotProps: TSlotProps; + export let el: HTMLElement | null = null; export let use: ActionArray = []; - export let slotProps: unknown = {}; export let visible = true; export let features: Features = Features.None; // The static and unmount props are only used in conjunction with the render strategies export let unmount = true; + let classProp: ((props: TSlotProps) => string) | string | undefined = + undefined; + export { classProp as class }; + + // This is not in upstream Headless UI, but we might as well add it here + export let style: ((props: TSlotProps) => string) | string | undefined = + undefined; + if (!as) { throw new Error(`<${name}> did not provide an \`as\` value to `); } @@ -55,7 +66,9 @@ ); } - $: classStyle = $$props.class; + $: computedClass = + typeof classProp === "function" ? classProp(slotProps) : classProp; + $: computedStyle = typeof style === "function" ? style(slotProps) : style; $: show = visible || @@ -74,10 +87,9 @@ bind:el use={[...use, forwardEvents]} {...$$restProps} - class={typeof classStyle === "function" - ? classStyle(slotProps) - : classStyle} - style={hidden ? "display: none" : $$props.style} + class={computedClass} + style={`${computedStyle ?? ""}${hidden ? " display: none" : ""}` || + undefined} hidden={hidden || undefined} > diff --git a/src/lib/utils/render.test.ts b/src/lib/utils/render.test.ts new file mode 100644 index 0000000..4584017 --- /dev/null +++ b/src/lib/utils/render.test.ts @@ -0,0 +1,52 @@ +import Render from "$lib/utils/Render.svelte"; +// import svelte from "svelte-inline-compile"; +import { getByTestId, render } from "@testing-library/svelte"; + +it("should be possible to use class as a string", () => { + let { container } = render(Render, { + as: "div", + name: "test", + slotProps: {}, + class: "test-class", + "data-testid": "test-id", + }); + let element = getByTestId(container, "test-id"); + expect(element).toHaveClass("test-class"); +}); + +it("should be possible to use class as a function", () => { + render(Render, { + as: "div", + name: "test", + slotProps: { foo: "bar" }, + class: JSON.stringify, + id: "test-id", + }); + expect("" + document.querySelector("#test-id")?.classList).toEqual( + JSON.stringify({ foo: "bar" }) + ); +}); + +it("should be possible to use style as a string", () => { + let { container } = render(Render, { + as: "div", + name: "test", + slotProps: {}, + style: "background-color: green", + "data-testid": "test-id", + }); + let element = getByTestId(container, "test-id"); + expect(element).toHaveStyle("background-color: green"); +}); + +it("should be possible to use style as a function", () => { + let { container } = render(Render, { + as: "div", + name: "test", + slotProps: { enabled: true }, + style: ({ enabled }: { enabled: boolean }) => enabled ? "background-color: red" : "background-color: green", + "data-testid": "test-id", + }); + let element = getByTestId(container, "test-id"); + expect(element).toHaveStyle("background-color: red"); +});