Skip to content

fix: wire up events in mount correctly and fix its types #10553

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/shaggy-cameras-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte": patch
---

fix: wire up `events` in `mount` correctly and fix its types
18 changes: 13 additions & 5 deletions packages/svelte/src/internal/client/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -2432,7 +2432,7 @@ export function createRoot() {
* @param {{
* target: Node;
* props?: Props;
* events?: Events;
* events?: { [Property in keyof Events]: (e: Events[Property]) => any };
* context?: Map<any, any>;
* intro?: boolean;
* }} options
Expand All @@ -2456,7 +2456,7 @@ export function mount(component, options) {
* @param {{
* target: Node;
* props?: Props;
* events?: Events;
* events?: { [Property in keyof Events]: (e: Events[Property]) => any };
* context?: Map<any, any>;
* intro?: boolean;
* recover?: false;
Expand Down Expand Up @@ -2524,7 +2524,7 @@ export function hydrate(component, options) {
* target: Node;
* anchor: null | Text;
* props?: Props;
* events?: Events;
* events?: { [Property in keyof Events]: (e: Events[Property]) => any };
* context?: Map<any, any>;
* intro?: boolean;
* recover?: false;
Expand All @@ -2547,8 +2547,16 @@ function _mount(Component, options) {
/** @type {import('../client/types.js').ComponentContext} */ (current_component_context).c =
options.context;
}
// @ts-expect-error the public typings are not what the actual function looks like
component = Component(options.anchor, options.props || {}) || {};
if (!options.props) {
options.props = /** @type {Props} */ ({});
}
if (options.events) {
// We can't spread the object or else we'd lose the state proxy stuff, if it is one
/** @type {any} */ (options.props).$$events = options.events;
}
component =
// @ts-expect-error the public typings are not what the actual function looks like
Component(options.anchor, options.props) || {};
if (options.context) {
pop();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { tick } from 'svelte';
import { test } from '../../test';

export default test({
html: `<button>toggle</button><div></div>`,
async test({ assert, target }) {
const button = target.querySelector('button');

await button?.click();
assert.htmlEqual(target.innerHTML, `<button>toggle</button><div><button>0</button></div>`);

const inner_button = target.querySelector('div')?.querySelector('button');

inner_button?.click();
await tick();
assert.htmlEqual(target.innerHTML, `<button>toggle</button><div><button>2</button></div>`);

await button?.click();
assert.htmlEqual(target.innerHTML, `<button>toggle</button><div></div>`);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script>
import { createEventDispatcher, getContext } from "svelte";
let { count } = $props();
const multiply = getContext('multiply');
// Use legacy createEventDispatcher here to test that `events` property in `mount` works
const dispatch = createEventDispatcher();
</script>

<button onclick={() => dispatch('update', count + 1 * multiply)}>{count}</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script>
import { mount, unmount } from 'svelte';
import Inner from './inner.svelte';
let el;
let component;
let props = $state({count: 0});
function toggle() {
if (component) {
unmount(component);
component = null;
} else {
component = mount(Inner, { target: el, props, context: new Map([['multiply', 2]]), events: { update: (e) => props.count = e.detail } });
}
}
</script>

<button onclick={toggle}>toggle</button>
<div bind:this={el}></div>
6 changes: 4 additions & 2 deletions packages/svelte/tests/types/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ mount(NewComponent, {
x: ''
},
events: {
event: new MouseEvent('click')
event: (e) => e.offsetX
},
immutable: true,
intro: false,
Expand All @@ -127,7 +127,9 @@ hydrate(NewComponent, {
x: ''
},
events: {
event: new MouseEvent('click')
event: (e) =>
// @ts-expect-error
e.doesNotExist
},
immutable: true,
intro: false,
Expand Down
4 changes: 2 additions & 2 deletions packages/svelte/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ declare module 'svelte' {
export function mount<Props extends Record<string, any>, Exports extends Record<string, any>, Events extends Record<string, any>>(component: ComponentType<SvelteComponent<Props, Events, any>>, options: {
target: Node;
props?: Props | undefined;
events?: Events | undefined;
events?: { [Property in keyof Events]: (e: Events[Property]) => any; } | undefined;
context?: Map<any, any> | undefined;
intro?: boolean | undefined;
}): Exports;
Expand All @@ -345,7 +345,7 @@ declare module 'svelte' {
export function hydrate<Props extends Record<string, any>, Exports extends Record<string, any>, Events extends Record<string, any>>(component: ComponentType<SvelteComponent<Props, Events, any>>, options: {
target: Node;
props?: Props | undefined;
events?: Events | undefined;
events?: { [Property in keyof Events]: (e: Events[Property]) => any; } | undefined;
context?: Map<any, any> | undefined;
intro?: boolean | undefined;
recover?: false | undefined;
Expand Down