Skip to content

Commit

Permalink
fix(router): enable matching fallback sibling routes (#437)
Browse files Browse the repository at this point in the history
Fixes #284
  • Loading branch information
platosha authored Feb 19, 2020
1 parent 4c13710 commit e3a32e4
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 22 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"bundlesize": [
{
"path": "dist/vaadin-router.min.js",
"maxSize": "7.7 kB"
"maxSize": "8 kB"
}
],
"browserslist": [
Expand Down
28 changes: 19 additions & 9 deletions src/resolver/resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,23 @@ function generateErrorMessage(currentContext) {
return errorMessage;
}

function addRouteToChain(context, match) {
function updateChainForRoute(context, match) {
const {route, path} = match;
function shouldDiscardOldChain(oldChain, route) {
return !route.parent || !oldChain || !oldChain.length || oldChain[oldChain.length - 1].route !== route.parent;
}

if (route && !route.__synthetic) {
const item = {path, route};
if (shouldDiscardOldChain(context.chain, route)) {
context.chain = [item];
if (!context.chain) {
context.chain = [];
} else {
context.chain.push(item);
// Discard old items
if (route.parent) {
let i = context.chain.length;
while (i-- && context.chain[i].route && context.chain[i].route !== route.parent) {
context.chain.pop();
}
}
}
context.chain.push(item);
}
}

Expand Down Expand Up @@ -158,8 +162,14 @@ class Resolver {
return Promise.reject(getNotFoundError(context));
}

addRouteToChain(context, matches.value);
currentContext = Object.assign({}, context, matches.value);
currentContext = Object.assign(
currentContext
? {chain: (currentContext.chain ? currentContext.chain.slice(0) : [])}
: {},
context,
matches.value
);
updateChainForRoute(currentContext, matches.value);

return Promise.resolve(resolve(currentContext)).then(resolution => {
if (resolution !== null && resolution !== undefined && resolution !== notFoundResult) {
Expand Down
51 changes: 39 additions & 12 deletions src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -548,21 +548,48 @@ export class Router extends Resolver {
const redirectsHappened = contextAfterRedirects !== contextBeforeRedirects;
const topOfTheChainContextAfterRedirects =
redirectsHappened ? contextAfterRedirects : topOfTheChainContextBeforeRedirects;
return contextAfterRedirects.next()
.then(nextChildContext => {
if (nextChildContext === null || nextChildContext === notFoundResult) {
const matchedPath = getPathnameForRouter(
getMatchedPath(contextAfterRedirects.chain),
contextAfterRedirects.resolver
);
if (matchedPath !== contextAfterRedirects.pathname) {
throw getNotFoundError(topOfTheChainContextAfterRedirects);

const matchedPath = getPathnameForRouter(
getMatchedPath(contextAfterRedirects.chain),
contextAfterRedirects.resolver
);
const isFound = (matchedPath === contextAfterRedirects.pathname);

// Recursive method to try matching more child and sibling routes
const findNextContextIfAny = (context, parent) => {
let prevResult = undefined;
if (parent === undefined) {
parent = context.route;
} else {
prevResult = null;
}
return context.next(undefined, parent, prevResult).then(nextContext => {
if (nextContext === null || nextContext === notFoundResult) {
// Next context is not found in children, ...
if (isFound) {
// ...but original context is already fully matching - use it
return context;
} else {
// ...and there is no full match yet - step up to check siblings
return findNextContextIfAny(context, context.route.parent);
}
}
return nextChildContext && nextChildContext !== notFoundResult
? this.__fullyResolveChain(topOfTheChainContextAfterRedirects, nextChildContext)
: this.__amendWithOnBeforeCallbacks(contextAfterRedirects);

return nextContext;
});
};

return findNextContextIfAny(contextAfterRedirects).then(nextContext => {
if (nextContext === null || nextContext === notFoundResult) {
throw getNotFoundError(topOfTheChainContextAfterRedirects);
}

return nextContext
&& nextContext !== notFoundResult
&& nextContext !== contextAfterRedirects
? this.__fullyResolveChain(topOfTheChainContextAfterRedirects, nextContext)
: this.__amendWithOnBeforeCallbacks(contextAfterRedirects);
});
});
}

Expand Down
14 changes: 14 additions & 0 deletions test/router/parent-layouts.spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,20 @@
checkOutlet(['x-a', 'x-d']);
});

it('should take next routes as fallback when children do not match', async() => {
router.setRoutes([
{path: '/a', component: 'x-a', children: [
{path: '/b', component: 'x-b'}
]},
{path: '/a/c', component: 'x-fallback'}
], true);

await router.render('/a/c');

verifyActiveRoutes(router, ['/a/c']);
checkOutlet(['x-fallback']);
});

it('redirect property amends previous path', async() => {
router.setRoutes([
{path: '/a', component: 'x-a', children: [
Expand Down

0 comments on commit e3a32e4

Please sign in to comment.