Skip to content

Commit 99e0242

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

File tree

7 files changed

+127
-12
lines changed

7 files changed

+127
-12
lines changed

cypress.json

Lines changed: 8 additions & 0 deletions
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

Lines changed: 32 additions & 0 deletions
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

Lines changed: 24 additions & 1 deletion
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

Lines changed: 9 additions & 1 deletion
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

Lines changed: 50 additions & 8 deletions
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,20 +12,58 @@ 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+
const prop = props[key]
39+
if (prop) f(ref.current, prop)
40+
});
41+
}
42+
}
43+
1544
export default class Incorporator extends PureComponent<Props, State> {
45+
private ref: any;
46+
private selector: string | symbol;
47+
private unsubscribe: any;
48+
1649
constructor(props: Props) {
1750
super(props);
51+
1852
this.state = {flip: false};
1953
this.selector = props.targetProps.sel;
54+
this.ref = props.targetRef || (hasModuleProps(props.targetProps) ? createRef() : null);
2055
}
2156

22-
private selector: string | symbol;
23-
private unsubscribe: any;
24-
2557
public componentDidMount() {
2658
this.unsubscribe = this.props.scope.subscribe(this.selector, () => {
2759
this.setState((prev: any) => ({flip: !prev.flip}));
2860
});
61+
62+
moduleProcessor(onMounts, this.ref, this.props.targetProps)
63+
}
64+
65+
public componentDidUpdate() {
66+
moduleProcessor(onUpdates, this.ref, this.props.targetProps)
2967
}
3068

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

4078
private materializeTargetProps() {
41-
const {targetProps, targetRef, scope} = this.props;
79+
const {targetProps, scope} = this.props;
4280
let output = {...targetProps};
4381
output = this.incorporateHandlers(output, scope);
44-
if (targetRef) {
45-
output.ref = targetRef;
82+
if (this.ref) {
83+
output.ref = this.ref;
4684
}
4785
delete output.sel;
86+
moduleEntries.forEach(pair => delete output[pair[0]])
4887
return output;
4988
}
5089

5190
public render() {
5291
const {target} = this.props;
5392
const targetProps = this.materializeTargetProps();
93+
5494
if (targetProps.children) {
5595
return createElement(target, targetProps, targetProps.children);
5696
} else {
5797
return createElement(target, targetProps);
5898
}
5999
}
60100

61-
public componentWillUnmount() {
101+
public componentWillUnmount() {
102+
moduleProcessor(onUnmounts, this.ref, this.props.targetProps)
103+
62104
this.unsubscribe();
63105
}
64106
}

src/h.ts

Lines changed: 3 additions & 2 deletions
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

Lines changed: 1 addition & 0 deletions
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)