Skip to content

Commit

Permalink
fix(vue-app): use child transition name when navigating to parent (#6946
Browse files Browse the repository at this point in the history
)
  • Loading branch information
reegodev committed Feb 11, 2020
1 parent b05d4a7 commit 539c865
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 20 deletions.
38 changes: 18 additions & 20 deletions packages/vue-app/template/client.js
Expand Up @@ -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)

// 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 fromComponents = from ? getMatchedComponents(from) : []
const maxDepth = Math.max(toComponents.length, fromComponents.length)

const mergedTransitions = []
for (let i=0; i<maxDepth; i++) {
// Clone original objects to prevent overrides
const toTransitions = Object.assign({}, componentTransitions(toComponents[i]))
const transitions = Object.assign({}, componentTransitions(fromComponents[i]))

// Combine transitions & prefer `leave` properties of "from" route
Object.keys(toTransitions)
.filter(key => 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) {
Expand Down
112 changes: 112 additions & 0 deletions 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()
})
})
6 changes: 6 additions & 0 deletions test/fixtures/page-transitions/layouts/default.vue
@@ -0,0 +1,6 @@
<template>
<div>
<ul id="transition-events"></ul>
<Nuxt />
</div>
</template>
3 changes: 3 additions & 0 deletions test/fixtures/page-transitions/page-transitions.test.js
@@ -0,0 +1,3 @@
import { buildFixture } from '../../utils/build'

buildFixture('page-transitions')
12 changes: 12 additions & 0 deletions test/fixtures/page-transitions/pages/callbacks.vue
@@ -0,0 +1,12 @@
<template>
<div>
<h1>Callbacks page</h1>
<NuxtChild />
</div>
</template>
<script>
import { createTransitionObject } from '../utils/transition-properties'
export default {
transition: createTransitionObject('callbacks')
}
</script>
11 changes: 11 additions & 0 deletions test/fixtures/page-transitions/pages/callbacks/child.vue
@@ -0,0 +1,11 @@
<template>
<div>
<h1>Callbacks child page</h1>
</div>
</template>
<script>
import { createTransitionObject } from '../../utils/transition-properties'
export default {
transition: createTransitionObject('callbacks-child', 'page', 'true')
}
</script>
11 changes: 11 additions & 0 deletions test/fixtures/page-transitions/pages/index.vue
@@ -0,0 +1,11 @@
<template>
<div>
<h1>Index page</h1>
</div>
</template>
<script>
import { createTransitionObject } from '../utils/transition-properties'
export default {
transition: createTransitionObject('index')
}
</script>
18 changes: 18 additions & 0 deletions test/fixtures/page-transitions/pages/transition-properties.vue
@@ -0,0 +1,18 @@
<template>
<div>
<h1>Transition properties page</h1>
<NuxtChild />
</div>
</template>

<script>
export default {
transition: {
name: 'custom',
appear: true,
css: false,
mode: 'in-out',
duration: 3000
}
}
</script>
@@ -0,0 +1,18 @@
<template>
<div>
<h1>Transition name child page</h1>
</div>
</template>

<script>
export default {
transition: {
name: 'custom-child'
}
}
</script>
<style>
.custom-child-enter-active, .custom-child-leave-active {
transition: opacity 1s ease;
}
</style>
34 changes: 34 additions & 0 deletions 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')
}
})
3 changes: 3 additions & 0 deletions test/utils/browser.js
Expand Up @@ -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
Expand Down

0 comments on commit 539c865

Please sign in to comment.