From 539c865533099c2c0e51228f0b941bc06ae7e9d0 Mon Sep 17 00:00:00 2001 From: Matteo Rigon Date: Tue, 11 Feb 2020 15:06:37 +0100 Subject: [PATCH] fix(vue-app): use child transition name when navigating to parent (#6946) --- packages/vue-app/template/client.js | 36 +++--- test/e2e/page-transitions.browser.test.js | 112 ++++++++++++++++++ .../page-transitions/layouts/default.vue | 6 + .../page-transitions/page-transitions.test.js | 3 + .../page-transitions/pages/callbacks.vue | 12 ++ .../pages/callbacks/child.vue | 11 ++ .../fixtures/page-transitions/pages/index.vue | 11 ++ .../pages/transition-properties.vue | 18 +++ .../pages/transition-properties/child.vue | 18 +++ .../utils/transition-properties.js | 34 ++++++ test/utils/browser.js | 3 + 11 files changed, 245 insertions(+), 19 deletions(-) create mode 100644 test/e2e/page-transitions.browser.test.js create mode 100644 test/fixtures/page-transitions/layouts/default.vue create mode 100644 test/fixtures/page-transitions/page-transitions.test.js create mode 100644 test/fixtures/page-transitions/pages/callbacks.vue create mode 100644 test/fixtures/page-transitions/pages/callbacks/child.vue create mode 100644 test/fixtures/page-transitions/pages/index.vue create mode 100644 test/fixtures/page-transitions/pages/transition-properties.vue create mode 100644 test/fixtures/page-transitions/pages/transition-properties/child.vue create mode 100644 test/fixtures/page-transitions/utils/transition-properties.js diff --git a/packages/vue-app/template/client.js b/packages/vue-app/template/client.js index 156d24003d..ab40be8e82 100644 --- a/packages/vue-app/template/client.js +++ b/packages/vue-app/template/client.js @@ -111,31 +111,29 @@ function componentOption (component, key, ...args) { return option } -function mapTransitions (Components, to, from) { +function mapTransitions (toComponents, to, from) { const componentTransitions = (component) => { const transition = componentOption(component, 'transition', to, from) || {} return (typeof transition === 'string' ? { name: transition } : transition) } - const tComponents = [].concat(Components) + + const fromComponents = from ? getMatchedComponents(from) : [] + const maxDepth = Math.max(toComponents.length, fromComponents.length) - // If leaving a child route to a parent, keep the child leave transition - while (from && from.matched.length > tComponents.length) { // eslint-disable-line no-unmodified-loop-condition - tComponents.push({}) + const mergedTransitions = [] + for (let i=0; i typeof toTransitions[key] !== 'undefined' && !key.toLowerCase().includes('leave')) + .forEach((key) => { transitions[key] = toTransitions[key] }) + + mergedTransitions.push(transitions) } - return tComponents.map((Component, i) => { - // Clone original object to prevent overrides - const transitions = Object.assign({}, componentTransitions(Component)) - - // Combine transitions & prefer `leave` transitions of 'from' route - if (from && from.matched[i] && from.matched[i].components.default) { - const fromTransitions = componentTransitions(from.matched[i].components.default) - Object.keys(fromTransitions) - .filter(key => fromTransitions[key] && key.toLowerCase().includes('leave')) - .forEach((key) => { transitions[key] = fromTransitions[key] }) - } - - return transitions - }) + return mergedTransitions } <% } %> <% if (loading) { %>async <% } %>function loadAsyncComponents (to, from, next) { diff --git a/test/e2e/page-transitions.browser.test.js b/test/e2e/page-transitions.browser.test.js new file mode 100644 index 0000000000..783b697a9c --- /dev/null +++ b/test/e2e/page-transitions.browser.test.js @@ -0,0 +1,112 @@ +import Browser from '../utils/browser' +import { loadFixture, getPort, Nuxt } from '../utils' + +let port +const browser = new Browser() +const url = route => 'http://localhost:' + port + route + +let nuxt = null +let page = null + +const parseEvents = async (page) => { + const events = await page.evaluate(() => [...document.querySelectorAll('#transition-events li')].map(li => li.textContent)) + return events.map(event => event.split('|')) +} + +describe('page transitions (browser)', () => { + beforeAll(async () => { + const config = await loadFixture('page-transitions') + nuxt = new Nuxt(config) + await nuxt.ready() + + port = await getPort() + await nuxt.server.listen(port, 'localhost') + + await browser.start({ + // slowMo: 50, + // headless: false + }) + }) + + test('Open /', async () => { + page = await browser.page(url('/')) + + expect(await page.$text('h1')).toBe('Index page') + }) + + test('Root page callbacks', async () => { + await page.nuxt.navigate('/callbacks') + const events = await parseEvents(page) + expect(events).toEqual( + [ + ['index', 'beforeLeave'], + ['index', 'leave'], + ['index', 'afterLeave'], + ['callbacks', 'beforeEnter'], + ['callbacks', 'enter'], + ['callbacks', 'afterEnter'] + ] + ) + }) + + test('Parent -> Child page callbacks', async () => { + await page.nuxt.navigate('/callbacks/child') + const events = await parseEvents(page) + expect(events).toEqual( + [ + ['callbacks-child', 'beforeEnter'], + ['callbacks-child', 'enter'], + ['callbacks-child', 'afterEnter'] + ] + ) + }) + + test('Child -> Parent page callbacks', async () => { + await page.nuxt.navigate('/callbacks') + const events = await parseEvents(page) + expect(events).toEqual( + [ + ['callbacks-child', 'beforeLeave'], + ['callbacks-child', 'leave'], + ['callbacks-child', 'afterLeave'] + ] + ) + }) + + test('Root page transition properties', async () => { + await page.nuxt.navigate('/transition-properties') + const transitionsData = await page.nuxt.transitionsData() + expect(transitionsData.length).toBe(1) + expect(transitionsData[0].name).toBe('custom') + expect(transitionsData[0].appear).toBe(true) + expect(transitionsData[0].css).toBe(false) + expect(transitionsData[0].mode).toBe('in-out') + expect(transitionsData[0].duration).toBe(3000) + }) + + test('Parent -> child transition properties', async () => { + await page.nuxt.navigate('/transition-properties/child') + const transitionsData = await page.nuxt.transitionsData() + expect(transitionsData.length).toBe(2) + expect(transitionsData[0].name).toBe('custom') + expect(transitionsData[1].name).toBe('custom-child') + }) + + test('Child -> parent transition properties', async () => { + await page.nuxt.navigate('/transition-properties') + const transitionsData = await page.nuxt.transitionsData() + expect(transitionsData.length).toBe(2) + expect(transitionsData[0].name).toBe('custom') + expect(transitionsData[1].name).toBe('custom-child') + }) + + afterAll(async () => { + await nuxt.close() + }) + + // Stop browser + afterAll(async () => { + await page.close() + await browser.close() + }) +}) diff --git a/test/fixtures/page-transitions/layouts/default.vue b/test/fixtures/page-transitions/layouts/default.vue new file mode 100644 index 0000000000..3d279eb8af --- /dev/null +++ b/test/fixtures/page-transitions/layouts/default.vue @@ -0,0 +1,6 @@ + diff --git a/test/fixtures/page-transitions/page-transitions.test.js b/test/fixtures/page-transitions/page-transitions.test.js new file mode 100644 index 0000000000..1b793798e6 --- /dev/null +++ b/test/fixtures/page-transitions/page-transitions.test.js @@ -0,0 +1,3 @@ +import { buildFixture } from '../../utils/build' + +buildFixture('page-transitions') diff --git a/test/fixtures/page-transitions/pages/callbacks.vue b/test/fixtures/page-transitions/pages/callbacks.vue new file mode 100644 index 0000000000..d84dd0fa1b --- /dev/null +++ b/test/fixtures/page-transitions/pages/callbacks.vue @@ -0,0 +1,12 @@ + + diff --git a/test/fixtures/page-transitions/pages/callbacks/child.vue b/test/fixtures/page-transitions/pages/callbacks/child.vue new file mode 100644 index 0000000000..fdef9df4cc --- /dev/null +++ b/test/fixtures/page-transitions/pages/callbacks/child.vue @@ -0,0 +1,11 @@ + + diff --git a/test/fixtures/page-transitions/pages/index.vue b/test/fixtures/page-transitions/pages/index.vue new file mode 100644 index 0000000000..c4090b7d03 --- /dev/null +++ b/test/fixtures/page-transitions/pages/index.vue @@ -0,0 +1,11 @@ + + diff --git a/test/fixtures/page-transitions/pages/transition-properties.vue b/test/fixtures/page-transitions/pages/transition-properties.vue new file mode 100644 index 0000000000..c7a11ba46d --- /dev/null +++ b/test/fixtures/page-transitions/pages/transition-properties.vue @@ -0,0 +1,18 @@ + + + diff --git a/test/fixtures/page-transitions/pages/transition-properties/child.vue b/test/fixtures/page-transitions/pages/transition-properties/child.vue new file mode 100644 index 0000000000..a757f71273 --- /dev/null +++ b/test/fixtures/page-transitions/pages/transition-properties/child.vue @@ -0,0 +1,18 @@ + + + + diff --git a/test/fixtures/page-transitions/utils/transition-properties.js b/test/fixtures/page-transitions/utils/transition-properties.js new file mode 100644 index 0000000000..fa424a1319 --- /dev/null +++ b/test/fixtures/page-transitions/utils/transition-properties.js @@ -0,0 +1,34 @@ +const addEvent = (componentName, callbackName, clear = false) => { + const ul = document.querySelector('#transition-events') + if (clear) { + ul.innerHTML = '' + } + const li = document.createElement('li') + li.textContent = `${componentName}|${callbackName}` + ul.appendChild(li) +} + +export const createTransitionObject = (componentName, transitionName = 'page', child = false) => ({ + name: transitionName, + + beforeEnter () { + addEvent(componentName, 'beforeEnter', child) + }, + enter (el, done) { + addEvent(componentName, 'enter') + done() + }, + afterEnter () { + addEvent(componentName, 'afterEnter') + }, + beforeLeave () { + addEvent(componentName, 'beforeLeave', true) + }, + leave (el, done) { + addEvent(componentName, 'leave') + done() + }, + afterLeave () { + addEvent(componentName, 'afterLeave') + } +}) diff --git a/test/utils/browser.js b/test/utils/browser.js index 3c5136a5dd..d22e156cee 100644 --- a/test/utils/browser.js +++ b/test/utils/browser.js @@ -105,6 +105,9 @@ export default class Browser { }, storeState () { return page.evaluate($nuxt => $nuxt.$store.state, page.$nuxt) + }, + transitionsData () { + return page.evaluate($nuxt => $nuxt.nuxt.transitions, page.$nuxt) } } return page