Skip to content

Commit

Permalink
feat: enable module support
Browse files Browse the repository at this point in the history
  • Loading branch information
ntilwalli committed Sep 22, 2020
1 parent 3d4f87f commit 99e0242
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 12 deletions.
8 changes: 8 additions & 0 deletions cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"baseUrl": "http://localhost:1234",
"chromeWebSecurity": false,
"defaultCommandTimeout": 10000,
"modifyObstructiveCode": false,
"video": false,
"fixturesFolder": false
}
32 changes: 32 additions & 0 deletions cypress/integration/test.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/// <reference types="cypress" />

const { watchFile } = require("fs")

context('Page load', () => {
beforeEach(() => {
cy.visit('/')
cy.wait(1000)
})
describe('React integration', () => {

it('Should mount', () => {
cy.get('#app')
.should('exist', 'success')
})
it('Should have foo property on button', () => {
cy.get('.clicker')
// .its('foo')
// .should('eq', 3)
.then(($el) => {
const el = $el[0]
cy.wrap(el.foo).should('eq', 3)
})
})
it('Should allow toggling className items based on domClass prop', () => {
cy.get('.clicker')
.then(($el) => {
cy.wrap($el[0].className).should('eq', 'clicker hello')
})
})
})
})
25 changes: 24 additions & 1 deletion example/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import xs from 'xstream';
import {createElement} from 'react';
import {render} from 'react-dom';
import {setModules} from '../src/Incorporator'
import {h, makeComponent} from '../src/index';

