From 7b7ac284691c7df6a267524bb670e69dd1330fa7 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Thu, 17 Jun 2021 12:55:07 +1000 Subject: [PATCH 1/7] wip: testing teleport --- tests/features/teleport.spec.ts | 51 +++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/features/teleport.spec.ts diff --git a/tests/features/teleport.spec.ts b/tests/features/teleport.spec.ts new file mode 100644 index 000000000..0c6cba977 --- /dev/null +++ b/tests/features/teleport.spec.ts @@ -0,0 +1,51 @@ +import { defineComponent, FunctionalComponent, h, Teleport } from 'vue' +import { mount } from '../../src' + +describe('teleport', () => { + beforeEach(() => { + document.body.outerHTML = '' + }) + + it('teleports a string', () => { + const destination = document.createElement('div') + destination.id = 'far-away' + document.body.appendChild(destination) + + const Comp = defineComponent({ + setup() { + return () => + h(() => h(Teleport, { to: '#far-away' }, h('div', 'teleported'))) + } + }) + + const wrapper = mount(Comp) + + expect(document.body.outerHTML).toBe(`
teleported
`) + }) + + it('teleports a component with props', async () => { + const destination = document.createElement('div') + destination.id = 'far-away' + document.body.appendChild(destination) + + const Greeter: FunctionalComponent<{ msg: string }, { greet: (msg: string) => void }> = ({ msg }, { emit }) => { + return h('button', { onClick: () => emit('greet', `${msg.toUpperCase()}!!!`) }, `${msg}!!!`) + } + + const onGreet = jest.fn() + + const Comp = defineComponent({ + setup() { + return () => + h(() => h(Teleport, { to: '#far-away' }, h(Greeter, { onGreet, msg: 'Hello' }))) + } + }) + + const wrapper = mount(Comp) + document.querySelector('button')!.click() + + expect(onGreet).toHaveBeenCalledWith('HELLO!!!') + + expect(document.body.outerHTML).toBe(`
`) + }) +}) From dbff7d2d7f2c4a83bedea9ad353ac2735b0284bc Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Thu, 17 Jun 2021 12:55:52 +1000 Subject: [PATCH 2/7] lint --- tests/features/teleport.spec.ts | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/tests/features/teleport.spec.ts b/tests/features/teleport.spec.ts index 0c6cba977..7ffb0bd50 100644 --- a/tests/features/teleport.spec.ts +++ b/tests/features/teleport.spec.ts @@ -20,7 +20,9 @@ describe('teleport', () => { const wrapper = mount(Comp) - expect(document.body.outerHTML).toBe(`
teleported
`) + expect(document.body.outerHTML).toBe( + `
teleported
` + ) }) it('teleports a component with props', async () => { @@ -28,8 +30,15 @@ describe('teleport', () => { destination.id = 'far-away' document.body.appendChild(destination) - const Greeter: FunctionalComponent<{ msg: string }, { greet: (msg: string) => void }> = ({ msg }, { emit }) => { - return h('button', { onClick: () => emit('greet', `${msg.toUpperCase()}!!!`) }, `${msg}!!!`) + const Greeter: FunctionalComponent< + { msg: string }, + { greet: (msg: string) => void } + > = ({ msg }, { emit }) => { + return h( + 'button', + { onClick: () => emit('greet', `${msg.toUpperCase()}!!!`) }, + `${msg}!!!` + ) } const onGreet = jest.fn() @@ -37,7 +46,13 @@ describe('teleport', () => { const Comp = defineComponent({ setup() { return () => - h(() => h(Teleport, { to: '#far-away' }, h(Greeter, { onGreet, msg: 'Hello' }))) + h(() => + h( + Teleport, + { to: '#far-away' }, + h(Greeter, { onGreet, msg: 'Hello' }) + ) + ) } }) @@ -46,6 +61,8 @@ describe('teleport', () => { expect(onGreet).toHaveBeenCalledWith('HELLO!!!') - expect(document.body.outerHTML).toBe(`
`) + expect(document.body.outerHTML).toBe( + `
` + ) }) }) From 7cf22de2d83125739b348f5df3c829a8f1677188 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Thu, 17 Jun 2021 14:39:05 +1000 Subject: [PATCH 3/7] add example with getComponent --- tests/features/teleport.spec.ts | 71 +++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/tests/features/teleport.spec.ts b/tests/features/teleport.spec.ts index 7ffb0bd50..596531cac 100644 --- a/tests/features/teleport.spec.ts +++ b/tests/features/teleport.spec.ts @@ -1,4 +1,4 @@ -import { defineComponent, FunctionalComponent, h, Teleport } from 'vue' +import { defineComponent, h, Teleport } from 'vue' import { mount } from '../../src' describe('teleport', () => { @@ -25,22 +25,33 @@ describe('teleport', () => { ) }) + const Greeter = defineComponent({ + emits: { + greet: (msg: string) => { + return true + } + }, + props: { + msg: { + type: String, + required: true + } + }, + setup({ msg }, { emit }) { + return () => + h( + 'button', + { onClick: () => emit('greet', `${msg.toUpperCase()}!!!`) }, + `${msg}!!!` + ) + } + }) + it('teleports a component with props', async () => { const destination = document.createElement('div') destination.id = 'far-away' document.body.appendChild(destination) - const Greeter: FunctionalComponent< - { msg: string }, - { greet: (msg: string) => void } - > = ({ msg }, { emit }) => { - return h( - 'button', - { onClick: () => emit('greet', `${msg.toUpperCase()}!!!`) }, - `${msg}!!!` - ) - } - const onGreet = jest.fn() const Comp = defineComponent({ @@ -60,6 +71,42 @@ describe('teleport', () => { document.querySelector('button')!.click() expect(onGreet).toHaveBeenCalledWith('HELLO!!!') + expect(document.body.outerHTML).toBe( + `
` + ) + }) + + it('teleports a component with props', async () => { + const destination = document.createElement('div') + destination.id = 'far-away' + document.body.appendChild(destination) + + const onGreet = jest.fn() + + const Comp = defineComponent({ + setup() { + return () => + h(() => + h( + Teleport, + { to: '#far-away' }, + h(Greeter, { onGreet, msg: 'Hello' }) + ) + ) + } + }) + + const wrapper = mount(Comp) + + // although is teleported outside the component, + // it's still in the vdom of the wrapper, + // so you can just query it like you normally would - pretty neat. + await wrapper.getComponent(Greeter).trigger('click') + + expect(onGreet).toHaveBeenCalledWith('HELLO!!!') + expect(wrapper.getComponent(Greeter).props()).toEqual({ + msg: 'Hello' + }) expect(document.body.outerHTML).toBe( `
` From da16a905e995fbed0f194d3e1fcff7318fef8ece Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Thu, 17 Jun 2021 15:43:58 +1000 Subject: [PATCH 4/7] tests around emits and props --- tests/components/EmitsEvent.vue | 18 +++++++++++++ tests/components/WithTeleportEmitsComp.vue | 9 +++++++ tests/components/WithTeleportPropsComp.vue | 9 +++++++ tests/features/teleport.spec.ts | 31 ++++++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 tests/components/EmitsEvent.vue create mode 100644 tests/components/WithTeleportEmitsComp.vue create mode 100644 tests/components/WithTeleportPropsComp.vue diff --git a/tests/components/EmitsEvent.vue b/tests/components/EmitsEvent.vue new file mode 100644 index 000000000..bea93f6d2 --- /dev/null +++ b/tests/components/EmitsEvent.vue @@ -0,0 +1,18 @@ + + + \ No newline at end of file diff --git a/tests/components/WithTeleportEmitsComp.vue b/tests/components/WithTeleportEmitsComp.vue new file mode 100644 index 000000000..e008eff4a --- /dev/null +++ b/tests/components/WithTeleportEmitsComp.vue @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/tests/components/WithTeleportPropsComp.vue b/tests/components/WithTeleportPropsComp.vue new file mode 100644 index 000000000..e335ab866 --- /dev/null +++ b/tests/components/WithTeleportPropsComp.vue @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/tests/features/teleport.spec.ts b/tests/features/teleport.spec.ts index 596531cac..9cc8bcaae 100644 --- a/tests/features/teleport.spec.ts +++ b/tests/features/teleport.spec.ts @@ -1,5 +1,9 @@ import { defineComponent, h, Teleport } from 'vue' import { mount } from '../../src' +import WithTeleportPropsComp from '../components/WithTeleportPropsComp.vue' +import WithTeleportEmitsComp from '../components/WithTeleportEmitsComp.vue' +import WithProps from '../components/WithProps.vue' +import EmitsEvent from '../components/EmitsEvent.vue' describe('teleport', () => { beforeEach(() => { @@ -112,4 +116,31 @@ describe('teleport', () => { `
` ) }) + + it('works with SFC and gets props', async () => { + const destination = document.createElement('div') + destination.id = 'somewhere' + document.body.appendChild(destination) + + const wrapper = mount(WithTeleportPropsComp) + + const withProps = wrapper.getComponent(WithProps) + + expect(withProps.props()).toEqual({ + msg: 'hi there' + }) + }) + + it('works with SFC and captures emitted events', async () => { + const destination = document.createElement('div') + destination.id = 'somewhere' + document.body.appendChild(destination) + + const wrapper = mount(WithTeleportEmitsComp) + + const withProps = wrapper.getComponent(EmitsEvent) + withProps.trigger('click') + + expect(withProps.emitted().greet[0]).toEqual(['Hey!']) + }) }) From c148def121a71862d3fc6d88685f3b8b92f7fd20 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Wed, 23 Jun 2021 17:44:19 +1000 Subject: [PATCH 5/7] finish article --- docs/guide/advanced/teleport.md | 189 +++++++++++++++++++++++++++ tests/docs-examples/Navbar.vue | 16 +++ tests/docs-examples/Signup.vue | 30 +++++ tests/docs-examples/teleport.spec.ts | 25 ++++ tsconfig.json | 1 + 5 files changed, 261 insertions(+) create mode 100644 docs/guide/advanced/teleport.md create mode 100644 tests/docs-examples/Navbar.vue create mode 100644 tests/docs-examples/Signup.vue create mode 100644 tests/docs-examples/teleport.spec.ts diff --git a/docs/guide/advanced/teleport.md b/docs/guide/advanced/teleport.md new file mode 100644 index 000000000..3df59e36c --- /dev/null +++ b/docs/guide/advanced/teleport.md @@ -0,0 +1,189 @@ +# Testing Teleport + +Vue 3 comes with a new built-in component: ``, which allows components to "teleport" their content far outside of their own `