Skip to content

Releases: cyclejs/dom

Small breaking change related to events()

05 Jan 21:44
Compare
Choose a tag to compare

This version fixes event bubbling for the new event delegation-based system. Highlights are:

  • events(eventType, options), where options is usually {useCapture: true}
  • Use event.ownerTarget instead of event.currentTarget. The latter is semantically the same as currentTarget but works in all browsers and was necessary because currentTarget is read-only according to W3 specs and we could not override it in our custom event delegation system.

Migration guide:

-DOMSource.select('.foo').events('blur', true)
+DOMSource.select('.foo').events('blur', {useCapture: true})
 DOMSource.select('.foo').events('click').map(ev =>
-  ev.currentTarget.textContent
+  ev.ownerTarget.textContent
 )

For a detailed discussion on these issues and more information, read issue 87.

New feature: DOMSource.events()

31 Dec 14:52
Compare
Choose a tag to compare

events() can now be called on the top-level sources.DOM without requiring a prior select():

      function app(sources) {
        // Event stream of every click happening in the mounted Cycle.js app
        const click$ = sources.DOM.events('click');
        // ...
      }

This closes issue #84.

The source code was refactored to use event delegation for every events() call. #85 This might or might not break functionality of your application. Migrate carefully. On the other hand, performance might improve significantly.

v8.0.0 - Cycle Nested

21 Dec 19:34
Compare
Choose a tag to compare

Cycle Nested

Cycle Nested is a new version of Cycle.js with a focus on hard-core reusability: any Cycle.js app can be easily reused in a larger Cycle.js app.

New documentation site at cycle.js.org.

Cycle Nested consists of:

NEW FEATURES in Cycle Nested:

  • Components are simply Cycle.js apps (main() renamed to e.g. Button()) that can be reused in larger apps.
  • Cycle DOM introduces hyperscript helpers, so you can create virtual DOM with functions like div(), h1(), ul(), li(), span(), etc.

Migration guide

Names changed.

Before After
DOM.get() DOM.select().events()
Response (naming convention) Source (naming convention)
Request (naming convention) Sink (naming convention)
Cycle DOM mockDOMResponse() Cycle DOM mockDOMSource()
labeledSlider (custom element) LabeledSlider (dataflow component)
let [sinks, sources] = Cycle.run(m, d) let {sinks, sources} = Cycle.run(m, d)

Custom Elements removed. Dataflow components replace them.

