Skip to content

Commit b80d9bd

Browse files
authored
fix: wire up events in mount correctly and fix its types (#10553)
fixes #10551
1 parent f510201 commit b80d9bd

File tree

7 files changed

+76
-9
lines changed

7 files changed

+76
-9
lines changed

.changeset/shaggy-cameras-live.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: wire up `events` in `mount` correctly and fix its types

packages/svelte/src/internal/client/render.js

+13-5
Original file line numberDiff line numberDiff line change
@@ -2432,7 +2432,7 @@ export function createRoot() {
24322432
* @param {{
24332433
* target: Node;
24342434
* props?: Props;
2435-
* events?: Events;
2435+
* events?: { [Property in keyof Events]: (e: Events[Property]) => any };
24362436
* context?: Map<any, any>;
24372437
* intro?: boolean;
24382438
* }} options
@@ -2456,7 +2456,7 @@ export function mount(component, options) {
24562456
* @param {{
24572457
* target: Node;
24582458
* props?: Props;
2459-
* events?: Events;
2459+
* events?: { [Property in keyof Events]: (e: Events[Property]) => any };
24602460
* context?: Map<any, any>;
24612461
* intro?: boolean;
24622462
* recover?: false;
@@ -2524,7 +2524,7 @@ export function hydrate(component, options) {
25242524
* target: Node;
25252525
* anchor: null | Text;
25262526
* props?: Props;
2527-
* events?: Events;
2527+
* events?: { [Property in keyof Events]: (e: Events[Property]) => any };
25282528
* context?: Map<any, any>;
25292529
* intro?: boolean;
25302530
* recover?: false;
@@ -2547,8 +2547,16 @@ function _mount(Component, options) {
25472547
/** @type {import('../client/types.js').ComponentContext} */ (current_component_context).c =
25482548
options.context;
25492549
}
2550-
// @ts-expect-error the public typings are not what the actual function looks like
2551-
component = Component(options.anchor, options.props || {}) || {};
2550+
if (!options.props) {
2551+
options.props = /** @type {Props} */ ({});
2552+
}
2553+
if (options.events) {
2554+
// We can't spread the object or else we'd lose the state proxy stuff, if it is one
2555+
/** @type {any} */ (options.props).$$events = options.events;
2556+
}
2557+
component =
2558+
// @ts-expect-error the public typings are not what the actual function looks like
2559+
Component(options.anchor, options.props) || {};
25522560
if (options.context) {
25532561
pop();
25542562
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { tick } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
html: `<button>toggle</button><div></div>`,
6+
async test({ assert, target }) {
7+
const button = target.querySelector('button');
8+
9+
await button?.click();
10+
assert.htmlEqual(target.innerHTML, `<button>toggle</button><div><button>0</button></div>`);
11+
12+
const inner_button = target.querySelector('div')?.querySelector('button');
13+
14+
inner_button?.click();
15+
await tick();
16+
assert.htmlEqual(target.innerHTML, `<button>toggle</button><div><button>2</button></div>`);
17+
18+
await button?.click();
19+
assert.htmlEqual(target.innerHTML, `<button>toggle</button><div></div>`);
20+
}
21+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
import { createEventDispatcher, getContext } from "svelte";
3+
4+
let { count } = $props();
5+
const multiply = getContext('multiply');
6+
7+
// Use legacy createEventDispatcher here to test that `events` property in `mount` works
8+
const dispatch = createEventDispatcher();
9+
</script>
10+
11+
<button onclick={() => dispatch('update', count + 1 * multiply)}>{count}</button>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script>
2+
import { mount, unmount } from 'svelte';
3+
import Inner from './inner.svelte';
4+
5+
let el;
6+
let component;
7+
let props = $state({count: 0});
8+
9+
function toggle() {
10+
if (component) {
11+
unmount(component);
12+
component = null;
13+
} else {
14+
component = mount(Inner, { target: el, props, context: new Map([['multiply', 2]]), events: { update: (e) => props.count = e.detail } });
15+
}
16+
}
17+
</script>
18+
19+
<button onclick={toggle}>toggle</button>
20+
<div bind:this={el}></div>

packages/svelte/tests/types/component.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ mount(NewComponent, {
112112
x: ''
113113
},
114114
events: {
115-
event: new MouseEvent('click')
115+
event: (e) => e.offsetX
116116
},
117117
immutable: true,
118118
intro: false,
@@ -127,7 +127,9 @@ hydrate(NewComponent, {
127127
x: ''
128128
},
129129
events: {
130-
event: new MouseEvent('click')
130+
event: (e) =>
131+
// @ts-expect-error
132+
e.doesNotExist
131133
},
132134
immutable: true,
133135
intro: false,

packages/svelte/types/index.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ declare module 'svelte' {
334334
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: {
335335
target: Node;
336336
props?: Props | undefined;
337-
events?: Events | undefined;
337+
events?: { [Property in keyof Events]: (e: Events[Property]) => any; } | undefined;
338338
context?: Map<any, any> | undefined;
339339
intro?: boolean | undefined;
340340
}): Exports;
@@ -345,7 +345,7 @@ declare module 'svelte' {
345345
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: {
346346
target: Node;
347347
props?: Props | undefined;
348-
events?: Events | undefined;
348+
events?: { [Property in keyof Events]: (e: Events[Property]) => any; } | undefined;
349349
context?: Map<any, any> | undefined;
350350
intro?: boolean | undefined;
351351
recover?: false | undefined;

0 commit comments

Comments
 (0)