Skip to content

Commit 3b05614

Browse files
committed
feat: enable module support
1 parent 3d4f87f commit 3b05614

File tree

7 files changed

+124
-8
lines changed

7 files changed

+124
-8
lines changed

cypress.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"baseUrl": "http://localhost:1234",
3+
"chromeWebSecurity": false,
4+
"defaultCommandTimeout": 10000,
5+
"modifyObstructiveCode": false,
6+
"video": false,
7+
"fixturesFolder": false
8+
}

cypress/integration/test.spec.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// <reference types="cypress" />
2+
3+
const { watchFile } = require("fs")
4+
5+
context('Page load', () => {
6+
beforeEach(() => {
7+
cy.visit('/')
8+
cy.wait(1000)
9+
})
10+
describe('React integration', () => {
11+
12+
it('Should mount', () => {
13+
cy.get('#app')
14+
.should('exist', 'success')
15+
})
16+
it('Should have foo property on button', () => {
17+
cy.get('.clicker')
18+
// .its('foo')
19+
// .should('eq', 3)
20+
.then(($el) => {
21+
const el = $el[0]
22+
cy.wrap(el.foo).should('eq', 3)
23+
})
24+
})
25+
it('Should allow toggling className items based on domClass prop', () => {
26+
cy.get('.clicker')
27+
.then(($el) => {
28+
cy.wrap($el[0].className).should('eq', 'clicker hello')
29+
})
30+
})
31+
})
32+
})

example/index.js

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import xs from 'xstream';
22
import {createElement} from 'react';
33
import {render} from 'react-dom';
4+
import {setModules} from '../src/Incorporator'
45
import {h, makeComponent} from '../src/index';
56

67
function main(sources) {
@@ -22,7 +23,12 @@ function main(sources) {
2223
const vdom$ = count$.map(i =>
2324
h('div', [
2425
h('h1', `Hello ${i} times`),
25-
h('button', {sel: btnSel}, 'Reset'),
26+
h('button', {
27+
sel: btnSel,
28+
className: 'clicker',
29+
domProps: {foo: 3},
30+
domClass: {hello: true, goodbye: false}
31+
}, 'Reset'),
2632
]),
2733
);
2834

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

3440
const App = makeComponent(main);
3541

42+
setModules({
43+
domProps: {
44+
componentDidUpdate: (element, props) => {
45+
Object.entries(props).forEach(([key, val]) => {
46+
element[key] = val;
47+
});
48+
}
49+
},
50+
domClass: {
51+
componentDidUpdate: (element, props) => {
52+
Object.entries(props).forEach(([key, val]) => {
53+
val ? element.classList.add(key) : element.classList.remove(key);
54+
});
55+
}
56+
}
57+
})
58+
3659
render(createElement(App), document.getElementById('app'));

package.json

+9-1
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@
2929
"@types/mocha": "^5.2.7",
3030
"@types/node": "^10.5.2",
3131
"@types/react": "16.9.3",
32+
"cypress": "^5.2.0",
3233
"mocha": "^6.2.0",
34+
"parcel": "^1.12.3",
3335
"react": "16.9.0",
3436
"react-dom": "16.9.0",
3537
"react-test-renderer": "16.9.0",
38+
"start-server-and-test": "^1.11.3",
3639
"symbol-observable": "^1.2.0",
3740
"ts-node": "^7.0.0",
3841
"typescript": "3.6.3",
@@ -46,6 +49,11 @@
4649
"compile": "npm run compile-cjs && npm run compile-es6",
4750
"compile-cjs": "tsc --module commonjs --outDir ./lib/cjs",
4851
"compile-es6": "echo 'TODO' : tsc --module es6 --outDir ./lib/es6",
49-
"test": "$(npm bin)/mocha test/*.ts --require ts-node/register --recursive"
52+
"full-test": "npm test; npm run cypress:run",
53+
"test": "$(npm bin)/mocha test/*.ts --require ts-node/register --recursive",
54+
"serve-test": "start-server-and-test start http://localhost:1234 full-test",
55+
"start": "parcel example/index.html",
56+
"cypress:open": "cypress open",
57+
"cypress:run": "cypress run"
5058
}
5159
}

src/Incorporator.ts

+47-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {PureComponent, createElement} from 'react';
1+
import {PureComponent, createElement, createRef} from 'react';
22
import {Scope} from './scope';
33

