From e9e764d468749efb907a1f18c43ed2fc660d9a92 Mon Sep 17 00:00:00 2001 From: Ryota Kameoka Date: Thu, 7 May 2020 20:23:33 +0900 Subject: [PATCH 1/4] Add enzyme to dev dependencies --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index c0dec0c..166588c 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@types/mocha": "^5.2.7", "@types/node": "^10.5.2", "@types/react": "16.9.3", + "enzyme": "^3.11.0", "mocha": "^6.2.0", "react": "16.9.0", "react-dom": "16.9.0", From f9b4a13e28622acccbff809618cf75982d1c54fc Mon Sep 17 00:00:00 2001 From: Ryota Kameoka Date: Thu, 7 May 2020 20:27:30 +0900 Subject: [PATCH 2/4] Add enzyme-adapter-react-16 to dev dependencies --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 166588c..77d9323 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@types/node": "^10.5.2", "@types/react": "16.9.3", "enzyme": "^3.11.0", + "enzyme-adapter-react-16": "^1.15.2", "mocha": "^6.2.0", "react": "16.9.0", "react-dom": "16.9.0", From 853e5753048f1b962caba1c32583405a60b44734 Mon Sep 17 00:00:00 2001 From: Ryota Kameoka Date: Thu, 7 May 2020 21:40:38 +0900 Subject: [PATCH 3/4] Support JSX as a pragma Fixes #8 Implementation of `jsxFactory` is just a copy and paste from @sliptype's pull request (https://github.com/cyclejs/react-dom/pull/3). Test cases have been rewritten in a DOM-free way (using enzyme). --- package.json | 2 +- src/index.ts | 1 + src/jsxFactory.ts | 36 ++++++++++++++++++++ test/jsxFactory.tsx | 83 +++++++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 4 ++- 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 src/jsxFactory.ts create mode 100644 test/jsxFactory.tsx diff --git a/package.json b/package.json index 77d9323..9fadf81 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,6 @@ "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" + "test": "$(npm bin)/mocha test/*.ts test/*.tsx --require ts-node/register --recursive" } } diff --git a/src/index.ts b/src/index.ts index 7c0473e..2e33bf7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,3 +5,4 @@ export {ReactSource} from './ReactSource'; export {h} from './h'; export {incorporate} from './incorporate'; export {StreamRenderer} from './StreamRenderer'; +export {default as jsxFactory} from './jsxFactory'; diff --git a/src/jsxFactory.ts b/src/jsxFactory.ts new file mode 100644 index 0000000..0cd12f1 --- /dev/null +++ b/src/jsxFactory.ts @@ -0,0 +1,36 @@ +import {createElement, ReactElement, ReactType} from 'react'; +import {incorporate} from './incorporate'; +export {Attributes} from 'react'; + +declare global { + namespace JSX { + interface IntrinsicAttributes { + sel?: string | symbol; + } + } + namespace React { + interface ClassAttributes extends Attributes { + sel?: string | symbol; + } + } +} + +type PropsExtensions = { + sel?: string | symbol; +} + +function createIncorporatedElement

( + type: ReactType

, + props: P & PropsExtensions | null, + ...children: Array> +): ReactElement

{ + if (!props || !props.sel) { + return createElement(type, props, ...children); + } else { + return createElement(incorporate(type), props, ...children); + } +} + +export default { + createElement: createIncorporatedElement +} diff --git a/test/jsxFactory.tsx b/test/jsxFactory.tsx new file mode 100644 index 0000000..e248515 --- /dev/null +++ b/test/jsxFactory.tsx @@ -0,0 +1,83 @@ +import * as React from 'react'; +import * as Adapter from 'enzyme-adapter-react-16'; +import * as Enzyme from 'enzyme'; +const assert = require('assert'); +import {jsxFactory, h, ReactSource, makeCycleReactComponent} from '../src'; + +Enzyme.configure({adapter: new Adapter()}); + +const {shallow} = Enzyme; + +describe('jsxFactory', function () { + it('w/ nothing', () => { + const wrapper = shallow(

); + + assert.strictEqual(wrapper.find('h1').length, 1); + }); + + it('w/ text child', () => { + const wrapper = shallow(

heading 1

); + + const h1 = wrapper.find('h1'); + assert.strictEqual(h1.text(), 'heading 1'); + }); + + it('w/ children array', () => { + const wrapper = shallow( +
+

heading 1

+

heading 2

+

heading 3

+
+ ); + + const section = wrapper.find('section'); + assert.strictEqual(section.children().length, 3); + }); + + it('w/ props', () => { + const wrapper = shallow(
); + + assert(wrapper.exists('[data-foo="bar"]')); + }); + + it('w/ props and children', () => { + const wrapper = shallow( +
+

heading 1

+

heading 2

+

heading 3

+
+ ); + + const section = wrapper.first(); + assert.strictEqual(section.length, 1); + assert(section.exists('[data-foo="bar"]')); + assert.deepStrictEqual(section.children().length, 3); + }); + + it('w/ className', () => { + const wrapper = shallow(
); + + assert(wrapper.hasClass('foo')); + }); + + it('renders functional component', () => { + const Test = () =>

Functional

; + const wrapper = shallow(); + + assert.strictEqual(wrapper.first().type(), 'h1'); + }); + + it('renders class component', () => { + class Test extends React.Component { + render() { + return

Class

; + } + } + + const wrapper = shallow(); + + assert.strictEqual(wrapper.first().type(), 'h1'); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index dab5af5..827111e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,9 @@ "rootDir": "src/", "outDir": "lib/cjs/", "skipLibCheck": true, - "lib": ["dom", "es5", "scripthost", "es2015"] + "lib": ["dom", "es5", "scripthost", "es2015"], + "jsx": "react", + "jsxFactory": "jsxFactory.createElement" }, "files": ["src/index.ts"] } From f580debc27460b88f8e488a818a7ace6c9538184 Mon Sep 17 00:00:00 2001 From: Ryota Kameoka Date: Thu, 7 May 2020 21:51:05 +0900 Subject: [PATCH 4/4] Note about JSX support on README --- readme.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/readme.md b/readme.md index dfe6dc1..60f4228 100644 --- a/readme.md +++ b/readme.md @@ -376,6 +376,83 @@ See [`@cycle/react-native`](https://github.com/cyclejs/react-native).

+
+ JSX support (click here) + +

+ +### Babel + +Add the following to your webpack config: + +```js +module: { + rules: [ + { + test: /\.jsx?$/, + loader: 'babel-loader', + options: { + plugins: [ + ['transform-react-jsx', { pragma: 'jsxFactory.createElement' }], + ] + } + } + ] +}, +``` + +If you used `create-cycle-app` you may have to eject to modify the config. + +### Automatically providing jsxFactory + +You can avoid having to import `jsxFactory` in every jsx file by allowing webpack to provide it: + +```js +plugins: [ + new webpack.ProvidePlugin({ + jsxFactory: ['@cycle/react', 'jsxFactory'] + }) +], +``` + +### Typescript + +Add the following to your `tsconfig.json`: + +```js +{ + "compilerOptions": { + "jsx": "react", + "jsxFactory": "jsxFactory.createElement" + } +} +``` + +If webpack is providing `jsxFactory` you will need to add typings to `custom-typings.d.ts`: + +```js +declare var jsxFactory: any; +``` + + +### Usage + +```js +import { jsxFactory } from '@cycle/react'; +function view(state$: Stream): Stream { + return state$.map(({ count }) => ( +

+

Counter: {count}

+ + +
+ )); +} +``` + +

+
+ ## License MIT, Copyright Andre 'Staltz' Medeiros 2018