Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support JSX as a pragma #9

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"@types/mocha": "^5.2.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",
Expand All @@ -46,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"
}
}
77 changes: 77 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,83 @@ See [`@cycle/react-native`](https://github.com/cyclejs/react-native).
</p>
</details>

<details>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: GitHub's "Display the rich diff" will help.

<summary><strong>JSX support</strong> (click here)</summary>

<p>

### 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']
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line was originally

jsxFactory: ['react-dom', 'jsxFactory']

(see https://github.com/cyclejs/react-dom/blob/229e592ab80998ace5bf4fd0c12eee36f4499731/readme.md#automatically-providing-jsxfactory)

But I believe 'react-dom' was a mistake for '@cycle/react-dom', so now it is '@cycle/react'.

})
],
```

### 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<State>): Stream<ReactElement> {
return state$.map(({ count }) => (
<div>
<h2>Counter: {count}</h2>
<button sel="add">Add</button>
<button sel="subtract">Subtract</button>
</div>
));
}
```

</p>
</details>

## License

MIT, Copyright Andre 'Staltz' Medeiros 2018
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
36 changes: 36 additions & 0 deletions src/jsxFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {createElement, ReactElement, ReactType} from 'react';
Copy link
Author

@ryota-ka ryota-ka May 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is a copy and paste of https://github.com/cyclejs/react-dom/blob/229e592ab80998ace5bf4fd0c12eee36f4499731/src/jsx-factory.ts, except for import path of incorporate funciton.

The original filename was kebab case (jsx-factory.tsx), but I've renamed to camel case, following the naming convention of this repository.

import {incorporate} from './incorporate';
export {Attributes} from 'react';

declare global {
namespace JSX {
interface IntrinsicAttributes {
sel?: string | symbol;
}
}
namespace React {
interface ClassAttributes<T> extends Attributes {
sel?: string | symbol;
}
}
}

type PropsExtensions = {
sel?: string | symbol;
}

function createIncorporatedElement<P = any>(
type: ReactType<P>,
props: P & PropsExtensions | null,
...children: Array<string | ReactElement<any>>
): ReactElement<P> {
if (!props || !props.sel) {
return createElement(type, props, ...children);
} else {
return createElement(incorporate(type), props, ...children);
}
}

export default {
createElement: createIncorporatedElement
}
83 changes: 83 additions & 0 deletions test/jsxFactory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import * as React from 'react';
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test cases have been almost rewritten in a DOM-free way (using enzyme's shallow rendering).
The next one, however, was difficult to rewrite with my limited knowledge.

https://github.com/cyclejs/react-dom/blob/229e592ab80998ace5bf4fd0c12eee36f4499731/test/jsx-factory.tsx#L152-L185

Any good ideas?

import * as Adapter from 'enzyme-adapter-react-16';
import * as Enzyme from 'enzyme';
Comment on lines +1 to +3
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider turning on esModuleInterop?

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(<h1></h1>);

assert.strictEqual(wrapper.find('h1').length, 1);
});

it('w/ text child', () => {
const wrapper = shallow(<h1>heading 1</h1>);

const h1 = wrapper.find('h1');
assert.strictEqual(h1.text(), 'heading 1');
});

it('w/ children array', () => {
const wrapper = shallow(
<section>
<h1>heading 1</h1>
<h2>heading 2</h2>
<h3>heading 3</h3>
</section>
);

const section = wrapper.find('section');
assert.strictEqual(section.children().length, 3);
});

it('w/ props', () => {
const wrapper = shallow(<section data-foo='bar' />);

assert(wrapper.exists('[data-foo="bar"]'));
});

it('w/ props and children', () => {
const wrapper = shallow(
<section data-foo="bar">
<h1>heading 1</h1>
<h2>heading 2</h2>
<h3>heading 3</h3>
</section>
);

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(<section className="foo" />);

assert(wrapper.hasClass('foo'));
});

it('renders functional component', () => {
const Test = () => <h1>Functional</h1>;
const wrapper = shallow(<Test />);

assert.strictEqual(wrapper.first().type(), 'h1');
});

it('renders class component', () => {
class Test extends React.Component {
render() {
return <h1>Class</h1>;
}
}

const wrapper = shallow(<Test />);

assert.strictEqual(wrapper.first().type(), 'h1');
});
});
4 changes: 3 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Comment on lines +15 to +16
Copy link
Author

@ryota-ka ryota-ka May 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of configuring jsx and jsxFactory here?
Or should we separately have test/tsconfig.json instead?

},
"files": ["src/index.ts"]
}