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

Expose child routes on currentRoute object; add property for a CSS class on inactive routes #1650

Closed
wants to merge 3 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
82 changes: 82 additions & 0 deletions examples/inactive-links/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const Home = { template: '<div><h2>Home</h2></div>' }
const About = { template: '<div><h2>About</h2></div>' }
const Others = { template: '<div><h2>Others</h2></div>' }

const Users = {
template: `
<div>
<h2>Users</h2>
<router-view></router-view>
</div>
`
}

const User = { template: '<div>{{ $route.params.username }}</div>' }

const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/others', component: Others },
{ path: '/users', component: Users,
children: [
{ path: ':username', name: 'user', component: User }
]
}
]
})

new Vue({
router,
template: `
<div id="app">
<h1>Active Links</h1>
<ul>
<li><router-link to="/">/</router-link></li>
<li><router-link to="/" exact>/ (exact match)</router-link></li>

<li><router-link to="/users">/users</router-link></li>
<li><router-link to="/users" exact>/users (exact match)</router-link></li>

<li><router-link to="/users/evan">/users/evan</router-link></li>
<li><router-link to="/users/evan#foo">/users/evan#foo</router-link></li>
<li>
<router-link :to="{ path: '/users/evan', query: { foo: 'bar' }}">
/users/evan?foo=bar
</router-link>
</li>
<li><!-- #635 -->
<router-link :to="{ name: 'user', params: { username: 'evan' }, query: { foo: 'bar' }}" exact>
/users/evan?foo=bar (named view + exact match)
</router-link>
</li>
<li>
<router-link :to="{ path: '/users/evan', query: { foo: 'bar', baz: 'qux' }}">
/users/evan?foo=bar&baz=qux
</router-link>
</li>

<li><router-link to="/about">/about</router-link></li>

<router-link tag="li" to="/about">
<a>/about (active class on outer element)</a>
</router-link>

<li><router-link to="/others" active-class="custom-active" exact-active-class="custom-exact-active" inactive-class="custom-inactive" exact-inactive-class="custom-exact-inactive">/others</router-link></li>

<router-link tag="li" to="/others" active-class="custom-active" exact-active-class="custom-exact-active" inactive-class="custom-inactive" exact-inactive-class="custom-exact-inactive">
<a>/others (active class on outer element)</a>
</router-link>

</ul>
<router-view class="view"></router-view>
</div>
`
}).$mount('#app')
34 changes: 34 additions & 0 deletions examples/inactive-links/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!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;
}
a.router-link-inactive, li.router-link-inactive a {
color: #f6f;
}
a.router-link-exact-inactive, li.router-link-exact-inactive a {
border-bottom: 1px solid #f6f;
}

a.custom-active, li.custom-active a {
color: #66f;
}
a.custom-exact-active, li.custom-exact-active a {
border-bottom: 1px solid #66f;
}
a.custom-inactive, li.custom-inactive a {
color: #6f6;
}
a.custom-exact-inactive, li.custom-exact-inactive a {
border-bottom: 1px solid #6f6;
}

</style>
<a href="/">&larr; Examples index</a>
<div id="app"></div>
<script src="/__build__/shared.js"></script>
<script src="/__build__/inactive-links.js"></script>
1 change: 1 addition & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ <h1>Vue Router Examples</h1>
<li><a href="named-views">Named Views</a></li>
<li><a href="route-matching">Route Matching</a></li>
<li><a href="active-links">Active Links</a></li>
<li><a href="inactive-links">Inactive Links</a></li>
<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>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"lint": "eslint src examples",
"test": "npm run lint && flow check && npm run test:unit && npm run test:e2e && npm run test:types",
"test:unit": "jasmine JASMINE_CONFIG_PATH=test/unit/jasmine.json",
"test:e2e": "node test/e2e/runner.js",
"test:e2e": "xvfb-run node test/e2e/runner.js && rm -rf ./xvfb-run.* && rm -rf ./.com.google.* && rm -rf ./.org.chromium.*",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please let it as it was

