Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router-lite): ce aliases as configured route #1723

Merged
merged 1 commit into from
Apr 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions docs/user-docs/router-lite/configuring-routes.md
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,33 @@ You can see this configuration in action below.

{% embed url="https://stackblitz.com/edit/router-lite-component-ce-instance-jx3kee?ctl=1&embed=1&file=src/my-app.ts" %}

## Using classes as routes

Using router-lite it is also possible to use the routed view model classes directly as routes configuration.
The following example demonstrates that the `C1` and `C2` classes are used directly as the child routes for the `Root`.

```typescript
@customElement({ name: 'c-1', template: 'c1', aliases: ['c-a', 'c-one'] })
class C1 { }

@customElement({ name: 'c-2', template: 'c2', aliases: ['c-b', 'c-two'] })
class C2 { }

@route({
routes: [C1, C2]
})
@customElement({ name: 'ro-ot', template: '<au-viewport></au-viewport>' })
class Root { }
```

The example above implies that `router.load('c-1')` and `router.load('c-2')` will load the `C1` and `C2` respectively.

{% hint style="info" %}
To know more about the router API refer [this section](./navigating.md#using-the-router-api).
{% endhint %}

Note that this extends also for alias; that is using `router.load('c-a')` and `router.load('c-two')` will load the `C1` and `C2` respectively.

## Distributed routing configurations

The examples discussed so far demonstrate the classic use-cases of route configurations where the parents define the child routes.
Expand Down
149 changes: 149 additions & 0 deletions packages/__tests__/router-lite/smoke-tests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5472,4 +5472,153 @@ describe('router-lite/smoke-tests.spec.ts', function () {
await au.stop(true);
});
});

describe('CE alias', function () {
it('using the aliases as path works', async function () {
@customElement({ name: 'c-1', template: 'c1', aliases: ['c-a', 'c-one'] })
class C1 { }

@customElement({ name: 'c-2', template: 'c2', aliases: ['c-b', 'c-two'] })
class C2 { }

@route({
routes: [C1, C2]
})
@customElement({ name: 'ro-ot', template: '<au-viewport></au-viewport>' })
class Root { }

const { au, container, host } = await start({ appRoot: Root });
const router = container.get(IRouter);

assert.html.textContent(host, '');

await router.load('c-a');
assert.html.textContent(host, 'c1');

await router.load('c-b');
assert.html.textContent(host, 'c2');

await router.load('c-1');
assert.html.textContent(host, 'c1');

await router.load('c-2');
assert.html.textContent(host, 'c2');

await router.load('c-one');
assert.html.textContent(host, 'c1');

await router.load('c-two');
assert.html.textContent(host, 'c2');

await au.stop();

});

it('order of route decorator and the customElement decorator does not matter', async function () {
@route({ title: 'c1' })
@customElement({ name: 'c-1', template: 'c1', aliases: ['c-a', 'c-one'] })
class C1 { }

@customElement({ name: 'c-2', template: 'c2', aliases: ['c-b', 'c-two'] })
@route({ title: 'c2' })
class C2 { }

@route({
routes: [C1, C2]
})
@customElement({ name: 'ro-ot', template: '<au-viewport></au-viewport>' })
class Root { }

const { au, container, host } = await start({ appRoot: Root });
const router = container.get(IRouter);
const doc = container.get(IPlatform).document;

assert.html.textContent(host, '');

await router.load('c-a');
assert.html.textContent(host, 'c1');
assert.strictEqual(doc.title, 'c1');

await router.load('c-b');
assert.html.textContent(host, 'c2');
assert.strictEqual(doc.title, 'c2');

await router.load('c-1');
assert.html.textContent(host, 'c1');
assert.strictEqual(doc.title, 'c1');

await router.load('c-2');
assert.html.textContent(host, 'c2');
assert.strictEqual(doc.title, 'c2');

await router.load('c-one');
assert.html.textContent(host, 'c1');
assert.strictEqual(doc.title, 'c1');

await router.load('c-two');
assert.html.textContent(host, 'c2');
assert.strictEqual(doc.title, 'c2');

await au.stop();

});

it('explicitly defined paths always override CE name or aliases', async function () {

@route('c1')
@customElement({ name: 'c-1', template: 'c1', aliases: ['c-a'] })
class C1 { }

@customElement({ name: 'c-2', template: 'c2', aliases: ['c-b'] })
class C2 { }

@route({
routes: [C1, { path: 'c2', component: C2 }]
})
@customElement({ name: 'ro-ot', template: '<au-viewport></au-viewport>' })
class Root { }

const { au, container, host } = await start({ appRoot: Root });
const router = container.get(IRouter);

assert.html.textContent(host, '');

await router.load('c1');
assert.html.textContent(host, 'c1');

await router.load('c2');
assert.html.textContent(host, 'c2');

try {
await router.load('c-1');
assert.fail('expected error 1');
} catch (er) {
assert.match((er as Error).message, /'c-1' matched any configured route/);
}

try {
await router.load('c-a');
assert.fail('expected error 2');
} catch (er) {
assert.match((er as Error).message, /'c-a' matched any configured route/);
}

try {
await router.load('c-2');
assert.fail('expected error 3');
} catch (er) {
assert.match((er as Error).message, /'c-2' matched any configured route/);
}

try {
await router.load('c-b');
assert.fail('expected error 4');
} catch (er) {
assert.match((er as Error).message, /'c-b' matched any configured route/);
}

await au.stop();

});
});
});
7 changes: 4 additions & 3 deletions packages/router-lite/src/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ export class RouteConfig implements IRouteConfig, IChildRouteConfig {
public get path(): string[] {
const path = this._path;
if (path.length > 0) return path;
return this._path = [CustomElement.getDefinition(this.component as RouteType).name];
const ceDfn = CustomElement.getDefinition(this.component as RouteType);
return this._path = [ceDfn.name, ...ceDfn.aliases];
}
private constructor(
public readonly id: string,
Expand Down Expand Up @@ -165,7 +166,7 @@ export class RouteConfig implements IRouteConfig, IChildRouteConfig {
const redirectTo = config.redirectTo ?? Type?.redirectTo ?? null;
const caseSensitive = config.caseSensitive ?? Type?.caseSensitive ?? false;
const id = config.id ?? Type?.id ?? (path instanceof Array ? path[0] : path);
const reentryBehavior = config.transitionPlan ?? Type?.transitionPlan ?? /* parentConfig?.transitionPlan ?? */ null;
const reentryBehavior = config.transitionPlan ?? Type?.transitionPlan ?? null;
const viewport = config.viewport ?? Type?.viewport ?? defaultViewportName;
const data = {
...Type?.data,
Expand Down Expand Up @@ -309,7 +310,7 @@ export const Route = {
configOrPath: IRouteConfig | IChildRouteConfig | string | string[],
Type: T,
): T {
const config = RouteConfig._create(configOrPath, Type /* , isDefinedByType , null */);
const config = RouteConfig._create(configOrPath, Type);
Metadata.define(Route.name, config, Type);

return Type;
Expand Down