Skip to content

Commit

Permalink
fix(router-lite): support ../ prefix for load CA (#1635)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sayan751 committed Jan 7, 2023
1 parent 03b86bf commit bcf8afd
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 123 deletions.
218 changes: 110 additions & 108 deletions packages/__tests__/router-lite/smoke-tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1137,148 +1137,150 @@ describe('router (smoke tests)', function () {
assert.areTaskQueuesEmpty();
});

it('will load the root-level fallback when navigating to a non-existing route - parent-child - children without fallback', async function () {
@customElement({ name: 'gc-11', template: 'gc11' })
class GrandChildOneOne { }
for (const attr of ['href', 'load']) {
it(`will load the root-level fallback when navigating to a non-existing route - parent-child - children without fallback - attr: ${attr}`, async function () {
@customElement({ name: 'gc-11', template: 'gc11' })
class GrandChildOneOne { }

@customElement({ name: 'gc-12', template: 'gc12' })
class GrandChildOneTwo { }
@customElement({ name: 'gc-12', template: 'gc12' })
class GrandChildOneTwo { }

@route({
routes: [
{ id: 'gc11', path: ['', 'gc11'], component: GrandChildOneOne },
{ id: 'gc12', path: 'gc12', component: GrandChildOneTwo },
],
})
@customElement({
name: 'c-one',
template: `c1 <br>
@route({
routes: [
{ id: 'gc11', path: ['', 'gc11'], component: GrandChildOneOne },
{ id: 'gc12', path: 'gc12', component: GrandChildOneTwo },
],
})
@customElement({
name: 'c-one',
template: `c1 <br>
<nav>
<a href="gc11">gc11</a>
<a href="gc12">gc12</a>
<a href="c2">c2 (doesn't work)</a>
<a href="../c2">../c2 (works)</a>
<a ${attr}="gc11">gc11</a>
<a ${attr}="gc12">gc12</a>
<a ${attr}="c2">c2 (doesn't work)</a>
<a ${attr}="../c2">../c2 (works)</a>
</nav>
<br>
<au-viewport></au-viewport>`,
})
class ChildOne { }
})
class ChildOne { }

@customElement({ name: 'gc-21', template: 'gc21' })
class GrandChildTwoOne { }
@customElement({ name: 'gc-21', template: 'gc21' })
class GrandChildTwoOne { }

@customElement({ name: 'gc-22', template: 'gc22' })
class GrandChildTwoTwo { }
@customElement({ name: 'gc-22', template: 'gc22' })
class GrandChildTwoTwo { }

@route({
routes: [
{ id: 'gc21', path: ['', 'gc21'], component: GrandChildTwoOne },
{ id: 'gc22', path: 'gc22', component: GrandChildTwoTwo },
],
})
@customElement({
name: 'c-two',
template: `c2 <br>
@route({
routes: [
{ id: 'gc21', path: ['', 'gc21'], component: GrandChildTwoOne },
{ id: 'gc22', path: 'gc22', component: GrandChildTwoTwo },
],
})
@customElement({
name: 'c-two',
template: `c2 <br>
<nav>
<a href="gc21">gc21</a>
<a href="gc22">gc22</a>
<a href="c1">c1 (doesn't work)</a>
<a href="../c1">../c1 (works)</a>
<a ${attr}="gc21">gc21</a>
<a ${attr}="gc22">gc22</a>
<a ${attr}="c1">c1 (doesn't work)</a>
<a ${attr}="../c1">../c1 (works)</a>
</nav>
<br>
<au-viewport></au-viewport>`,
})
class ChildTwo { }
})
class ChildTwo { }

@customElement({
name: 'not-found',
template: 'nf',
})
class NotFound { }
@customElement({
name: 'not-found',
template: 'nf',
})
class NotFound { }

@route({
routes: [
{
path: ['', 'c1'],
component: ChildOne,
},
{
path: 'c2',
component: ChildTwo,
},
{
path: 'not-found',
component: NotFound,
},
],
fallback: 'not-found',
})
@customElement({
name: 'my-app',
template: `<nav>
<a load="c1">C1</a>
<a load="c2">C2</a>
@route({
routes: [
{
path: ['', 'c1'],
component: ChildOne,
},
{
path: 'c2',
component: ChildTwo,
},
{
path: 'not-found',
component: NotFound,
},
],
fallback: 'not-found',
})
@customElement({
name: 'my-app',
template: `<nav>
<a ${attr}="c1">C1</a>
<a ${attr}="c2">C2</a>
</nav>
<au-viewport></au-viewport>` })
class Root { }
class Root { }

const { au, container, host } = await start({
appRoot: Root,
registrations: [
NotFound,
]
});
const { au, container, host } = await start({
appRoot: Root,
registrations: [
NotFound,
]
});

const queue = container.get(IPlatform).domWriteQueue;
const queue = container.get(IPlatform).domWriteQueue;

const rootVp = host.querySelector('au-viewport');
let childVp = rootVp.querySelector('au-viewport');
assert.html.textContent(childVp, 'gc11');
const rootVp = host.querySelector('au-viewport');
let childVp = rootVp.querySelector('au-viewport');
assert.html.textContent(childVp, 'gc11');

let [, a2, nf, f] = Array.from(rootVp.querySelectorAll('a'));
a2.click();
queue.flush();
await queue.yield();
let [, a2, nf, f] = Array.from(rootVp.querySelectorAll('a'));
a2.click();
queue.flush();
await queue.yield();

assert.html.textContent(childVp, 'gc12');
assert.html.textContent(childVp, 'gc12');

nf.click();
queue.flush();
await queue.yield();
nf.click();
queue.flush();
await queue.yield();

assert.html.textContent(childVp, 'nf');
assert.html.textContent(childVp, 'nf');

f.click();
queue.flush();
await queue.yield();
f.click();
queue.flush();
await queue.yield();

childVp = rootVp.querySelector('au-viewport');
assert.html.textContent(childVp, 'gc21', host.textContent);
childVp = rootVp.querySelector('au-viewport');
assert.html.textContent(childVp, 'gc21', host.textContent);

[, a2, nf, f] = Array.from(rootVp.querySelectorAll('a'));
a2.click();
queue.flush();
await queue.yield();
[, a2, nf, f] = Array.from(rootVp.querySelectorAll('a'));
a2.click();
queue.flush();
await queue.yield();

assert.html.textContent(childVp, 'gc22');
assert.html.textContent(childVp, 'gc22');

nf.click();
queue.flush();
await queue.yield();
nf.click();
queue.flush();
await queue.yield();

assert.html.textContent(childVp, 'nf');
assert.html.textContent(childVp, 'nf');

f.click();
queue.flush();
await queue.yield();
f.click();
queue.flush();
await queue.yield();

childVp = rootVp.querySelector('au-viewport');
assert.html.textContent(childVp, 'gc11');
childVp = rootVp.querySelector('au-viewport');
assert.html.textContent(childVp, 'gc11');

await au.stop(true);
assert.areTaskQueuesEmpty();
});
await au.stop(true);
assert.areTaskQueuesEmpty();
});
}

it(`correctly parses parameters`, async function () {
const a1Params: Params[] = [];
Expand Down
10 changes: 2 additions & 8 deletions packages/router-lite/src/resources/href.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,11 @@ export class HrefCustomAttribute implements ICustomAttributeViewModel {
}

// Use the normalized attribute instead of this.value to ensure consistency.
let href = this.el.getAttribute('href');
const href = this.el.getAttribute('href');
if (href !== null) {
e.preventDefault();
// align the context with href
let context = this.ctx;
while (href.startsWith('../') && context.parent !== null) {
href = href.slice(3);
context = context.parent;
}
// Floating promises from `Router#load` are ok because the router keeps track of state and handles the errors, etc.
void this.router.load(href, { context });
void this.router.load(href, { context: this.ctx });
}
}
}
23 changes: 16 additions & 7 deletions packages/router-lite/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,10 +565,23 @@ export class Router {

public createViewportInstructions(instructionOrInstructions: NavigationInstruction | readonly NavigationInstruction[], options?: INavigationOptions): ViewportInstructionTree {
if (instructionOrInstructions instanceof ViewportInstructionTree) return instructionOrInstructions;

let context: IRouteContext | null = (options?.context ?? null) as IRouteContext | null;
if (typeof instructionOrInstructions === 'string') {
instructionOrInstructions = this.locationMgr.removeBaseHref(instructionOrInstructions);
if ((instructionOrInstructions as string).startsWith('../') && context !== null) {
context = this.resolveContext(context);
while ((instructionOrInstructions as string).startsWith('../') && (context?.parent ?? null) !== null) {
instructionOrInstructions = (instructionOrInstructions as string).slice(3);
context = context!.parent;
}
}
}
return ViewportInstructionTree.create(instructionOrInstructions, this.getNavigationOptions(options), this.ctx);
return ViewportInstructionTree.create(
instructionOrInstructions,
NavigationOptions.create({ ...this.options, ...options, context }),
this.ctx
);
}

/**
Expand Down Expand Up @@ -648,7 +661,7 @@ export class Router {
return ret;
}).catch(err => {
logger.error(`Transition %s failed: %s`, nextTr, err);
if(nextTr.erredWithUnknownRoute) {
if (nextTr.erredWithUnknownRoute) {
this.cancelNavigation(nextTr);
} else {
this._isNavigating = false;
Expand Down Expand Up @@ -822,7 +835,7 @@ export class Router {

public updateTitle(tr: Transition = this.currentTr): string {
let title: string;
if(this._hasTitleBuilder) {
if (this._hasTitleBuilder) {
title = this.options.buildTitle!(tr) ?? '';
} else {
switch (typeof tr.options.title) {
Expand Down Expand Up @@ -890,10 +903,6 @@ export class Router {
},
);
}

private getNavigationOptions(options?: INavigationOptions): NavigationOptions {
return NavigationOptions.create({ ...this.options, ...options });
}
}

function updateNode(
Expand Down

0 comments on commit bcf8afd

Please sign in to comment.