function main(sources) {
Expand All @@ -22,7 +23,12 @@ function main(sources) {
const vdom$ = count$.map(i =>
h('div', [
h('h1', `Hello ${i} times`),
h('button', {sel: btnSel}, 'Reset'),
h('button', {
sel: btnSel,
className: 'clicker',
domProps: {foo: 3},
domClass: {hello: true, goodbye: false}
}, 'Reset'),
]),
);

Expand All @@ -33,4 +39,21 @@ function main(sources) {

const App = makeComponent(main);

setModules({
domProps: {
componentDidUpdate: (element, props) => {
Object.entries(props).forEach(([key, val]) => {
element[key] = val;
});
}
},
domClass: {
componentDidUpdate: (element, props) => {
Object.entries(props).forEach(([key, val]) => {
val ? element.classList.add(key) : element.classList.remove(key);
});
}
}
})

render(createElement(App), document.getElementById('app'));
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@
"@types/mocha": "^5.2.7",
"@types/node": "^10.5.2",
"@types/react": "16.9.3",
"cypress": "^5.2.0",
"mocha": "^6.2.0",
"parcel": "^1.12.3",
"react": "16.9.0",
"react-dom": "16.9.0",
"react-test-renderer": "16.9.0",
"start-server-and-test": "^1.11.3",
"symbol-observable": "^1.2.0",
"ts-node": "^7.0.0",
"typescript": "3.6.3",
Expand All @@ -46,6 +49,11 @@
"compile": "npm run compile-cjs && npm run compile-es6",
"compile-cjs": "tsc --module commonjs --outDir ./lib/cjs",
"compile-es6": "echo 'TODO' : tsc --module es6 --outDir ./lib/es6",
"test": "$(npm bin)/mocha test/*.ts --require ts-node/register --recursive"
"full-test": "npm test; npm run cypress:run",
"test": "$(npm bin)/mocha test/*.ts --require ts-node/register --recursive",
"serve-test": "start-server-and-test start http://localhost:1234 full-test",
"start": "parcel example/index.html",
"cypress:open": "cypress open",
"cypress:run": "cypress run"
}
}
58 changes: 50 additions & 8 deletions src/Incorporator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {PureComponent, createElement} from 'react';
import {PureComponent, createElement, createRef} from 'react';
import {Scope} from './scope';

type Props = {
Expand All @@ -12,20 +12,58 @@ type State = {
flip: boolean;
};

let moduleEntries: any = []

let onMounts: any[] = []
let onUpdates: any[] = []
let onUnmounts: any[] = []

export function setModules(mods: any) {
if (mods === null || typeof mods !== 'object') return;
moduleEntries = Object.entries(mods)
onMounts = moduleEntries.map(mod => [mod[0], mod[1].componentDidMount]).filter(mod => mod[1])
onUpdates = moduleEntries.map(mod => [mod[0], mod[1].componentDidUpdate]).filter(mod => mod[1])
onUnmounts = moduleEntries.map(mod => [mod[0], mod[1].componentWillUnmount]).filter(mod => mod[1])
}

export function hasModuleProps (props) {
return props
? moduleEntries.some(([mkey]) => props.hasOwnProperty(mkey))
: false
}

function moduleProcessor (base, ref, props) {
if (ref && ref.current && base.length) {
base.forEach(([key, f]) => {
const prop = props[key]
if (prop) f(ref.current, prop)
});
}
}

export default class Incorporator extends PureComponent<Props, State> {
private ref: any;
private selector: string | symbol;
private unsubscribe: any;

constructor(props: Props) {
super(props);

this.state = {flip: false};
this.selector = props.targetProps.sel;
this.ref = props.targetRef || (hasModuleProps(props.targetProps) ? createRef() : null);
}

private selector: string | symbol;
private unsubscribe: any;

public componentDidMount() {
this.unsubscribe = this.props.scope.subscribe(this.selector, () => {
this.setState((prev: any) => ({flip: !prev.flip}));
});

moduleProcessor(onMounts, this.ref, this.props.targetProps)
}

public componentDidUpdate() {
moduleProcessor(onUpdates, this.ref, this.props.targetProps)
}

private incorporateHandlers<P>(props: P, scope: Scope): P {
Expand All @@ -38,27 +76,31 @@ export default class Incorporator extends PureComponent<Props, State> {
}

private materializeTargetProps() {
const {targetProps, targetRef, scope} = this.props;
const {targetProps, scope} = this.props;
let output = {...targetProps};
output = this.incorporateHandlers(output, scope);
if (targetRef) {
output.ref = targetRef;
if (this.ref) {
output.ref = this.ref;
}
delete output.sel;
moduleEntries.forEach(pair => delete output[pair[0]])
return output;
}

public render() {
const {target} = this.props;
const targetProps = this.materializeTargetProps();

if (targetProps.children) {
return createElement(target, targetProps, targetProps.children);
} else {
return createElement(target, targetProps);
}
}

public componentWillUnmount() {
public componentWillUnmount() {
moduleProcessor(onUnmounts, this.ref, this.props.targetProps)

this.unsubscribe();
}
}
5 changes: 3 additions & 2 deletions src/h.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Attributes,
} from 'react';
import {incorporate} from './incorporate';
import { hasModuleProps } from './Incorporator';

export type PropsExtensions = {
sel?: string | symbol;
Expand All @@ -32,7 +33,7 @@ function hyperscriptProps<P = any>(
type: ReactType<P> | keyof ReactHTML,
props: PropsLike<P>,
): ReactElement<P> {
if (!props.sel) {
if (!props.sel && !hasModuleProps(props)) {
return createElement(type, props);
} else {
return createElement(incorporate(type), props);
Expand All @@ -51,7 +52,7 @@ function hyperscriptPropsChildren<P = any>(
props: PropsLike<P>,
children: Children,
): ReactElement<P> {
if (!props.sel) {
if (!props.sel && !hasModuleProps(props)) {
return createElementSpreading(type, props, children);
} else {
return createElementSpreading(incorporate(type), props, children);
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export {Scope} from './scope';
export {ReactSource} from './ReactSource';
export {h} from './h';
export {incorporate} from './incorporate';
export {setModules} from './Incorporator'
export {StreamRenderer} from './StreamRenderer';

0 comments on commit 99e0242

Please sign in to comment.