diff --git a/.changeset/hungry-things-retire.md b/.changeset/hungry-things-retire.md
new file mode 100644
index 0000000000..3b9ee38d99
--- /dev/null
+++ b/.changeset/hungry-things-retire.md
@@ -0,0 +1,55 @@
+---
+"@stackoverflow/stacks-svelte": minor
+---
+
+Migrate `Popover`, `PopoverReference`, `PopoverContent`, and `PopoverCloseButton` components to use Svelte 5 runes API
+
+BREAKING CHANGES:
+
+**Popover component:**
+- Slot props (`let:visible`, `let:open`, `let:close`) are not available anymore. Snippet parameters should be used instead: `{#snippet children({ visible, open, close })}...{/snippet}`
+- `on:open` and `on:close` events are not available anymore. The new callback props should be used instead: `onopen`, `onclose`.
+
+**PopoverCloseButton component:**
+- `on:click` event forwarding is not available anymore. The new callback prop should be used instead: `onclick`.
+
+**Migration examples:**
+
+```svelte
+
+ console.log('opened')}
+ on:close={() => console.log('closed')}
+ let:visible
+ let:close
+>
+
+ Trigger
+
+
+ Content here
+
+
+ Visible: {visible}
+
+
+
+ console.log('opened')}
+ onclose={() => console.log('closed')}
+>
+ {#snippet children({ visible, close })}
+
+ Trigger
+
+
+ Content here
+
+
+ Visible: {visible}
+ {/snippet}
+
+```
+
diff --git a/packages/stacks-svelte/src/components/Popover/Popover.stories.svelte b/packages/stacks-svelte/src/components/Popover/Popover.stories.svelte
index 6a54ae26c7..21f3796304 100644
--- a/packages/stacks-svelte/src/components/Popover/Popover.stories.svelte
+++ b/packages/stacks-svelte/src/components/Popover/Popover.stories.svelte
@@ -46,8 +46,8 @@
-
+
-
+{@render children?.({ visible: pstate.visible, open, close })}
diff --git a/packages/stacks-svelte/src/components/Popover/Popover.test.ts b/packages/stacks-svelte/src/components/Popover/Popover.test.ts
index 0a3388306b..8d81b8934b 100644
--- a/packages/stacks-svelte/src/components/Popover/Popover.test.ts
+++ b/packages/stacks-svelte/src/components/Popover/Popover.test.ts
@@ -22,21 +22,17 @@ const defaultChildren = {
reference: {
component: PopoverReference,
props: {
- $$slots: {
- default: createRawSnippet(() => ({
- render: () => "Trigger ",
- })),
- },
+ children: createRawSnippet(() => ({
+ render: () => "Trigger ",
+ })),
},
},
content: {
component: PopoverContent,
props: {
- $$slots: {
- default: createRawSnippet(() => ({
- render: () => "Popover Content ",
- })),
- },
+ children: createRawSnippet(() => ({
+ render: () => "Popover Content ",
+ })),
},
},
closeButton: {
@@ -52,12 +48,10 @@ describe("Popover", () => {
render(Popover, {
props: {
...defaultProps,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -80,17 +74,15 @@ describe("Popover", () => {
target,
props: {
...defaultProps,
- $$slots: {
- default: createSvelteComponentsSnippet([
- {
- component: PopoverReference,
- props: {
- elementId: "external-reference",
- },
+ children: createSvelteComponentsSnippet([
+ {
+ component: PopoverReference,
+ props: {
+ elementId: "external-reference",
},
- defaultChildren.content,
- ]),
- },
+ },
+ defaultChildren.content,
+ ]),
},
});
return () => {
@@ -117,12 +109,10 @@ describe("Popover", () => {
props: {
...defaultProps,
autoshow: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -134,23 +124,18 @@ describe("Popover", () => {
props: {
...defaultProps,
autoshow: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- {
- component: PopoverContent,
- props: {
- role: "menu",
- $$slots: {
- default: createRawSnippet(() => ({
- render: () =>
- "Popover Content ",
- })),
- },
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ {
+ component: PopoverContent,
+ props: {
+ role: "menu",
+ children: createRawSnippet(() => ({
+ render: () => "Popover Content ",
+ })),
},
- ]),
- },
+ },
+ ]),
},
});
@@ -162,23 +147,18 @@ describe("Popover", () => {
props: {
...defaultProps,
autoshow: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- {
- component: PopoverContent,
- props: {
- class: "custom-class",
- $$slots: {
- default: createRawSnippet(() => ({
- render: () =>
- "Popover Content ",
- })),
- },
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ {
+ component: PopoverContent,
+ props: {
+ class: "custom-class",
+ children: createRawSnippet(() => ({
+ render: () => "Popover Content ",
+ })),
},
- ]),
- },
+ },
+ ]),
},
});
@@ -190,27 +170,23 @@ describe("Popover", () => {
props: {
...defaultProps,
autoshow: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- {
- component: PopoverContent,
- props: {
- $$slots: {
- default: createSvelteComponentsSnippet([
- {
- component: PopoverCloseButton,
- props: {
- label: "Close",
- class: "custom-class",
- },
- },
- ]),
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ {
+ component: PopoverContent,
+ props: {
+ children: createSvelteComponentsSnippet([
+ {
+ component: PopoverCloseButton,
+ props: {
+ label: "Close",
+ class: "custom-class",
+ },
},
- },
+ ]),
},
- ]),
- },
+ },
+ ]),
},
});
@@ -224,12 +200,10 @@ describe("Popover", () => {
props: {
...defaultProps,
autoshow: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -243,21 +217,17 @@ describe("Popover", () => {
render(Popover, {
props: {
...defaultProps,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- {
- component: PopoverContent,
- props: {
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.closeButton,
- ]),
- },
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ {
+ component: PopoverContent,
+ props: {
+ children: createSvelteComponentsSnippet([
+ defaultChildren.closeButton,
+ ]),
},
- ]),
- },
+ },
+ ]),
},
});
@@ -274,12 +244,10 @@ describe("Popover", () => {
props: {
...defaultProps,
autoshow: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -294,12 +262,10 @@ describe("Popover", () => {
props: {
...defaultProps,
visible: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -316,29 +282,23 @@ describe("Popover", () => {
props: {
...defaultProps,
visible: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- {
- component: PopoverContent,
- props: {
- $$slots: {
- default: createSvelteComponentsSnippet([
- {
- component: PopoverCloseButton,
- props: {
- label: "Close",
- $$events: {
- click: onCloseButtonClick,
- },
- },
- },
- ]),
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ {
+ component: PopoverContent,
+ props: {
+ children: createSvelteComponentsSnippet([
+ {
+ component: PopoverCloseButton,
+ props: {
+ label: "Close",
+ onclick: onCloseButtonClick,
+ },
},
- },
+ ]),
},
- ]),
- },
+ },
+ ]),
},
});
@@ -367,16 +327,12 @@ describe("Popover", () => {
render(Popover, {
props: {
...defaultProps,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
- $$events: {
- open: onOpenSpy,
- close: onCloseSpy,
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
+ onopen: onOpenSpy,
+ onclose: onCloseSpy,
},
});
@@ -393,21 +349,17 @@ describe("Popover", () => {
...defaultProps,
autoshow: true,
dismissible: false,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- {
- component: PopoverContent,
- props: {
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.closeButton,
- ]),
- },
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ {
+ component: PopoverContent,
+ props: {
+ children: createSvelteComponentsSnippet([
+ defaultChildren.closeButton,
+ ]),
},
- ]),
- },
+ },
+ ]),
},
});
@@ -428,36 +380,32 @@ describe("Popover", () => {
props: {
...defaultProps,
trapFocus: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- {
- component: PopoverContent,
- props: {
- $$slots: {
- default: createRawSnippet(() => ({
- render: () =>
- ' ',
- setup: (target) => {
- const closeButton = mount(
- PopoverCloseButton,
- {
- target,
- props: {
- label: "Close",
- },
- }
- );
- return () => {
- unmount(closeButton);
- };
- },
- })),
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ {
+ component: PopoverContent,
+ props: {
+ children: createRawSnippet(() => ({
+ render: () =>
+ ' ',
+ setup: (target) => {
+ const closeButton = mount(
+ PopoverCloseButton,
+ {
+ target,
+ props: {
+ label: "Close",
+ },
+ }
+ );
+ return () => {
+ unmount(closeButton);
+ };
},
- },
+ })),
},
- ]),
- },
+ },
+ ]),
},
});
@@ -501,13 +449,10 @@ describe("Popover", () => {
props: {
...defaultProps,
placement: "top",
- $$slots: {
- // @ts-expect-error $$slots is used to pass children while component is still using Svelte 4 syntax
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
return () => {
@@ -531,12 +476,10 @@ describe("Popover", () => {
props: {
...defaultProps,
autoshow: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -555,12 +498,10 @@ describe("Popover", () => {
props: {
...defaultProps,
strategy,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -579,22 +520,18 @@ describe("Popover", () => {
render(Popover, {
props: {
...defaultProps,
- $$slots: {
- default: createSvelteComponentsSnippet([
- {
- component: PopoverReference,
- props: {
- $$slots: {
- default: createRawSnippet(() => ({
- render: () =>
- "non-button-role element ",
- })),
- },
- },
+ children: createSvelteComponentsSnippet([
+ {
+ component: PopoverReference,
+ props: {
+ children: createRawSnippet(() => ({
+ render: () =>
+ "non-button-role element ",
+ })),
},
- defaultChildren.content,
- ]),
- },
+ },
+ defaultChildren.content,
+ ]),
},
})
).to.throw(
@@ -607,12 +544,10 @@ describe("Popover", () => {
props: {
...defaultProps,
autoshow: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -632,12 +567,10 @@ describe("Popover", () => {
render(Popover, {
props: {
...defaultProps,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -659,12 +592,10 @@ describe("Popover", () => {
...defaultProps,
autoshow: true,
tooltip: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -680,22 +611,18 @@ describe("Popover", () => {
props: {
...defaultProps,
tooltip: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- {
- component: PopoverReference,
- props: {
- $$slots: {
- default: createRawSnippet(() => ({
- render: () =>
- "non-button-role element ",
- })),
- },
- },
+ children: createSvelteComponentsSnippet([
+ {
+ component: PopoverReference,
+ props: {
+ children: createRawSnippet(() => ({
+ render: () =>
+ "non-button-role element ",
+ })),
},
- defaultChildren.content,
- ]),
- },
+ },
+ defaultChildren.content,
+ ]),
},
});
} catch (e) {
@@ -715,12 +642,10 @@ describe("Popover", () => {
props: {
...defaultProps,
tooltip: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -751,12 +676,10 @@ describe("Popover", () => {
props: {
...defaultProps,
tooltip: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -784,12 +707,10 @@ describe("Popover", () => {
props: {
...defaultProps,
tooltip: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -814,12 +735,10 @@ describe("Popover", () => {
props: {
...defaultProps,
tooltip: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- defaultChildren.content,
- ]),
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ defaultChildren.content,
+ ]),
},
});
@@ -852,22 +771,18 @@ describe("Popover", () => {
props: {
...defaultProps,
tooltip: true,
- $$slots: {
- default: createSvelteComponentsSnippet([
- defaultChildren.reference,
- {
- component: PopoverContent,
- props: {
- $$slots: {
- default: createRawSnippet(() => ({
- render: () =>
- ' ',
- })),
- },
- },
+ children: createSvelteComponentsSnippet([
+ defaultChildren.reference,
+ {
+ component: PopoverContent,
+ props: {
+ children: createRawSnippet(() => ({
+ render: () =>
+ ' ',
+ })),
},
- ]),
- },
+ },
+ ]),
},
});
diff --git a/packages/stacks-svelte/src/components/Popover/PopoverCloseButton.svelte b/packages/stacks-svelte/src/components/Popover/PopoverCloseButton.svelte
index f70e8103eb..791a0f8f60 100644
--- a/packages/stacks-svelte/src/components/Popover/PopoverCloseButton.svelte
+++ b/packages/stacks-svelte/src/components/Popover/PopoverCloseButton.svelte
@@ -3,26 +3,36 @@
import { IconClear } from "@stackoverflow/stacks-icons/icons";
import { usePopoverContext } from "./Popover.svelte";
- const pstate = usePopoverContext("PopoverCloseButton");
+ interface Props {
+ /**
+ * The aria-label for the close button
+ */
+ label?: string;
+ /**
+ * Additional CSS classes added to the element
+ */
+ class?: string;
+ /**
+ * Callback fired when the close button is clicked
+ */
+ onclick?: (e: MouseEvent) => void;
+ }
+
+ let { label = "Close", class: className = "", onclick }: Props = $props();
- /**
- * The aria-label for the close button
- */
- export let label: string = "Close";
+ const pstate = usePopoverContext("PopoverCloseButton");
- /**
- * Additional CSS classes added to the element
- */
- let className = "";
- export { className as class };
+ const handleClick = (e: MouseEvent) => {
+ pstate.close();
+ onclick?.(e);
+ };
diff --git a/packages/stacks-svelte/src/components/Popover/PopoverContent.svelte b/packages/stacks-svelte/src/components/Popover/PopoverContent.svelte
index 9f601cbb60..1f2049b36d 100644
--- a/packages/stacks-svelte/src/components/Popover/PopoverContent.svelte
+++ b/packages/stacks-svelte/src/components/Popover/PopoverContent.svelte
@@ -1,52 +1,69 @@
-
+ {@render children?.()}
diff --git a/packages/stacks-svelte/src/components/Popover/PopoverReference.svelte b/packages/stacks-svelte/src/components/Popover/PopoverReference.svelte
index 3bd0efb94a..57e11b52f4 100644
--- a/packages/stacks-svelte/src/components/Popover/PopoverReference.svelte
+++ b/packages/stacks-svelte/src/components/Popover/PopoverReference.svelte
@@ -1,14 +1,22 @@
-
+ {@render children?.()}