homeAlias: {} as RouteRecordNormalized,
foo: {} as RouteRecordNormalized,
parent: {} as RouteRecordNormalized,
+ childEmpty: {} as RouteRecordNormalized,
child: {} as RouteRecordNormalized,
parentAlias: {} as RouteRecordNormalized,
childAlias: {} 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: '' },
},
},
+ 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: {
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(
)
})
+ 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,
<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>
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>(