From 5e3b0adec356a77213bf105a3c4ae82a7f7ec021 Mon Sep 17 00:00:00 2001 From: James Forbes Date: Wed, 29 Mar 2017 16:23:39 +1100 Subject: [PATCH 1/5] Acknowledge multiple component creation strategies --- docs/framework-comparison.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/framework-comparison.md b/docs/framework-comparison.md index a27ae135..96d9087f 100644 --- a/docs/framework-comparison.md +++ b/docs/framework-comparison.md @@ -203,7 +203,7 @@ Vue | Mithril Vue is heavily inspired by Angular and has many things that Angular does (e.g. directives, filters, bi-directional bindings, `v-cloak`), but also has things inspired by React (e.g. components). As of Vue 2.0, it's also possible to write templates using hyperscript/JSX syntax (in addition to single-file components and the various webpack-based language transpilation plugins). Vue provides both bi-directional data binding and an optional Redux-like state management library, but unlike Angular, it provides no style guide. The many-ways-of-doing-one-thing approach can cause architectural fragmentation in long-lived projects. -Mithril has far less concepts and typically organizes applications in terms of components and a data layer. There are no different ways of defining components, and thus there's no need to install different sets of tools to make different flavors work. +Mithril has far less concepts and typically organizes applications in terms of components and a data layer. There's no need to install different sets of tools to make different flavors work, because all component creation styles in Mithril output the same vnode structure. #### Documentation From 6612cf6ebd92b3d24acb923f79ac7501c3c348ab Mon Sep 17 00:00:00 2001 From: James Forbes Date: Thu, 30 Mar 2017 12:09:17 +1100 Subject: [PATCH 2/5] ES6 classes may require build tools. Instead of saying they won't need build tools, we can say that we only use native features of the language which usually makes for a simpler project. --- docs/framework-comparison.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/framework-comparison.md b/docs/framework-comparison.md index 96d9087f..cf8a3483 100644 --- a/docs/framework-comparison.md +++ b/docs/framework-comparison.md @@ -203,7 +203,7 @@ Vue | Mithril Vue is heavily inspired by Angular and has many things that Angular does (e.g. directives, filters, bi-directional bindings, `v-cloak`), but also has things inspired by React (e.g. components). As of Vue 2.0, it's also possible to write templates using hyperscript/JSX syntax (in addition to single-file components and the various webpack-based language transpilation plugins). Vue provides both bi-directional data binding and an optional Redux-like state management library, but unlike Angular, it provides no style guide. The many-ways-of-doing-one-thing approach can cause architectural fragmentation in long-lived projects. -Mithril has far less concepts and typically organizes applications in terms of components and a data layer. There's no need to install different sets of tools to make different flavors work, because all component creation styles in Mithril output the same vnode structure. +Mithril has far less concepts and typically organizes applications in terms of components and a data layer. All component creation styles in Mithril output the same vnode structure using native Javascript features only. The direct consequence of leaning on the language is less tooling and a simpler project setup. #### Documentation From c32d508de130be5ba70245682b5ccca10fd5cf88 Mon Sep 17 00:00:00 2001 From: Henrik Myntti Date: Thu, 30 Mar 2017 18:46:42 +0200 Subject: [PATCH 3/5] Improve keys example The previous example about hiding keys had syntax errors, and example in itself didn't follow our own guidelines (key should be on immediate child of an array) In the new example we reuse the code from the previous example but break out a User component (which is much more likely) and the example says to move the key from inside the user component instead --- docs/keys.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/keys.md b/docs/keys.md index d6b16f8c..0ad0b0c5 100644 --- a/docs/keys.md +++ b/docs/keys.md @@ -96,21 +96,21 @@ users.map(function(u) { #### Avoid hiding keys in component root elements -If you refactor the code and put the button inside a component, the key must be moved out of the component and placed back where the component took the place of the button. +If you refactor the code and make a user component, the key must be moved out of the component and put on the component itself, since it is now the immediate child of the array. ```javascript // AVOID -var Button = { +var User = { view: function(vnode) { - return m("button", {key: vnode.attrs.id}, u.name) + return m("div", { key: vnode.attrs.user.id }, [ + m(Button, vnode.attrs.user.name) + ]) } } // PREFER users.map(function(u) { - return m("div", [ - m(Button, {key: u.id}, u.name) // key should be here, not in component - ]) + return m(User, { key: u.id, user: u }) // key should be here, not in component }) ``` @@ -195,12 +195,12 @@ users[0].key = 'c' // AVOID users.map(function(user){ // The component for John will be destroyed and recreated - return m(UserComponent, user) + return m(UserComponent, user) }) // PREFER users.map(function(user){ // Key is specifically extracted: data model is given its own property - return m(UserComponent, {key: user.id, model: user}) + return m(UserComponent, {key: user.id, model: user}) }) ``` From d0ee256390b8600d595e06de7e67d3f39dd0fb91 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Sat, 1 Apr 2017 16:48:22 +0200 Subject: [PATCH 4/5] [mocks] make location.onhashchange debounced async --- test-utils/pushStateMock.js | 19 ++- test-utils/tests/test-pushStateMock.js | 157 ++++++++++++++++++------- 2 files changed, 129 insertions(+), 47 deletions(-) diff --git a/test-utils/pushStateMock.js b/test-utils/pushStateMock.js index ebd963fd..c727f393 100644 --- a/test-utils/pushStateMock.js +++ b/test-utils/pushStateMock.js @@ -1,6 +1,18 @@ "use strict" var parseURL = require("../test-utils/parseURL") +var callAsync = require("../test-utils/callAsync.js") + +function debouncedAsync(f) { + var ref + return function() { + if (ref != null) return + ref = callAsync(function(){ + ref = null + f() + }) + } +} module.exports = function(options) { if (options == null) options = {} @@ -29,7 +41,9 @@ module.exports = function(options) { if (data.search != null && data.search !== search) search = data.search, isNew = true if (data.hash != null && data.hash !== hash) { hash = data.hash - if (!isNew) hashchange() + if (!isNew) { + hashchange() + } } return isNew } @@ -38,9 +52,10 @@ module.exports = function(options) { if (value === "") return "" return (value.charAt(0) !== prefix ? prefix : "") + value } - function hashchange() { + function _hashchange() { if (typeof $window.onhashchange === "function") $window.onhashchange({type: "hashchange"}) } + var hashchange = debouncedAsync(_hashchange) function popstate() { if (typeof $window.onpopstate === "function") $window.onpopstate({type: "popstate", state: $window.history.state}) } diff --git a/test-utils/tests/test-pushStateMock.js b/test-utils/tests/test-pushStateMock.js index 41efacab..b07ee1b4 100644 --- a/test-utils/tests/test-pushStateMock.js +++ b/test-utils/tests/test-pushStateMock.js @@ -2,7 +2,7 @@ var o = require("../../ospec/ospec") var pushStateMock = require("../../test-utils/pushStateMock") - +var callAsync = require("../../test-utils/callAsync") o.spec("pushStateMock", function() { var $window @@ -478,93 +478,160 @@ o.spec("pushStateMock", function() { }) }) o.spec("onhashchance", function() { - o("onhashchange triggers on location.href change", function() { + o("onhashchange triggers on location.href change", function(done) { $window.onhashchange = o.spy() $window.location.href = "http://localhost/#a" - o($window.onhashchange.callCount).equals(1) - o($window.onhashchange.args[0].type).equals("hashchange") + callAsync(function(){ + o($window.onhashchange.callCount).equals(1) + o($window.onhashchange.args[0].type).equals("hashchange") + done() + }) }) - o("onhashchange triggers on relative location.href change", function() { + o("onhashchange triggers on relative location.href change", function(done) { $window.onhashchange = o.spy() $window.location.href = "#a" - o($window.onhashchange.callCount).equals(1) + callAsync(function(){ + o($window.onhashchange.callCount).equals(1) + done() + }) }) - o("onhashchange triggers on location.hash change", function() { + o("onhashchange triggers on location.hash change", function(done) { $window.onhashchange = o.spy() $window.location.hash = "#a" - o($window.onhashchange.callCount).equals(1) + callAsync(function(){ + o($window.onhashchange.callCount).equals(1) + done() + }) }) - o("onhashchange does not trigger on page change", function() { + o("onhashchange does not trigger on page change", function(done) { $window.onhashchange = o.spy() $window.location.href = "http://localhost/a" - o($window.onhashchange.callCount).equals(0) + callAsync(function(){ + o($window.onhashchange.callCount).equals(0) + done() + }) }) - o("onhashchange does not trigger on page change with different hash", function() { + o("onhashchange does not trigger on page change with different hash", function(done) { $window.location.href = "http://localhost/#a" - $window.onhashchange = o.spy() - $window.location.href = "http://localhost/a#b" + callAsync(function(){ + $window.onhashchange = o.spy() + $window.location.href = "http://localhost/a#b" - o($window.onhashchange.callCount).equals(0) + callAsync(function(){ + o($window.onhashchange.callCount).equals(0) + done() + }) + }) }) - o("onhashchange does not trigger on page change with same hash", function() { + o("onhashchange does not trigger on page change with same hash", function(done) { $window.location.href = "http://localhost/#b" - $window.onhashchange = o.spy() - $window.location.href = "http://localhost/a#b" + callAsync(function(){ + $window.onhashchange = o.spy() + $window.location.href = "http://localhost/a#b" - o($window.onhashchange.callCount).equals(0) + callAsync(function(){ + o($window.onhashchange.callCount).equals(0) + done() + }) + }) }) - o("onhashchange triggers on history.back()", function() { + o("onhashchange triggers on history.back()", function(done) { $window.location.href = "#a" - $window.onhashchange = o.spy() - $window.history.back() + callAsync(function(){ + $window.onhashchange = o.spy() + $window.history.back() - o($window.onhashchange.callCount).equals(1) + callAsync(function(){ + o($window.onhashchange.callCount).equals(1) + done() + }) + }) }) - o("onhashchange triggers on history.forward()", function() { + o("onhashchange triggers on history.forward()", function(done) { $window.location.href = "#a" - $window.onhashchange = o.spy() - $window.history.back() - $window.history.forward() + callAsync(function(){ + $window.onhashchange = o.spy() + $window.history.back() + callAsync(function(){ + $window.history.forward() - o($window.onhashchange.callCount).equals(2) + callAsync(function(){ + o($window.onhashchange.callCount).equals(2) + done() + }) + }) + }) }) - o("onhashchange does not trigger on history.back() that causes page change with different hash", function() { + o("onhashchange triggers once when the hash changes twice in a single tick", function(done) { + $window.location.href = "#a" + callAsync(function(){ + $window.onhashchange = o.spy() + $window.history.back() + $window.history.forward() + + callAsync(function(){ + o($window.onhashchange.callCount).equals(1) + done() + }) + }) + }) + o("onhashchange does not trigger on history.back() that causes page change with different hash", function(done) { $window.location.href = "#a" $window.location.href = "a#b" - $window.onhashchange = o.spy() - $window.history.back() + callAsync(function(){ + $window.onhashchange = o.spy() + $window.history.back() - o($window.onhashchange.callCount).equals(0) + callAsync(function(){ + o($window.onhashchange.callCount).equals(0) + done() + }) + }) }) - o("onhashchange does not trigger on history.back() that causes page change with same hash", function() { + o("onhashchange does not trigger on history.back() that causes page change with same hash", function(done) { $window.location.href = "#a" $window.location.href = "a#a" - $window.onhashchange = o.spy() - $window.history.back() + callAsync(function(){ + $window.onhashchange = o.spy() + $window.history.back() - o($window.onhashchange.callCount).equals(0) + callAsync(function(){ + o($window.onhashchange.callCount).equals(0) + done() + }) + }) }) - o("onhashchange does not trigger on history.forward() that causes page change with different hash", function() { + o("onhashchange does not trigger on history.forward() that causes page change with different hash", function(done) { $window.location.href = "#a" $window.location.href = "a#b" - $window.onhashchange = o.spy() - $window.history.back() - $window.history.forward() + callAsync(function(){ + $window.onhashchange = o.spy() + $window.history.back() + $window.history.forward() - o($window.onhashchange.callCount).equals(0) + callAsync(function(){ + o($window.onhashchange.callCount).equals(0) + done() + }) + }) }) - o("onhashchange does not trigger on history.forward() that causes page change with same hash", function() { + o("onhashchange does not trigger on history.forward() that causes page change with same hash", function(done) { $window.location.href = "#a" $window.location.href = "a#b" - $window.onhashchange = o.spy() - $window.history.back() - $window.history.forward() + callAsync(function(){ + $window.onhashchange = o.spy() + $window.history.back() + $window.history.forward() - o($window.onhashchange.callCount).equals(0) + callAsync(function(){ + o($window.onhashchange.callCount).equals(0) + done() + }) + }) }) }) o.spec("onunload", function() { From 63ce8995a296519405fb3872e9b6402e37576327 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Gerardy Date: Mon, 3 Apr 2017 10:27:24 +0200 Subject: [PATCH 5/5] Update change log for v1.1.1 --- docs/change-log.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/change-log.md b/docs/change-log.md index ca5e695c..2d3b03fe 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -1,5 +1,6 @@ # Change log +- [v1.1.1](#v111) - [v1.1.0](#v110) - [v1.0.1](#v101) - [Migrating from v0.2.x](#migrating-from-v02x) @@ -7,12 +8,21 @@ --- +### v1.1.1 + +#### Bug fixes + +- hyperscript: Allow `0` as the second argument to `m()` - [#1752](https://github.com/lhorie/mithril.js/issues/#1752) / [#1753](https://github.com/lhorie/mithril.js/pull/#1753) ([@StephanHoyer](https://github.com/StephanHoyer)) +- hyperscript: remove `attrs.class` after normalizing to `attrs.className` - [#1764](https://github.com/lhorie/mithril.js/issues/#1764) / [#1769](https://github.com/lhorie/mithril.js/pull/#1769) +- documentation improvements ([@JAForbes](https://github.com/JAForbes), [@smuemd](https://github.com/smuemd), [@hankeypancake](https://github.com/hankeypancake)) + ### v1.1.0 #### News - support for ES6 class components - support for closure components +- improvements in build and release automation #### Bug fixes