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 +> + + + + +

Content here

+ +
+

Visible: {visible}

+
+ + + console.log('opened')} + onclose={() => console.log('closed')} +> + {#snippet children({ visible, close })} + + + + +

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: () => "", - })), - }, + children: createRawSnippet(() => ({ + render: () => "", + })), }, }, 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?.()}