Skip to content
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

feat: functional router-link, fixed #2021 #2029

Closed
wants to merge 7 commits into from
Closed
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
1 change: 1 addition & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ <h1>Vue Router Examples</h1>
<li><a href="redirect">Redirect</a></li>
<li><a href="route-props">Route Props</a></li>
<li><a href="route-alias">Route Alias</a></li>
<li><a href="router-link">Router Link</a></li>
<li><a href="transitions">Transitions</a></li>
<li><a href="data-fetching">Data Fetching</a></li>
<li><a href="navigation-guards">Navigation Guards</a></li>
Expand Down
52 changes: 52 additions & 0 deletions examples/router-link/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const Home = { template: '<div><h2>{{ $route.fullPath }}</h2></div>' }
const Sub = { template: '<div><h2>Nested View</h2><router-view/></div>' }
const SubNested = { template: '<div><h2>Sub</h2><pre>{{ $route.params.id }}</pre></div>' }

const CustomLink = {
props: ['disabled', 'n'],
template: '<button :disabled="disabled">{{ n }} <slot/></button>'
}

Vue.component('CustomLink', CustomLink)

const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{ path: '/', component: Home },
{ path: '/other', component: Home },
{
path: '/sub/:id',
component: Sub,
children: [
{ path: 'nested1', component: SubNested, name: 'sub1' },
{ path: 'nested2', component: SubNested, name: 'sub2' }
]
}
]
})

new Vue({
router,
template: `
<div id="app">
<h1>Router Link lol</h1>
<ul>
<li><router-link to="/" class="custom-class" :class="{ otherClass: true }">/</router-link></li>
<li><router-link is="CustomLink" disabled n="N" to="/other">CustomLink</router-link></li>
<li><router-link to="/" style="color: crimson" :style="{ fontSize: '8px' }">/</router-link></li>
<li><router-link to="/other">/other</router-link></li>
<li><router-link to="/sub/1">/sub/1</router-link></li>
<li><router-link to="/sub/2">/sub/2</router-link></li>
<li><router-link :to="{ name: 'sub1' }">/sub/:id/nested1</router-link></li>
<li><router-link :to="{ name: 'sub2' }">/sub/:id/nested2</router-link></li>
</ul>
<router-view class="view"></router-view>
</div>
`
}).$mount('#app')
14 changes: 14 additions & 0 deletions examples/router-link/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<link rel="stylesheet" href="/global.css">
<style>
a.router-link-active, li.router-link-active a {
color: #f66;
}
a.router-link-exact-active, li.router-link-exact-active a {
border-bottom: 1px solid #f66;
}
</style>
<a href="/">&larr; Examples index</a>
<div id="app"></div>
<script src="/__build__/shared.js"></script>
<script src="/__build__/router-link.js"></script>
41 changes: 20 additions & 21 deletions src/components/link.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const eventTypes: Array<Function> = [String, Array]

export default {
name: 'RouterLink',
functional: true,
props: {
to: {
type: toTypes,
Expand All @@ -28,10 +29,10 @@ export default {
default: 'click'
}
},
render (h: Function) {
const router = this.$router
const current = this.$route
const { location, route, href } = router.resolve(this.to, current, this.append)
render (h: Function, { props, children, parent, data }: any) {
const router = parent.$router
const current = parent.$route
const { location, route, href } = router.resolve(props.to, current, props.append)

const classes = {}
const globalActiveClass = router.options.linkActiveClass
Expand All @@ -43,52 +44,50 @@ export default {
const exactActiveClassFallback = globalExactActiveClass == null
? 'router-link-exact-active'
: globalExactActiveClass
const activeClass = this.activeClass == null
const activeClass = props.activeClass == null
? activeClassFallback
: this.activeClass
const exactActiveClass = this.exactActiveClass == null
: props.activeClass
const exactActiveClass = props.exactActiveClass == null
? exactActiveClassFallback
: this.exactActiveClass
: props.exactActiveClass
const compareTarget = location.path
? createRoute(null, location, null, router)
: route
const extend = _Vue.util.extend

classes[exactActiveClass] = isSameRoute(current, compareTarget)
classes[activeClass] = this.exact
classes[activeClass] = props.exact
? classes[exactActiveClass]
: isIncludedRoute(current, compareTarget)

const handler = e => {
if (guardEvent(e)) {
if (this.replace) {
if (props.replace) {
router.replace(location)
} else {
router.push(location)
}
}
}

data.class = extend(data.class || {}, classes)

const on = { click: guardEvent }
if (Array.isArray(this.event)) {
this.event.forEach(e => { on[e] = handler })
if (Array.isArray(props.event)) {
props.event.forEach(e => { on[e] = handler })
} else {
on[this.event] = handler
}

const data: any = {
class: classes
on[props.event] = handler
}

if (this.tag === 'a') {
if (props.tag === 'a') {
data.on = on
data.attrs = { href }
} else {
// find the first <a> child and apply listener and href
const a = findAnchor(this.$slots.default)
const a = findAnchor(children)
if (a) {
// in case the <a> is a static node
a.isStatic = false
const extend = _Vue.util.extend
const aData = a.data = extend({}, a.data)
aData.on = on
const aAttrs = a.data.attrs = extend({}, a.data.attrs)
Expand All @@ -99,7 +98,7 @@ export default {
}
}

return h(this.tag, data, this.$slots.default)
return h(props.tag, data, children)
}
}

Expand Down
47 changes: 47 additions & 0 deletions test/e2e/specs/router-link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// This test should be done in a unit test once we migrate to jest

module.exports = {
'router link': function (browser) {
browser
.url('http://localhost:8080/router-link/')
.waitForElementVisible('#app', 1000)
// assert correct href with base
.assert.cssClassPresent('li:nth-child(1) a', 'custom-class')
.assert.cssClassPresent('li:nth-child(1) a', 'otherClass')
.assert.cssClassPresent('li:nth-child(1) a', 'router-link-active')

.assert.containsText('li:nth-child(2) button', 'N CustomLink')

.assert.attributeContains('li:nth-child(3) a', 'style', 'color:')
.assert.attributeContains('li:nth-child(3) a', 'style', 'font-size: 8px')

.click('li:nth-child(5) a')
.assert.attributeContains('li:nth-child(7) a', 'href', '/router-link/sub/1/nested1')
.assert.attributeContains('li:nth-child(8) a', 'href', '/router-link/sub/1/nested2')
.click('li:nth-child(6) a')
.assert.attributeContains('li:nth-child(7) a', 'href', '/router-link/sub/2/nested1')
.assert.attributeContains('li:nth-child(8) a', 'href', '/router-link/sub/2/nested2')

browser.expect.element('li:nth-child(2) button').to.have.attribute('disabled')

browser.end()

function assertActiveLinks (n, activeA, activeLI, exactActiveA, exactActiveLI) {
browser.click(`li:nth-child(${n}) a`)
activeA.forEach(i => {
browser.assert.cssClassPresent(`li:nth-child(${i}) a`, 'router-link-active')
})
activeLI && activeLI.forEach(i => {
browser.assert.cssClassPresent(`li:nth-child(${i})`, 'router-link-active')
})
exactActiveA.forEach(i => {
browser.assert.cssClassPresent(`li:nth-child(${i}) a`, 'router-link-exact-active')
.assert.cssClassPresent(`li:nth-child(${i}) a`, 'router-link-active')
})
exactActiveLI && exactActiveLI.forEach(i => {
browser.assert.cssClassPresent(`li:nth-child(${i})`, 'router-link-exact-active')
.assert.cssClassPresent(`li:nth-child(${i})`, 'router-link-active')
})
}
}
}