Skip to content

Commit

Permalink
refactor(router): rewrite package to use decorators (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lodin committed Jul 27, 2018
1 parent 17ade2d commit ce1d2dd
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 73 deletions.
31 changes: 18 additions & 13 deletions packages/router/__tests__/outlet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,63 +33,68 @@ const outletTest = () => {
});

it("should create a router outlet that contains initial layout", async () => {
class Provider extends provider(BasicProvider) {
@provider
class Provider extends BasicProvider {
public static is: string = `x-${uuid()}`;

protected readonly [$router]: UniversalRouter = router;
}

class Test extends outlet(routes)(BasicConsumer) {
@outlet(routes)
class Test extends BasicConsumer {
public static is: string = `x-${uuid()}`;

public readonly [layout]: string;
}

const [, o] = defineAndMountContext(Provider, Test);

await o.resolvingPromise;
await (o as any).resolvingPromise;

expect(o[layout]).toBe("Test Root");
});

it("should get new layout on 'popstate' event", async () => {
class Provider extends provider(BasicProvider) {
@provider
class Provider extends BasicProvider {
public static is: string = `x-${uuid()}`;

protected readonly [$router]: UniversalRouter = router;
}

class BasicTest extends BasicConsumer {
@outlet(routes)
class Test extends BasicConsumer {
public static is: string = `x-${uuid()}`;

public readonly [layout]: string;
}

const Test = outlet(routes)(BasicTest); // tslint:disable-line:naming-convention

const [, o] = defineAndMountContext(Provider, Test);

dispatchEvent(new PopStateEvent("popstate", {state: `${basicLocation}#test`}));

await o.resolvingPromise;
await (o as any).resolvingPromise;

expect(o[layout]).toBe("Test Branch");
});

it("should ignore layouts for another routes", async () => {
class Provider extends provider(BasicProvider) {
@provider
class Provider extends BasicProvider {
public static is: string = `x-${uuid()}`;

protected readonly [$router]: UniversalRouter = router;
}

class Test extends outlet(routes)(BasicConsumer) {
@outlet(routes)
class Test extends BasicConsumer {
public static is: string = `x-${uuid()}`;

public readonly [layout]: string;
}

class Child extends outlet(childrenRoutes)(BasicConsumer) {
@outlet(childrenRoutes)
class Child extends BasicConsumer {
public static is: string = `x-${uuid()}`;

public readonly [layout]: string;
Expand All @@ -99,13 +104,13 @@ const outletTest = () => {

dispatchEvent(new PopStateEvent("popstate", {state: `${basicLocation}#parent`}));

await child.resolvingPromise;
await (child as any).resolvingPromise;

expect(child[layout]).toBe("Child Root");

dispatchEvent(new PopStateEvent("popstate", {state: `${basicLocation}#parent/child`}));

await child.resolvingPromise;
await (child as any).resolvingPromise;

expect(child[layout]).toBe("Child Branch");
expect(test[layout]).toBe("Test Root");
Expand Down
6 changes: 3 additions & 3 deletions packages/router/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@corpuscule/router",
"version": "0.4.1",
"version": "0.5.0",
"description": "Routing utils for Corpuscule",
"main": "lib/index.js",
"module": "lib/index.js",
Expand All @@ -16,8 +16,8 @@
"url": "git+https://github.com/corpusculejs/corpuscule.git"
},
"dependencies": {
"@corpuscule/context": "^0.3.0",
"@corpuscule/typings": "^0.2.0"
"@corpuscule/context": "^0.4.0",
"@corpuscule/typings": "^0.3.0"
},
"peerDependencies": {
"@types/universal-router": "^6.0.0",
Expand Down
4 changes: 1 addition & 3 deletions packages/router/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,4 @@ export interface RouterOutlet<T> {
[resolve](path: string): IterableIterator<any>;
}

export const outlet:
<T = any>(routes: ReadonlyArray<Route>) =>
<U = {}>(target: UncertainCustomElementClass<U>) => CustomElementClass<U & RouterOutlet<T>>;
export const outlet: (routes: ReadonlyArray<Route>) => ClassDecorator;
131 changes: 77 additions & 54 deletions packages/router/src/outlet.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,60 +17,83 @@ export {
router,
};

const outlet = routes => target =>
class Route extends consumer(target) {
get resolvingPromise() {
return this[$$resolving];
}

constructor() {
super();
this[$$updateRoute] = this[$$updateRoute].bind(this);
}

connectedCallback() {
window.addEventListener("popstate", this[$$updateRoute]);

if (super.connectedCallback) {
super.connectedCallback();
}

this[$$updateRoute](location.pathname);
}

disconnectedCallback() {
window.removeEventListener("popstate", this[$$updateRoute]);

if (super.disconnectedCallback) {
super.disconnectedCallback();
}
}

// eslint-disable-next-line class-methods-use-this
*[resolve](path) {
return yield path;
}

[$$updateRoute](pathOrEvent) {
const path = typeof pathOrEvent === "string"
? pathOrEvent
: pathOrEvent.state || "";

const iter = this[resolve](path);

this[$$resolving] = this[context].resolve(iter.next().value)
.then((resolved) => {
if (resolved === undefined) {
return;
}

const [result, {route}] = resolved;

if (routes.includes(route)) {
this[layout] = iter.next(result).value;
}
const outlet = routes => (target) => {
const consumed = consumer(target);

const {
connectedCallback,
disconnectedCallback,
} = consumed.prototype;

Object.defineProperties(target.prototype, {
connectedCallback: {
configurable: true,
value() {
window.addEventListener("popstate", this[$$updateRoute]);

if (connectedCallback) {
connectedCallback.call(this);
}

this[$$updateRoute](location.pathname);
},
},
disconnectedCallback: {
configurable: true,
value() {
window.removeEventListener("popstate", this[$$updateRoute]);

if (disconnectedCallback) {
disconnectedCallback.call(this);
}
},
},
resolvingPromise: {
get() {
return this[$$resolving];
},
},
// eslint-disable-next-line sort-keys
[resolve]: {
configurable: true,
*value(path) {
return yield path;
},
},
// eslint-disable-next-line sort-keys
[$$updateRoute]: {
get() {
const updateRoute = (pathOrEvent) => {
const path = typeof pathOrEvent === "string"
? pathOrEvent
: pathOrEvent.state || "";

const iter = this[resolve](path);

this[$$resolving] = this[context].resolve(iter.next().value)
.then((resolved) => {
if (resolved === undefined) {
return;
}

const [result, {route}] = resolved;

if (routes.includes(route)) {
this[layout] = iter.next(result).value;
}
});
};

Object.defineProperty(this, $$updateRoute, {
value: updateRoute,
});
}
};

return updateRoute;
},
},
});

return target;
};

export default outlet;

0 comments on commit ce1d2dd

Please sign in to comment.