44
type Props = {
@@ -12,11 +12,44 @@ type State = {
1212
flip: boolean;
1313
};
1414

15+
let moduleEntries: any = []
16+
17+
let onMounts: any[] = []
18+
let onUpdates: any[] = []
19+
let onUnmounts: any[] = []
20+
21+
export function setModules(mods: any) {
22+
if (mods === null || typeof mods !== 'object') return;
23+
moduleEntries = Object.entries(mods)
24+
onMounts = moduleEntries.map(mod => [mod[0], mod[1].componentDidMount]).filter(mod => mod[1])
25+
onUpdates = moduleEntries.map(mod => [mod[0], mod[1].componentDidUpdate]).filter(mod => mod[1])
26+
onUnmounts = moduleEntries.map(mod => [mod[0], mod[1].componentWillUnmount]).filter(mod => mod[1])
27+
}
28+
29+
export function hasModuleProps (props) {
30+
return props
31+
? moduleEntries.some(([mkey]) => props.hasOwnProperty(mkey))
32+
: false
33+
}
34+
35+
function moduleProcessor (base, ref, props) {
36+
if (ref && ref.current && base.length) {
37+
base.forEach(([key, f]) => {
38+
f(ref.current, props[key])
39+
});
40+
}
41+
42+
}
43+
1544
export default class Incorporator extends PureComponent<Props, State> {
45+
private ref: any;
46+
private moduleProps: string;
47+
1648
constructor(props: Props) {
1749
super(props);
1850
this.state = {flip: false};
1951
this.selector = props.targetProps.sel;
52+
this.ref = props.targetRef || (moduleEntries.some(e => Object.keys(props.targetProps).some(key => key === e[0])) ? createRef() : null);
2053
}
2154

2255
private selector: string | symbol;
@@ -26,6 +59,12 @@ export default class Incorporator extends PureComponent<Props, State> {
2659
this.unsubscribe = this.props.scope.subscribe(this.selector, () => {
2760
this.setState((prev: any) => ({flip: !prev.flip}));
2861
});
62+
63+
moduleProcessor(onMounts, this.ref, this.props.targetProps)
64+
}
65+
66+
public componentDidUpdate() {
67+
moduleProcessor(onUpdates, this.ref, this.props.targetProps)
2968
}
3069

3170
private incorporateHandlers<P>(props: P, scope: Scope): P {
@@ -38,19 +77,21 @@ export default class Incorporator extends PureComponent<Props, State> {
3877
}
3978

4079
private materializeTargetProps() {
41-
const {targetProps, targetRef, scope} = this.props;
80+
const {targetProps, scope} = this.props;
4281
let output = {...targetProps};
4382
output = this.incorporateHandlers(output, scope);
44-
if (targetRef) {
45-
output.ref = targetRef;
83+
if (this.ref) {
84+
output.ref = this.ref;
4685
}
4786
delete output.sel;
87+
moduleEntries.forEach(pair => delete output[pair[0]])
4888
return output;
4989
}
5090

5191
public render() {
5292
const {target} = this.props;
5393
const targetProps = this.materializeTargetProps();
94+
5495
if (targetProps.children) {
5596
return createElement(target, targetProps, targetProps.children);
5697
} else {
@@ -59,6 +100,8 @@ export default class Incorporator extends PureComponent<Props, State> {
59100
}
60101

61102
public componentWillUnmount() {
103+
moduleProcessor(onUnmounts, this.ref, this.props.targetProps)
104+
62105
this.unsubscribe();
63106
}
64107
}

src/h.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
Attributes,
88
} from 'react';
99
import {incorporate} from './incorporate';
10+
import { hasModuleProps } from './Incorporator';
1011

1112
export type PropsExtensions = {
1213
sel?: string | symbol;
@@ -32,7 +33,7 @@ function hyperscriptProps<P = any>(
3233
type: ReactType<P> | keyof ReactHTML,
3334
props: PropsLike<P>,
3435
): ReactElement<P> {
35-
if (!props.sel) {
36+
if (!props.sel && !hasModuleProps(props)) {
3637
return createElement(type, props);
3738
} else {
3839
return createElement(incorporate(type), props);
@@ -51,7 +52,7 @@ function hyperscriptPropsChildren<P = any>(
5152
props: PropsLike<P>,
5253
children: Children,
5354
): ReactElement<P> {
54-
if (!props.sel) {
55+
if (!props.sel && !hasModuleProps(props)) {
5556
return createElementSpreading(type, props, children);
5657
} else {
5758
return createElementSpreading(incorporate(type), props, children);

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ export {Scope} from './scope';
44
export {ReactSource} from './ReactSource';
55
export {h} from './h';
66
export {incorporate} from './incorporate';
7+
export {setModules} from './Incorporator'
78
export {StreamRenderer} from './StreamRenderer';

0 commit comments

Comments
 (0)