Skip to content

Commit

Permalink
feat(link): make empty child active with adjacent children
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Apr 29, 2020
1 parent 67a1426 commit 4b813b1
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 14 deletions.
58 changes: 50 additions & 8 deletions __tests__/RouterLink.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const records = {
homeAlias: {} as RouteRecordNormalized,
foo: {} as RouteRecordNormalized,
parent: {} as RouteRecordNormalized,
childEmpty: {} as RouteRecordNormalized,
child: {} as RouteRecordNormalized,
parentAlias: {} as RouteRecordNormalized,
childAlias: {} as RouteRecordNormalized,
Expand All @@ -33,14 +34,20 @@ records.childAlias = { aliasOf: records.child } as RouteRecordNormalized

type RouteLocationResolved = RouteLocationNormalized & { href: string }

const locations: Record<
string,
{
string: string
normalized: RouteLocationResolved
toResolve?: MatcherLocationRaw & Required<RouteQueryAndHash>
}
> = {
function createLocations<
T extends Record<
string,
{
string: string
normalized: RouteLocationResolved
toResolve?: MatcherLocationRaw & Required<RouteQueryAndHash>
}
>
>(locs: T) {
return locs
}

const locations = createLocations({
basic: {
string: '/home',
// toResolve: { path: '/home', fullPath: '/home', undefined, query: {}, hash: '' },
Expand Down Expand Up @@ -167,6 +174,21 @@ const locations: Record<
},
},

childEmpty: {
string: '/parent',
normalized: {
fullPath: '/parent',
href: '/parent',
path: '/parent',
params: {},
meta: {},
query: {},
hash: '',
matched: [records.parent, records.childEmpty],
redirectedFrom: undefined,
name: undefined,
},
},
child: {
string: '/parent/child',
normalized: {
Expand Down Expand Up @@ -257,6 +279,14 @@ const locations: Record<
name: undefined,
},
},
})

// add paths to records because they are used to check isActive
for (let record in records) {
let location = locations[record as keyof typeof locations]
if (location) {
records[record as keyof typeof records].path = location.normalized.path
}
}

async function factory(
Expand Down Expand Up @@ -461,6 +491,18 @@ describe('RouterLink', () => {
)
})

it('empty path child is active as if it was the parent when on adjacent child', async () => {
const { wrapper } = await factory(
locations.child.normalized,
{ to: locations.childEmpty.string },
locations.childEmpty.normalized
)
expect(wrapper.find('a')!.className).toContain('router-link-active')
expect(wrapper.find('a')!.className).not.toContain(
'router-link-exact-active'
)
})

it('alias parent is active if the child is an absolute path', async () => {
const { wrapper } = await factory(
locations.childAsAbsolute.normalized,
Expand Down
19 changes: 19 additions & 0 deletions playground/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,25 @@
<li>
<router-link to="/always-redirect">/always-redirect</router-link>
</li>
<li>
<router-link to="/children">/children</router-link>
</li>
<li>
<router-link :to="{ name: 'default-child' }"
>/children (child named)</router-link
>
</li>
<li>
<router-link :to="{ name: 'WithChildren' }"
>/children (parent named)</router-link
>
</li>
<li>
<router-link to="/children/a">/children/a</router-link>
</li>
<li>
<router-link to="/children/b">/children/b</router-link>
</li>
<li>
<router-link to="/nested">/nested</router-link>
</li>
Expand Down
1 change: 1 addition & 0 deletions playground/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const router = createRouter({
{ path: '/cant-leave', component: GuardedWithLeave },
{
path: '/children',
name: 'WithChildren',
component,
children: [
{ path: '', name: 'default-child', component },
Expand Down
28 changes: 22 additions & 6 deletions src/RouterLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,29 @@ export function useLink(props: UseLinkOptions) {
const route = computed(() => router.resolve(unref(props.to)))

const activeRecordIndex = computed<number>(() => {
// TODO: handle children with empty path: they should relate to their parent
const currentMatched: RouteRecord | undefined =
route.value.matched[route.value.matched.length - 1]
if (!currentMatched) return -1
return currentRoute.matched.findIndex(
isSameRouteRecord.bind(null, currentMatched)
let { matched } = route.value
let { length } = matched
const routeMatched: RouteRecord | undefined = matched[length - 1]
let currentMatched = currentRoute.matched
if (!routeMatched || !currentMatched.length) return -1
let index = currentMatched.findIndex(
isSameRouteRecord.bind(null, routeMatched)
)
if (index > -1) return index
// possible parent record
let parentRecord = matched[length - 2]
if (
length > 1 &&
// if the have the same path, this link is referring to the empty child
// are we currently are on a different child of the same parent
routeMatched.path === parentRecord.path &&
// avoid comparing the child with its parent
currentMatched[currentMatched.length - 1].path !== parentRecord.path
)
return currentMatched.findIndex(
isSameRouteRecord.bind(null, matched[length - 2])
)
return index
})

const isActive = computed<boolean>(
Expand Down

0 comments on commit 4b813b1

Please sign in to comment.