Skip to content
This repository has been archived by the owner on Dec 6, 2022. It is now read-only.

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdbradley committed Apr 11, 2019
1 parent 122cb52 commit 4af7591
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 150 deletions.
2 changes: 1 addition & 1 deletion packages/state-tunnel/src/declarations.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export type PropList<T> = Extract<keyof T, string>[] | string;
export type SubscribeCallback<T> = (el: any, props: PropList<T>) => () => void;
export type SubscribeCallback<T> = (el: any, props: PropList<T>) => void;
export type ConsumerRenderer<T> = (subscribe: SubscribeCallback<T>, renderer: Function) => any;
126 changes: 0 additions & 126 deletions packages/state-tunnel/src/utils/__test__/state-tunnel.spec.ts

This file was deleted.

118 changes: 118 additions & 0 deletions packages/state-tunnel/src/utils/__test__/state-tunnel.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { Component, Prop } from '@stencil/core';
import { newSpecPage, mockDocument } from '@stencil/core/testing';
import { createProviderConsumer } from '../state-tunnel';

describe('createProviderConsumer', () => {
let Tunnel: any;
let createTunnelCallback: any;
let subscribe: any;
let elm: any;
let doc = mockDocument();

interface State {
messageLog: string[],
creatingMessage: boolean,
setCreatingMessage: () => void
}

beforeEach(() => {
elm = doc.createElement('stencil-element');

createTunnelCallback = jest.fn((subscribeFn) => {
subscribe = subscribeFn;
});
Tunnel = createProviderConsumer<State>({
messageLog: [],
creatingMessage: false,
setCreatingMessage: () => {}
}, createTunnelCallback);
});

it('should have a provider and consumer', () => {
expect(Tunnel.Provider).toBeDefined();
expect(Tunnel.Consumer).toBeDefined();
});

it('should call Tunnel consumerRender when Consumer created', () => {
const props = {};
const childAsFunction = jest.fn();
Tunnel.Consumer(props, [childAsFunction]);
subscribe(elm, 'all');

expect(createTunnelCallback.mock.calls.length).toBe(1);
expect(createTunnelCallback.mock.calls[0][1]).toBe(childAsFunction);
});

it('should set element prop as copy of state when only a string is passed', () => {
Tunnel.Consumer({}, [jest.fn()]);
subscribe(elm, 'all');

const newState = {
messageLog: ['a'],
creatingMessage: false,
setCreatingMessage: () => {}
}

Tunnel.Provider({ state: newState });
expect(elm.all).toEqual(newState);
});

it('should set element props array of strings', () => {
Tunnel.Consumer({}, [jest.fn()]);
subscribe(elm, ['messageLog', 'creatingMessage']);

const newState = {
messageLog: ['a'],
creatingMessage: false,
setCreatingMessage: () => {}
}

Tunnel.Provider({ state: newState });
expect(elm.messageLog).toEqual(['a']);
expect(elm.creatingMessage).toEqual(false);
expect(elm.setCreatingMessage).toBeUndefined();
});

fit('should set element props array of strings', async () => {
@Component({ tag: 'cmp-a' })
class CmpA {
@Prop() messageLog: string[];
@Prop() creatingMessage: boolean;
}

Tunnel.injectProps(CmpA, ['messageLog', 'creatingMessage']);

const { root } = await newSpecPage({
components: [CmpA],
html: `<cmp-a></cmp-a>`
});

expect(root.messageLog).toEqual([]);
expect(root.creatingMessage).toEqual(false);

let newState = {
messageLog: ['a'],
creatingMessage: true,
setCreatingMessage: () => {}
}

Tunnel.Provider({ state: newState });

expect(root.messageLog).toEqual(['a']);
expect(root.creatingMessage).toEqual(true);
expect(root.setCreatingMessage).toBeUndefined();

newState = {
messageLog: ['b'],
creatingMessage: false,
setCreatingMessage: () => {}
}

Tunnel.Provider({ state: newState });

expect(root.messageLog).toEqual(['b']);
expect(root.creatingMessage).toEqual(false);
expect(root.setCreatingMessage).toBeUndefined();
});

});
40 changes: 18 additions & 22 deletions packages/state-tunnel/src/utils/state-tunnel.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ComponentInterface, FunctionalComponent } from '@stencil/core';
import { SubscribeCallback, ConsumerRenderer, PropList } from '../declarations';


export const createProviderConsumer = <T extends {[key: string]: any}>(defaultState: T, consumerRender: ConsumerRenderer<T>) => {

let listeners: Map<any, PropList<T>> = new Map();
Expand All @@ -20,14 +21,10 @@ export const createProviderConsumer = <T extends {[key: string]: any}>(defaultSt
}

const subscribe: SubscribeCallback<T> = (instance: ComponentInterface, propList: PropList<T>) => {
if (listeners.has(instance)) {
return noop;
if (!listeners.has(instance)) {
listeners.set(instance, propList);
updateListener(propList, instance);
}

listeners.set(instance, propList);
updateListener(propList, instance);

return () => listeners.delete(instance);
}

const Provider: FunctionalComponent<{state: T}> = ({ state }, children) => {
Expand All @@ -42,24 +39,25 @@ export const createProviderConsumer = <T extends {[key: string]: any}>(defaultSt
return consumerRender(subscribe, children[0] as any);
}

const injectProps = (childComponent: any, fieldList: PropList<T>) => {
let unsubscribe: any = null;
const injectProps = (Cstr: any, fieldList: PropList<T>) => {
const CstrPrototype = Cstr.prototype;
const cstrComponentWillLoad = CstrPrototype.componentWillLoad;
const cstrComponentDidUnload = CstrPrototype.componentDidUnload;

const prevComponentWillLoad = (childComponent.prototype as ComponentInterface).componentWillLoad;
(childComponent.prototype as ComponentInterface).componentWillLoad = function() {
unsubscribe = subscribe(this, fieldList);
if (prevComponentWillLoad) {
return prevComponentWillLoad.bind(this)();
CstrPrototype.componentWillLoad = function() {
subscribe(this, fieldList);

if (cstrComponentWillLoad) {
return cstrComponentWillLoad.call(this);
}
}

const prevComponentDidUnload = childComponent.prototype.componentDidUnload;
childComponent.prototype.componentDidUnload = function() {
unsubscribe();
if (prevComponentDidUnload) {
return prevComponentDidUnload.bind(this)();
CstrPrototype.componentDidUnload = function() {
listeners.delete(this);
if (cstrComponentDidUnload) {
cstrComponentDidUnload.call(this);
}
}
};
}

return {
Expand All @@ -68,5 +66,3 @@ export const createProviderConsumer = <T extends {[key: string]: any}>(defaultSt
injectProps
}
}

const noop = () => {};
1 change: 0 additions & 1 deletion packages/state-tunnel/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"allowUnreachableCode": false,
"strict": true,
"declaration": false,
"experimentalDecorators": true,
"lib": [
Expand Down

0 comments on commit 4af7591

Please sign in to comment.