-function labeledSlider(sources) {
+function LabeledSlider(sources) {
-  const initialValue$ = sources.props.get('initial')
+  const initialValue$ = sources.props$
+    .map(props => props.initial)
     .first();

   const newValue$ = sources.DOM
     .select('.slider')
     .events('input')
     .map(ev => ev.target.value);

   const value$ = initialValue$.concat(newValue$);

-  const props$ = sources.props.getAll();

   const vtree$ = Rx.Observable
-    .combineLatest(props$, value$, (props, value) =>
+    .combineLatest(sources.props$, value$, (props, value) =>
       h('div.labeled-slider', [
         h('span.label', [
           props.label + ' ' + value + props.unit
         ]),
         h('input.slider', {
           type: 'range',
           min: props.min,
           max: props.max,
           value: value
         })
       ])
     );

   return {
     DOM: vtree$,
-    events: {
-      newValue: newValue$
-    }
+    value$: value$
   };
 }

The function above, LabeledSlider() follows the same techniques we use to build any main() function in Cycle.js. There is no magic and no tricks, it is simply a function that does what it says it does. The usage of components is very different to the usage of custom elements.

Not necessary to register the component anymore:

-const domDriver = CycleDOM.makeDOMDriver('#app', {
-  'labeled-slider': labeledSlider // our function
-});
+const domDriver = CycleDOM.makeDOMDriver('#app');

Using a component in a parent view has changed:

 function main(sources) {
   // ...

-  const childValue$ = state(intent(sources.DOM));

+  const props$ = Observable.of({
+    label: 'Radius', unit: '', min: 10, initial: 30, max: 100
+  });
+  const childSources = {DOM: sources.DOM, props$};
+  const labeledSlider = LabeledSlider(childSources);
+  const childVTree$ = labeledSlider.DOM;
+  const childValue$ = labeledSlider.value$;

-  const vtree$ = childValue$.map(
-    value =>
+  const vtree$ = childVTree$.withLatestFrom(childValue$,
+    (childVTree, value) =>
       h('div', [
-        h('labeled-slider#weight', {
-          key: 1, label: 'Weight', unit: 'kg',
-          min: 40, initial: weight, max: 140
-        }),
+        childVTree,
         div({style: {
           backgroundColor: '#58D3D8',
           width: String(value) + 'px',
           height: String(value) + 'px',
           borderRadius: String(value * 0.5) + 'px'
         }})
       ])
     );
   return {
     DOM: vtree$
   };
 }

To get events from a component, we don't use anymore DOM.select('labeled-slider').events('myCustomEvent'). Instead, we just get the Observable returned from the LabeledSlider component function.

Read more instructions about Dataflow components.


Using hyperscript helpers:

+import { div, span, input } from '@cycle/dom';


-h('div.labeled-slider', [
+div('.labeled-slider', [
-  h('span.label', [
+  span('.label', [
     props.label + ' ' + value + props.unit
   ]),
-  h('input.slider', {
+  input('.slider', {
     type: 'range',
     min: props.min,
     max: props.max,
     value: value
   })
 ])

For more help, read the new documentation site Cycle.js.org or ask for help in the Gitter chat room.

Add support for virtual-dom Thunks

Update peer dependency Cycle Core to v5.0

RxJS updated to v4.0, removed DOM.get()

14 Oct 20:02
Compare
Choose a tag to compare

https://github.com/cyclejs/cycle-core/releases/tag/v4.0.0

Removed DOM.get(selector, eventType). It was deprecated. Use DOM.select(selector).events(eventType) instead.

Fix a minor issue with nested svg custom elements

05 Oct 17:17
Compare
Choose a tag to compare

New testing helper: mockDOMResponse()

12 Sep 15:24
Compare
Choose a tag to compare

Added mockDOMResponse(mockedSelectors).

A testing utility which aids in creating a queryable collection of Observables. Call mockDOMResponse giving it an object specifying selectors, eventTypes and their Observabls, and get as output an object following the
same format as the DOM Driver's response.

Example:

// Create the mocked user events
const userEvents = mockDOMResponse({
  '.foo': {
    'click': Rx.Observable.just({target: {}}),
    'mouseover': Rx.Observable.just({target: {}})
  },
  '.bar': {
    'scroll': Rx.Observable.just({target: {}})
  }
});

// Use them in your tests against `main`
const requests = main({DOM: userEvents});

requests.DOM.subscribe(function (vtree) {
  // make assertions on vtree
});

Read more in the docs.

New minor feature: specify useCapture in select().events()

11 Sep 15:40
Compare
Choose a tag to compare

Thanks to @Frikki for #43, you can now specify the useCapture parameter of event listeners when defining events:

events(eventType, useCapture) where useCapture is optional and by default false.

Example:

let click$ = DOM.select('.button').events('click', true);

DOM.select().events() to replace DOM.get()

30 Aug 17:31
Compare
Choose a tag to compare

DOM.get(selector, eventType) is deprecated. Use DOM.select(selector).events(eventType) instead.

select(selector).events(eventType) is a more descriptive API than get() is. It also allows you to get an Observable of DOM elements matching the selector († below).

Before After
DOM.get('.like', 'click') DOM.select('.like').events('click')
DOM.get(':root') DOM.select(':root').observable
-- DOM.select('.like').observable

Both get() and select().events() still work as of v5.1, but using get() will trigger a warning in the console. In an upcoming major version, get() will be removed and only select().events() will work.