cycle-react
Advanced tools
Comparing version 1.0.0-beta3 to 1.0.1
# Changelog | ||
## 1.0.0-beta3 | ||
## 1.0.1 | ||
MAJOR RELEASE: 1.0.1 is the stable version of Cycle-React. | ||
Add feature: `props.getAll()` returns the Observable of the whole properties object | ||
@@ -18,2 +20,7 @@ [#10] | ||
Breaking change: `renderAsHTML` has been removed | ||
> Use `React.renderToString` with `Cycle.component` instead. | ||
> Examples can be found at `test/node/html-render.js` and `examples/isomorphic`. | ||
Breaking change: The `h` hyperscript helper has been removed [#11] | ||
@@ -20,0 +27,0 @@ |
@@ -0,18 +1,9 @@ | ||
# Cycle-React API | ||
# `Cycle` object API | ||
- [`applyToDOM`](#applyToDOM) | ||
- [`renderAsHTML`](#renderAsHTML) | ||
- [`component`](#component) | ||
- [`makeDOMDriver`](#makeDOMDriver) | ||
- [`React`](#React) | ||
- [`Rx`](#Rx) | ||
- [`h`](#h) | ||
### <a id="applyToDOM"></a> `applyToDOM(container, computer)` | ||
@@ -42,19 +33,2 @@ | ||
### <a id="renderAsHTML"></a> `renderAsHTML(vtree$)` | ||
Converts a given Observable of React elements (`vtree$`) into an | ||
Observable of corresponding HTML strings (`html$`). The provided `vtree$` | ||
must complete (must call onCompleted on its observers) in finite time, | ||
otherwise the output `html$` will never emit an HTML string. | ||
#### Arguments: | ||
- `vtree$ :: RxObservable` Observable of React elements. | ||
#### Return: | ||
*(Rx.Observable)* an Observable emitting a string as the HTML renderization of the React element. | ||
- - - | ||
### <a id="component"></a> `component(displayName, definitionFn, [options])` | ||
@@ -151,11 +125,1 @@ | ||
- - - | ||
### <a id="h"></a> `h` | ||
This is a helper for creating VTrees in Views. The API is identical to | ||
[virtual-hyperscript]( | ||
https://github.com/Matt-Esch/virtual-dom/tree/master/virtual-hyperscript) | ||
but returns React element tree instead. | ||
- - - | ||
@@ -33,1 +33,34 @@ # Interactions API | ||
``` | ||
## interactions.bindListeners | ||
Create an object with event listeners that have the property names mapped from | ||
the `interactionTypes` parameter. | ||
This helper method is inspired by `bindActionCreators` from | ||
[redux](https://github.com/gaearon/redux). | ||
Example: | ||
```js | ||
// Define interaction types | ||
let interactionTypes = { | ||
IncrementCounter: 'INCREMENT_COUNTER', | ||
DecrementCounter: 'DECREMENT_COUNTER' | ||
}; | ||
// bindListeners(interactionsTypes: any): any | ||
let listeners = interactions.bindListeners(interactionsTypes); | ||
// View | ||
<button onClick={listeners.IncrementCounter}>+1</button> | ||
``` | ||
```js | ||
// Intentions | ||
import {IncrementCounter, DecrementCounter} from './interaction-types'; | ||
export default function intent(interactions) { | ||
return { | ||
incrementCounterObservable: interactions.get(IncrementCounter), | ||
decrementCounterObservable: interactions.get(DecrementCounter) | ||
}; | ||
} | ||
``` |
{ | ||
"name": "cycle-react", | ||
"version": "1.0.0-beta3", | ||
"version": "1.0.1", | ||
"author": { | ||
@@ -24,3 +24,3 @@ "name": "pH200", | ||
], | ||
"main": "src/cycle.js", | ||
"main": "index.js", | ||
"dependencies": { | ||
@@ -35,2 +35,3 @@ "rx": "2.5.3" | ||
"babel": "^5.6.4", | ||
"babel-eslint": "^3.1.21", | ||
"babelify": "^6.1.2", | ||
@@ -40,7 +41,5 @@ "browserify": "^10.2.4", | ||
"cuid": "^1.2.5", | ||
"eslint": "^0.24.0", | ||
"express": "^4.13.0", | ||
"immutable": "^3.7.4", | ||
"jscs": "^1.13.1", | ||
"jshint": "^2.8.0", | ||
"markdox": "~0.1.9", | ||
"mocha": "^2.2.5", | ||
@@ -55,5 +54,3 @@ "react": "^0.13.3", | ||
"scripts": { | ||
"lint": "npm run jshint && npm run jscs", | ||
"jshint": "jshint src/", | ||
"jscs": "jscs src/", | ||
"lint": "eslint src examples test", | ||
"test-common": "mocha --compilers js:babel/register test/common", | ||
@@ -64,10 +61,9 @@ "pretest-onlynode": "mkdir -p test/node", | ||
"test-browser": "testem", | ||
"test": "npm run jshint && npm run test-node && npm run test-browser -- ci -l PhantomJS", | ||
"travis-test": "testem launchers && npm run jshint && npm run test-node && testem ci -s Chromium", | ||
"browserify": "NODE_ENV=production browserify src/cycle.js --standalone Cycle -o dist/cycle-react.js", | ||
"test": "npm run lint && npm run test-node && npm run test-browser -- ci -l PhantomJS", | ||
"travis-test": "testem launchers && npm run lint && npm run test-node && testem ci -s Chromium", | ||
"browserify": "NODE_ENV=production browserify index.js --standalone Cycle -o dist/cycle-react.js", | ||
"uglify": "uglifyjs dist/cycle-react.js -o dist/cycle-react.min.js", | ||
"dist": "mkdir -p dist && npm run browserify && npm run uglify", | ||
"docs": "node ./scripts/make-api-docs.js", | ||
"examples": "node examples/server.js" | ||
} | ||
} |
@@ -96,11 +96,15 @@ # Cycle-React | ||
Cycle-React is a React-style implementation of Cycle.js, so we have the same | ||
concept of handling user interactions. More information of this concept can be | ||
found at: http://cycle.js.org/ | ||
concept of handling user interactions. Learn more on: | ||
http://cycle.js.org/dialogue.html | ||
In addition, we're working on the documentation site for Cycle-React with more | ||
useful examples, too. Stay tuned! | ||
## Cycle.js Driver | ||
The original Cycle.js has the | ||
[driver architecture](http://cycle.js.org/drivers.html) starting from | ||
[v0.23](https://github.com/cyclejs/cycle-core/releases/tag/v0.23.0). | ||
Cycle-React provides a DOM driver(powered by React, of course) for Cycle.js. | ||
Cycle.js (not Cycle-React) has the | ||
[driver architecture](http://cycle.js.org/drivers.html) to externalize the | ||
side-effects. Cycle Web, for example, is a driver externalizes DOM environment. | ||
And Cycle-React provides a DOM driver (powered by React, of course) | ||
for Cycle.js, too. | ||
@@ -112,2 +116,14 @@ Details can be found at | ||
### Can I use Cycle-React with Flux (e.g. [redux](https://github.com/gaearon/redux)) | ||
Absolutely. Since Cycle-React's `component` creates native React components, | ||
there's nothing stopping you from using Flux architecture. | ||
**HOWEVER**, we don't really recommend to use Flux when you already had Rx or | ||
other event stream libraries at your disposal. Instead, we recommend the MVI | ||
architecture which also achieves unidirectional data flow. See | ||
["Reactive MVC and the Virtual DOM"](http://futurice.com/blog/reactive-mvc-and-the-virtual-dom) | ||
and ["Good bye Flux, welcome Bacon/Rx?"](https://medium.com/@milankinen/good-bye-flux-welcome-bacon-rx-23c71abfb1a7) | ||
for more details. | ||
### Can I use Cycle-React with [react-hot-loader](https://github.com/gaearon/react-hot-loader)? | ||
@@ -121,3 +137,3 @@ | ||
Yes. You can even use Cycle-React with your current React apps. Because | ||
Yes. You can also integrate Cycle-React with your current React apps. Because | ||
`component` creates the native React component for you. | ||
@@ -139,4 +155,2 @@ | ||
* Propose and discuss significant changes as a GitHub issues | ||
* In addition, more resources can be found at the official website of | ||
[Cycle.js](http://cycle.js.org/) | ||
@@ -143,0 +157,0 @@ ## License |
@@ -35,3 +35,3 @@ 'use strict'; | ||
definitionFn, | ||
options, | ||
componentOptions, | ||
observer, | ||
@@ -45,3 +45,3 @@ eventObserver) { | ||
} | ||
options = options || {}; | ||
var options = componentOptions || {}; | ||
// The option for the default root element type. | ||
@@ -48,0 +48,0 @@ var rootTagName = options.rootTagName || 'div'; |
@@ -8,3 +8,3 @@ /* globals process */ | ||
function getEventSubject(name) { | ||
function get(name) { | ||
if (name === null || name === (void 0)) { | ||
@@ -19,19 +19,32 @@ throw new Error('Invalid name for the interaction collection.'); | ||
return { | ||
get: getEventSubject, | ||
listener: function listener(name) { | ||
var eventSubject = subjects[name]; | ||
if (!eventSubject && process.env.NODE_ENV !== 'production') { | ||
if (typeof console !== 'undefined') { | ||
console.warn( | ||
'Listening event "' + name + '" before using interactions.get("' + | ||
name + '")' | ||
); | ||
} | ||
function listener(name) { | ||
var eventSubject = subjects[name]; | ||
if (!eventSubject && process.env.NODE_ENV !== 'production') { | ||
if (typeof console !== 'undefined') { | ||
console.warn( | ||
'Listening event "' + name + '" before using interactions.get("' + | ||
name + '")' | ||
); | ||
} | ||
if (!eventSubject) { | ||
eventSubject = getEventSubject(name); | ||
} | ||
return eventSubject.onEvent; | ||
} | ||
if (!eventSubject) { | ||
eventSubject = get(name); | ||
} | ||
return eventSubject.onEvent; | ||
} | ||
function bindListeners(interactionTypes) { | ||
var result = {}; | ||
var names = Object.keys(interactionTypes); | ||
for (var i = 0; i < names.length; i++) { | ||
var name = names[i]; | ||
result[name] = listener(interactionTypes[name]); | ||
} | ||
return result; | ||
} | ||
return { | ||
get: get, | ||
listener: listener, | ||
bindListeners: bindListeners | ||
}; | ||
@@ -38,0 +51,0 @@ } |
@@ -1,2 +0,2 @@ | ||
/* jshint browser:true */ | ||
/* eslint-env browser */ | ||
'use strict'; | ||
@@ -9,3 +9,3 @@ var React = require('react'); | ||
typeof HTMLElement === 'object' ? | ||
obj instanceof HTMLElement || obj instanceof DocumentFragment : //DOM2 | ||
obj instanceof HTMLElement : | ||
obj && typeof obj === 'object' && obj !== null && | ||
@@ -12,0 +12,0 @@ (obj.nodeType === 1 || obj.nodeType === 11) && |
'use strict'; | ||
/* global describe, it, beforeEach */ | ||
let assert = require('assert'); | ||
let Cycle = require('../../src/cycle'); | ||
let Cycle = require('../../'); | ||
let {Rx, React} = Cycle; | ||
@@ -98,3 +98,2 @@ | ||
it('should recognize and create two unrelated elements', function () { | ||
@@ -435,3 +434,3 @@ // Make the first custom element | ||
if (control === 0) { | ||
return <h3 className="myelementclass" /> | ||
return <h3 className="myelementclass" />; | ||
} | ||
@@ -465,3 +464,3 @@ throw new Error('The error'); | ||
done(); | ||
}) | ||
}); | ||
return Rx.Observable.just(() => | ||
@@ -492,3 +491,3 @@ <h3 className="myelementclass" | ||
<div />; | ||
}) | ||
}); | ||
Cycle.applyToDOM(createRenderTarget(), () => vtree$); | ||
@@ -522,3 +521,3 @@ // Make assertions | ||
events: { | ||
myevent$: number$.do(i => log.push(i)) | ||
onMyEvent: number$.do(i => log.push(i)) | ||
} | ||
@@ -529,6 +528,10 @@ }; | ||
let vtree$ = customElementSwitch$.map(theSwitch => { | ||
function onMyEventHandler(ev) { | ||
assert.ok(ev === 1 || ev === 2); | ||
assert.notStrictEqual(ev, 3); | ||
} | ||
return theSwitch === 0 ? | ||
<MyElement /> : | ||
<MyElement onMyEvent={onMyEventHandler} /> : | ||
<div />; | ||
}) | ||
}); | ||
Cycle.applyToDOM(createRenderTarget(), () => vtree$); | ||
@@ -542,8 +545,2 @@ // Make assertions | ||
Rx.Observable.fromEvent(myElement, 'myevent') | ||
.take(3) | ||
.subscribe(function (ev){ | ||
assert.notStrictEqual(ev.detail, 3); | ||
}); | ||
// Trigger the event | ||
@@ -580,3 +577,3 @@ number$.request(1); | ||
<div />; | ||
}) | ||
}); | ||
Cycle.applyToDOM(createRenderTarget(), () => vtree$); | ||
@@ -616,3 +613,3 @@ // Make assertions | ||
<div />; | ||
}) | ||
}); | ||
Cycle.applyToDOM(createRenderTarget(), () => vtree$); | ||
@@ -649,3 +646,3 @@ // Make assertions | ||
<div />; | ||
}) | ||
}); | ||
Cycle.applyToDOM(createRenderTarget(), () => vtree$); | ||
@@ -652,0 +649,0 @@ // Make assertions |
@@ -5,3 +5,3 @@ 'use strict'; | ||
let {run} = require('@cycle/core'); | ||
let Cycle = require('../../src/cycle'); | ||
let Cycle = require('../../'); | ||
let {Rx, React} = Cycle; | ||
@@ -8,0 +8,0 @@ |
'use strict'; | ||
let Cycle = require('../../../src/cycle'); | ||
let {Rx, React} = Cycle; | ||
let Rx = require('rx'); | ||
let React = require('react'); | ||
@@ -5,0 +5,0 @@ function myelement(interactions, props) { |
'use strict'; | ||
/* global describe, it, beforeEach */ | ||
let assert = require('assert'); | ||
let Cycle = require('../../src/cycle'); | ||
let Cycle = require('../../'); | ||
let Fixture89 = require('./fixtures/issue-89'); | ||
@@ -133,3 +133,3 @@ let {Rx, React} = Cycle; | ||
}); | ||
}) | ||
}); | ||
@@ -136,0 +136,0 @@ it('should not set props.className to the root element', function () { |
'use strict'; | ||
/* global describe, it */ | ||
let assert = require('assert'); | ||
let Cycle = require('../../src/cycle'); | ||
let Cycle = require('../../'); | ||
@@ -12,6 +12,2 @@ describe('Cycle', function () { | ||
it('should have `renderAsHTML`', function () { | ||
assert.strictEqual(typeof Cycle.renderAsHTML, 'function'); | ||
}); | ||
it('should have `component`', function () { | ||
@@ -18,0 +14,0 @@ assert.strictEqual(typeof Cycle.component, 'function'); |
'use strict'; | ||
/* global describe, it */ | ||
let assert = require('assert'); | ||
let Cycle = require('../../src/cycle'); | ||
let Cycle = require('../../'); | ||
let Rx = Cycle.Rx; | ||
@@ -6,0 +6,0 @@ |
@@ -9,3 +9,3 @@ 'use strict'; | ||
describe('interactions', function () { | ||
it('should provide collection of EventSubjects by get', function (done) { | ||
it('should provide collection of interactions', function (done) { | ||
let interactions = makeInteractions(); | ||
@@ -20,3 +20,3 @@ interactions.get('foo').subscribe(function (value) { | ||
it('should return created EventSubject from the inserted key', function () { | ||
it('should return the same interaction observable from the same key', function () { | ||
let interactions = makeInteractions(); | ||
@@ -29,2 +29,24 @@ assert.strictEqual( | ||
it('should return the object of listeners by bindListeners', function (done) { | ||
let interactions = makeInteractions(); | ||
let interactionTypes = { | ||
foo: 'onFoo', | ||
foo2: 'onFoo2' | ||
}; | ||
let foo = interactions.get(interactionTypes.foo); | ||
let foo2 = interactions.get(interactionTypes.foo2); | ||
Rx.Observable.zip( | ||
foo.tap(value => assert.strictEqual(value, 'bar')), | ||
foo2.tap(value => assert.strictEqual(value, 'bar2')), | ||
() => null | ||
).subscribe(done); | ||
let listeners = interactions.bindListeners(interactionTypes); | ||
assert.ok(listeners.foo); | ||
assert.ok(listeners.foo2); | ||
// test listeners | ||
listeners.foo('bar'); | ||
// test listener key | ||
interactions.listener(interactionTypes.foo2)('bar2'); | ||
}); | ||
it('should throw error when the key is null', function () { | ||
@@ -31,0 +53,0 @@ let interactions = makeInteractions(); |
@@ -5,21 +5,15 @@ 'use strict'; | ||
let cheerio = require('cheerio'); | ||
let Cycle = require('../../src/cycle'); | ||
let Cycle = require('../../'); | ||
let {Rx, React} = Cycle; | ||
describe('renderAsHTML()', function () { | ||
it('should output HTML when given a simple vtree stream', function (done) { | ||
let vtree$ = Rx.Observable.just( | ||
describe('Server-side rendering', function () { | ||
it('should output HTML when given a simple component', function () { | ||
let Root = Cycle.component('Root', () => Rx.Observable.just( | ||
<div className="test-element">Foobar</div> | ||
); | ||
let html$ = Cycle.renderAsHTML(vtree$); | ||
html$.subscribe(function (html) { | ||
let $ = cheerio.load(html); | ||
assert.ok($('*').first().is('div.test-element')); | ||
assert.strictEqual($('div.test-element').length, 1); | ||
assert.strictEqual($('div.test-element').text(), 'Foobar'); | ||
done(); | ||
}); | ||
)); | ||
let html = React.renderToStaticMarkup(React.createElement(Root)); | ||
assert.strictEqual(html, '<div class="test-element">Foobar</div>'); | ||
}); | ||
it('should not emit events', function (done) { | ||
it('should not emit events', function () { | ||
var log = 0; | ||
@@ -34,60 +28,21 @@ let MyElement = Cycle.component('MyElement', function () { | ||
} | ||
} | ||
}; | ||
}); | ||
let html$ = Cycle.renderAsHTML(MyElement); | ||
html$.subscribe(function (html) { | ||
let $ = cheerio.load(html); | ||
assert.ok($('*').first().is('div.test-element')); | ||
assert.strictEqual($('div.test-element').length, 1); | ||
assert.strictEqual($('div.test-element').text(), 'Foobar'); | ||
assert.notStrictEqual(log, 123); | ||
done(); | ||
}); | ||
let html = React.renderToStaticMarkup(React.createElement(MyElement)); | ||
assert.strictEqual(html, '<div class="test-element">Foobar</div>'); | ||
assert.notStrictEqual(log, 123); | ||
}); | ||
it('should render a simple nested custom element as HTML', function (done) { | ||
let MyElement = Cycle.component('MyElement', function () { | ||
return Rx.Observable.just(<h3 className="myelementclass" />); | ||
}); | ||
let vtree$ = Rx.Observable.just( | ||
it('should render a simple nested component as HTML', function () { | ||
let MyElement = Cycle.component('MyElement', () => Rx.Observable.just( | ||
<h3 className="myelementclass" /> | ||
)); | ||
let Root = Cycle.component('Root', () => Rx.Observable.just( | ||
<div className="test-element"><MyElement /></div> | ||
); | ||
let html$ = Cycle.renderAsHTML(vtree$); | ||
html$.subscribe(function (html) { | ||
let $ = cheerio.load(html); | ||
assert.ok($('*').first().is('div.test-element')); | ||
assert.strictEqual($('div.test-element').length, 1); | ||
assert.ok($('div.test-element').children().is('h3.myelementclass')); | ||
done(); | ||
}); | ||
)); | ||
let html = React.renderToStaticMarkup(React.createElement(Root)); | ||
assert.strictEqual(html, '<div class="test-element"><h3 class="myelementclass"></h3></div>'); | ||
}); | ||
it('should render double nested custom elements as HTML', function (done) { | ||
let MyElement = Cycle.component('MyElement', function () { | ||
return Rx.Observable.just(<h3 className="myelementclass" />); | ||
}); | ||
let NiceElement = Cycle.component('NiceElement', function () { | ||
return Rx.Observable.just( | ||
<div className="a-nice-element"> | ||
foobar | ||
<MyElement /> | ||
</div> | ||
); | ||
}); | ||
let vtree$ = Rx.Observable.just( | ||
<div className="test-element"><NiceElement /></div> | ||
); | ||
let html$ = Cycle.renderAsHTML(vtree$); | ||
html$.subscribe(function (html) { | ||
let $ = cheerio.load(html); | ||
assert.ok($('*').first().is('div.test-element')); | ||
assert.strictEqual($('div.test-element').length, 1); | ||
assert.ok($('div.test-element').children().is('div.a-nice-element')); | ||
assert.strictEqual($('div.a-nice-element').children().first().text(), 'foobar'); | ||
assert.ok($('div.a-nice-element').children().eq(1).is('h3.myelementclass')); | ||
done(); | ||
}); | ||
}); | ||
it('should render a nested custom element with props as HTML', function (done) { | ||
it('should render a nested custom element with props as HTML', function () { | ||
let MyElement = Cycle.component('MyElement', function (_, props) { | ||
@@ -98,17 +53,10 @@ return props.get('foobar').map(foobar => | ||
}); | ||
let vtree$ = Rx.Observable.just( | ||
let Root = Cycle.component('Root', () => Rx.Observable.just( | ||
<div className="test-element"> | ||
<MyElement foobar="yes" /> | ||
</div> | ||
); | ||
let html$ = Cycle.renderAsHTML(vtree$); | ||
html$.subscribe(function (html) { | ||
let $ = cheerio.load(html); | ||
assert.ok($('*').first().is('div.test-element')); | ||
assert.strictEqual($('div.test-element').length, 1); | ||
assert.ok($('div.test-element').children().is('h3.myelementclass')); | ||
assert.strictEqual($('h3.myelementclass').text(), 'YES'); | ||
done(); | ||
}); | ||
)); | ||
let html = React.renderToStaticMarkup(React.createElement(Root)); | ||
assert.strictEqual(html, '<div class="test-element"><h3 class="myelementclass">YES</h3></div>'); | ||
}); | ||
}); |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
14
2
156
3232136
42
62071