Support style as a function

Seems useful to add.

Also improve the prop typing of <Render>, marginally, though as these types do not carry over to end users yet, it's just cosmetic for now.
This commit is contained in:
Ryan Gossiaux
2022-02-01 12:38:35 -08:00
parent bfc68577cc
commit 495fa64bf9
7 changed files with 75 additions and 7 deletions

View File

@@ -22,7 +22,7 @@
onMount(() => $contextStore?.register(id)); onMount(() => $contextStore?.register(id));
$: slotProps = $contextStore!.slotProps; $: slotProps = $contextStore!.slotProps || {};
</script> </script>
<Render <Render

View File

@@ -71,6 +71,7 @@
{...$$restProps} {...$$restProps}
{as} {as}
use={[...use, forwardEvents]} use={[...use, forwardEvents]}
slotProps={{}}
name={"PopoverGroup"} name={"PopoverGroup"}
bind:el={groupRef} bind:el={groupRef}
> >

View File

@@ -208,6 +208,7 @@
{...{ ...$$restProps, ...propsWeControl }} {...{ ...$$restProps, ...propsWeControl }}
{as} {as}
use={[...use, forwardEvents]} use={[...use, forwardEvents]}
slotProps={{}}
name={"RadioGroup"} name={"RadioGroup"}
bind:el={radioGroupRef} bind:el={radioGroupRef}
aria-labelledby={labelledby} aria-labelledby={labelledby}

View File

@@ -42,6 +42,7 @@
{...$$restProps} {...$$restProps}
{as} {as}
use={[...use, forwardEvents]} use={[...use, forwardEvents]}
slotProps={{}}
name={"SwitchGroup"} name={"SwitchGroup"}
> >
<DescriptionProvider name="SwitchDescription"> <DescriptionProvider name="SwitchDescription">

View File

@@ -195,6 +195,7 @@
{...$$restProps} {...$$restProps}
{as} {as}
use={[...use, forwardEvents]} use={[...use, forwardEvents]}
slotProps={{}}
name={"TransitionChild"} name={"TransitionChild"}
bind:el={container} bind:el={container}
class={classes} class={classes}

View File

@@ -34,16 +34,27 @@
import { forwardEventsBuilder } from "$lib/internal/forwardEventsBuilder"; import { forwardEventsBuilder } from "$lib/internal/forwardEventsBuilder";
const forwardEvents = forwardEventsBuilder(get_current_component()); const forwardEvents = forwardEventsBuilder(get_current_component());
type TSlotProps = $$Generic<{}>;
export let name: string; export let name: string;
export let as: SvelteComponent | SupportedElement; export let as: SvelteComponent | SupportedElement;
export let slotProps: TSlotProps;
export let el: HTMLElement | null = null; export let el: HTMLElement | null = null;
export let use: ActionArray = []; export let use: ActionArray = [];
export let slotProps: unknown = {};
export let visible = true; export let visible = true;
export let features: Features = Features.None; export let features: Features = Features.None;
// The static and unmount props are only used in conjunction with the render strategies // The static and unmount props are only used in conjunction with the render strategies
export let unmount = true; 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) { if (!as) {
throw new Error(`<${name}> did not provide an \`as\` value to <Render>`); throw new Error(`<${name}> did not provide an \`as\` value to <Render>`);
} }
@@ -55,7 +66,9 @@
); );
} }
$: classStyle = $$props.class; $: computedClass =
typeof classProp === "function" ? classProp(slotProps) : classProp;
$: computedStyle = typeof style === "function" ? style(slotProps) : style;
$: show = $: show =
visible || visible ||
@@ -74,10 +87,9 @@
bind:el bind:el
use={[...use, forwardEvents]} use={[...use, forwardEvents]}
{...$$restProps} {...$$restProps}
class={typeof classStyle === "function" class={computedClass}
? classStyle(slotProps) style={`${computedStyle ?? ""}${hidden ? " display: none" : ""}` ||
: classStyle} undefined}
style={hidden ? "display: none" : $$props.style}
hidden={hidden || undefined} hidden={hidden || undefined}
> >
<slot /> <slot />

52
src/lib/utils/render.test.ts vendored Normal file
View File

@@ -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");
});