"test:types": "tsc -p types/test",
"docs": "cd docs && gitbook install && gitbook serve",
"docs:deploy": "bash ./build/update-docs.sh",
Expand Down
18 changes: 18 additions & 0 deletions src/components/link.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export default {
replace: Boolean,
activeClass: String,
exactActiveClass: String,
inactiveClass: String,
exactInactiveClass: String,
event: {
type: eventTypes,
default: 'click'
Expand All @@ -36,6 +38,8 @@ export default {
const classes = {}
const globalActiveClass = router.options.linkActiveClass
const globalExactActiveClass = router.options.linkExactActiveClass
const globalInactiveClass = router.options.linkInactiveClass
const globalExactInactiveClass = router.options.linkExactInactiveClass
// Support global empty active class
const activeClassFallback = globalActiveClass == null
? 'router-link-active'
Expand All @@ -49,6 +53,18 @@ export default {
const exactActiveClass = this.exactActiveClass == null
? exactActiveClassFallback
: this.exactActiveClass
const inactiveClassFallback = globalInactiveClass == null
? 'router-link-inactive'
: globalInactiveClass
const exactInactiveClassFallback = globalExactInactiveClass == null
? 'router-link-exact-inactive'
: globalExactInactiveClass
const inactiveClass = this.inactiveClass == null
? inactiveClassFallback
: this.inactiveClass
const exactInactiveClass = this.exactInactiveClass == null
? exactInactiveClassFallback
: this.exactInactiveClass
const compareTarget = location.path
? createRoute(null, location, null, router)
: route
Expand All @@ -57,6 +73,8 @@ export default {
classes[activeClass] = this.exact
? classes[exactActiveClass]
: isIncludedRoute(current, compareTarget)
classes[exactInactiveClass] = !classes[exactActiveClass]
classes[inactiveClass] = !classes[activeClass]

const handler = e => {
if (guardEvent(e)) {
Expand Down
1 change: 1 addition & 0 deletions src/create-route-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ function addRouteRecord (
regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),
components: route.components || { default: route.component },
instances: {},
children: route.children || [],
name,
parent,
matchAs,
Expand Down
2 changes: 2 additions & 0 deletions src/util/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export function createRoute (
const route: Route = {
name: location.name || (record && record.name),
meta: (record && record.meta) || {},
children: (record && record.children) || [],
parent: (record && record.parent) || {},
path: location.path || '/',
hash: location.hash || '',
query: location.query || {},
Expand Down
5 changes: 4 additions & 1 deletion test/e2e/nightwatch.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ module.exports = {
'desiredCapabilities': {
'browserName': 'chrome',
'javascriptEnabled': true,
'acceptSslCerts': true
'acceptSslCerts': true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove this as well please

"chromeOptions": {
"args" : ["--no-sandbox"]
}
}
},

Expand Down
64 changes: 64 additions & 0 deletions test/e2e/specs/inactive-links.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@

module.exports = {
'inactive links': function (browser) {
browser
.url('http://localhost:8080/inactive-links/')
.waitForElementVisible('#app', 1000)
.assert.count('li a', 13)
// assert correct href with base
.assert.attributeContains('li:nth-child(1) a', 'href', '/inactive-links/')
.assert.attributeContains('li:nth-child(2) a', 'href', '/inactive-links/')
.assert.attributeContains('li:nth-child(3) a', 'href', '/inactive-links/users')
.assert.attributeContains('li:nth-child(4) a', 'href', '/inactive-links/users')
.assert.attributeContains('li:nth-child(5) a', 'href', '/inactive-links/users/evan')
.assert.attributeContains('li:nth-child(6) a', 'href', '/inactive-links/users/evan#foo')
.assert.attributeContains('li:nth-child(7) a', 'href', '/inactive-links/users/evan?foo=bar')
.assert.attributeContains('li:nth-child(8) a', 'href', '/inactive-links/users/evan?foo=bar')
.assert.attributeContains('li:nth-child(9) a', 'href', '/inactive-links/users/evan?foo=bar&baz=qux')
.assert.attributeContains('li:nth-child(10) a', 'href', '/inactive-links/about')
.assert.attributeContains('li:nth-child(11) a', 'href', '/inactive-links/about')
.assert.attributeContains('li:nth-child(12) a', 'href', '/inactive-links/others')
.assert.attributeContains('li:nth-child(13) a', 'href', '/inactive-links/others')
.assert.containsText('.view', 'Home')

assertCustomActiveLinks(12, [12], [13], [12], [13])
assertCustomInactiveLinks(10, [12], [13], [12], [13])

browser.end()

function assertCustomActiveLinks (n, activeA, activeLI, exactActiveA, exactActiveLI) {
browser.click(`li:nth-child(${n}) a`)
activeA.forEach(i => {
browser.assert.cssClassPresent(`li:nth-child(${i}) a`, 'custom-active')
})
activeLI && activeLI.forEach(i => {
browser.assert.cssClassPresent(`li:nth-child(${i})`, 'custom-active')
})
exactActiveA.forEach(i => {
browser.assert.cssClassPresent(`li:nth-child(${i}) a`, 'custom-exact-active')
.assert.cssClassPresent(`li:nth-child(${i}) a`, 'custom-active')
})
exactActiveLI && exactActiveLI.forEach(i => {
browser.assert.cssClassPresent(`li:nth-child(${i})`, 'custom-exact-active')
.assert.cssClassPresent(`li:nth-child(${i})`, 'custom-active')
})
}
function assertCustomInactiveLinks (n, activeA, activeLI, exactActiveA, exactActiveLI) {
browser.click(`li:nth-child(${n}) a`)
activeA.forEach(i => {
browser.assert.cssClassPresent(`li:nth-child(${i}) a`, 'custom-inactive')
})
activeLI && activeLI.forEach(i => {
browser.assert.cssClassPresent(`li:nth-child(${i})`, 'custom-inactive')
})
exactActiveA.forEach(i => {
browser.assert.cssClassPresent(`li:nth-child(${i}) a`, 'custom-exact-inactive')
.assert.cssClassPresent(`li:nth-child(${i}) a`, 'custom-inactive')
})
exactActiveLI && exactActiveLI.forEach(i => {
browser.assert.cssClassPresent(`li:nth-child(${i})`, 'custom-exact-inactive')
.assert.cssClassPresent(`li:nth-child(${i})`, 'custom-inactive')
})
}
}
}
78 changes: 78 additions & 0 deletions test/unit/specs/children.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import Vue from 'vue'
import VueRouter from '../../../src/index'

Vue.use(VueRouter)

describe('currentRoute', () => {
describe('children[]', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think a describe is necessary here. a test with should have children and another with should have parent.children is enough

it('should work', () => {
const router = new VueRouter({
routes:
[
{
path: '/jobs',
},
{
path: '/setup',
children:
[
{
path: 'lists',
},
{
path: 'periods',
}
]
}
]
})

router.push('/setup')

expect(router.currentRoute.fullPath).toEqual('/setup')
expect(router.currentRoute.children.length).toEqual(2)
expect(router.currentRoute.children[0].path).toEqual('lists')
expect(router.currentRoute.children[1].path).toEqual('periods')
expect(router.currentRoute.children[1].fullPath).toEqual(undefined)
})
})

describe('parent', () => {
it('should work', () => {
const router = new VueRouter({
routes:
[
{
path: '/jobs',
},
{
path: '/setup',
children:
[
{
path: '/',
redirect: 'lists'
},
{
path: 'lists',
},
{
path: 'periods',
}
]
}
]
})

router.push('/setup/lists')

expect(router.currentRoute.fullPath).toEqual('/setup/lists')
expect(router.currentRoute.children.length).toEqual(0)
expect(router.currentRoute.parent.children.length).toEqual(3)
expect(router.currentRoute.parent.children[0].path).toEqual('/')
expect(router.currentRoute.parent.children[2].path).toEqual('periods')
expect(router.currentRoute.parent.children[2].fullPath).toEqual(undefined)
expect(router.currentRoute.parent.fullPath).toEqual(undefined)
})
})
})