fix(router): popping route now accounts for route params (#24315)

This commit is contained in:
Sean Perkins
2021-12-06 17:25:40 -05:00
committed by GitHub
parent 8f188eaae7
commit 5e5054d369
2 changed files with 70 additions and 17 deletions

View File

@ -30,15 +30,24 @@ const CHAIN_3: RouteChain = [
describe('matchesIDs', () => {
it('should match simple set of ids', () => {
const chain: RouteChain = CHAIN_1;
expect(matchesIDs(['2'], chain)).toBe(1);
expect(matchesIDs(['2', '1'], chain)).toBe(2);
expect(matchesIDs(['2', '1', '3'], chain)).toBe(3);
expect(matchesIDs(['2', '1', '3', '4'], chain)).toBe(4);
expect(matchesIDs(['2', '1', '3', '4', '5'], chain)).toBe(4);
expect(matchesIDs([{ id: '2' }], chain)).toBe(1);
expect(matchesIDs([{ id: '2' }, { id: '1' }], chain)).toBe(2);
expect(matchesIDs([{ id: '2' }, { id: '1' }, { id: '3' }], chain)).toBe(3);
expect(matchesIDs([{ id: '2' }, { id: '1' }, { id: '3' }, { id: '4' }], chain)).toBe(4);
expect(matchesIDs([{ id: '2' }, { id: '1' }, { id: '3' }, { id: '4' }, { id: '5' }], chain)).toBe(4);
expect(matchesIDs([], chain)).toBe(0);
expect(matchesIDs(['1'], chain)).toBe(0);
expect(matchesIDs([{ id: '1' }], chain)).toBe(0);
});
it('should match path with params', () => {
const ids = [{ id: 'my-page', params: { s1: 'a', s2: 'b' } }];
expect(matchesIDs(ids, [{ id: 'my-page', path: [''], params: {} }])).toBe(1);
expect(matchesIDs(ids, [{ id: 'my-page', path: [':s1'], params: {} }])).toBe(1);
expect(matchesIDs(ids, [{ id: 'my-page', path: [':s1', ':s2'], params: {} }])).toBe(3);
expect(matchesIDs(ids, [{ id: 'my-page', path: [':s1', ':s2', ':s3'], params: {} }])).toBe(1);
})
});
describe('matchesPath', () => {

View File

@ -32,16 +32,60 @@ export const findRouteRedirect = (path: string[], redirects: RouteRedirect[]) =>
return redirects.find(redirect => matchesRedirect(path, redirect));
};
export const matchesIDs = (ids: string[], chain: RouteChain): number => {
export const matchesIDs = (ids: Pick<RouteID, 'id' | 'params'>[], chain: RouteChain): number => {
const len = Math.min(ids.length, chain.length);
let i = 0;
for (; i < len; i++) {
if (ids[i].toLowerCase() !== chain[i].id) {
let score = 0;
for (let i = 0; i < len; i++) {
const routeId = ids[i];
const routeChain = chain[i];
// Skip results where the route id does not match the chain at the same index
if (routeId.id.toLowerCase() !== routeChain.id) {
break;
}
if (routeId.params) {
const routeIdParams = Object.keys(routeId.params);
/**
* Only compare routes with the chain that have the same number of parameters.
*/
if (routeIdParams.length === routeChain.path.length) {
/**
* Maps the route's params into a path based on the path variable names,
* to compare against the route chain format.
*
* Before:
* ```ts
* {
* params: {
* s1: 'a',
* s2: 'b'
* }
* }
* ```
*
* After:
* ```ts
* [':s1',':s2']
* ```
*/
const pathWithParams = routeIdParams.map(key => `:${key}`);
for (let j = 0; j < pathWithParams.length; j++) {
// Skip results where the path variable is not a match
if (pathWithParams[j].toLowerCase() !== routeChain.path[j]) {
break;
}
// Weight path matches for the same index higher.
score++;
}
}
}
// Weight id matches
score++;
}
return score;
}
return i;
};
export const matchesPath = (inputPath: string[], chain: RouteChain): RouteChain | null => {
const segments = new RouterSegments(inputPath);
@ -97,9 +141,9 @@ export const mergeParams = (a: {[key: string]: any} | undefined, b: {[key: strin
export const routerIDsToChain = (ids: RouteID[], chains: RouteChain[]): RouteChain | null => {
let match: RouteChain | null = null;
let maxMatches = 0;
const plainIDs = ids.map(i => i.id);
for (const chain of chains) {
const score = matchesIDs(plainIDs, chain);
const score = matchesIDs(ids, chain);
if (score > maxMatches) {
match = chain;
maxMatches = score;