Skip to content

Commit c5101d9

Browse files
committed
add empty function with JSDOC & test for #44
1 parent 8aafc99 commit c5101d9

File tree

2 files changed

+67
-86
lines changed

2 files changed

+67
-86
lines changed

Diff for: examples/todo-list/elmish.js

+37-75
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,51 @@
1-
// Define the Component's Actions:
2-
var Inc = 'inc'; // increment the counter
3-
var Dec = 'dec'; // decrement the counter
4-
var Res = 'reset'; // reset counter: git.io/v9KJk
51

6-
function update(model, action) { // Update function takes the current state
7-
var parts = action ? action.split('-') : []; // e.g: inc-0 where 0 is the counter "id"
8-
var act = parts[0];
9-
var index = parts[1] || 0;
10-
var new_model = JSON.parse(JSON.stringify(model)) // "clone" the model
11-
switch(act) { // and an action (String) runs a switch
12-
case Inc:
13-
new_model.counters[index] = model.counters[index] + 1;
14-
break;
15-
case Dec:
16-
new_model.counters[index] = model.counters[index] - 1;
17-
break;
18-
case Res: // use ES6 Array.fill to create a new array with values set to 0:
19-
new_model.counters[index] = 0;
20-
break;
21-
default: return model; // if action not defined, return curent state.
22-
}
23-
return new_model;
24-
}
252

26-
function view(signal, model, root) {
27-
empty(root); // clear root element before re-rendering the App (DOM).
28-
model.counters.map(function(counter, index) {
29-
return container(index, [ // wrap DOM nodes in an "container"
30-
button('+', signal, Inc + '-' + index), // append index to action
31-
div('count', counter), // create div w/ count as text
32-
button('-', signal, Dec + '-' + index), // decrement counter
33-
button('Reset', signal, Res + '-' + index) // reset counter
34-
]);
35-
}).forEach(function (el) { root.appendChild(el) }); // forEach is ES5 so IE9+
3+
4+
5+
/**
6+
* `empty` the contents of a given DOM element "node" (before re-rendering).
7+
* This is the *fastest* way according to: stackoverflow.com/a/3955238/1148249
8+
* @param {Object} node the exact DOM node you want to empty
9+
* @return {Boolean} returns true once the DOM node is empty.
10+
* @example
11+
* // returns true (once the 'app' node is emptied)
12+
* const node = document.getElementById('app');
13+
* empty(node);
14+
*/
15+
function empty(node) {
16+
while (node.lastChild) {
17+
node.removeChild(node.lastChild);
18+
}
19+
return true;
3620
}
3721

3822
// Mount Function receives all MUV and mounts the app in the "root" DOM Element
3923
function mount(model, update, view, root_element_id) {
4024
var root = document.getElementById(root_element_id); // root DOM element
41-
function signal(action) { // signal function takes action
42-
return function callback() { // and returns callback
43-
model = update(model, action); // update model according to action
44-
view(signal, model, root); // subsequent re-rendering
25+
function signal(action) { // signal function takes action
26+
return function callback() { // and returns callback
27+
var updatedModel = update(model, action); // update model for the action
28+
empty(root); // clear root el before rerender
29+
view(signal, updatedModel, root); // subsequent re-rendering
4530
};
4631
};
47-
view(signal, model, root); // render initial model (once)
32+
view(signal, model, root); // render initial model (once)
4833
}
4934

50-
// The following are "Helper" Functions which each "Do ONLY One Thing" and are
51-
// used in the "View" function to render the Model (State) to the Browser DOM:
52-
53-
// empty the contents of a given DOM element "node" (before re-rendering)
54-
function empty(node) {
55-
while (node.firstChild) {
56-
node.removeChild(node.firstChild);
57-
}
58-
} // Inspired by: stackoverflow.com/a/3955238/1148249
59-
60-
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section
61-
function container(index, elements) {
62-
var con = document.createElement('section');
63-
con.id = index;
64-
con.className = 'counter';
65-
elements.forEach(function(el) { con.appendChild(el) });
66-
return con;
35+
function init(doc){
36+
document = doc; // this is used for instantiating JSDOM. ignore!
6737
}
6838

69-
function button(text, signal, action) {
70-
var button = document.createElement('button');
71-
var text = document.createTextNode(text); // human-readable button text
72-
button.appendChild(text); // text goes *inside* not attrib
73-
button.className = action.split('-')[0]; // use action as CSS class
74-
button.id = action;
75-
// console.log(signal, ' action:', action)
76-
button.onclick = signal(action); // onclick tells how to process
77-
return button; // return the DOM node(s)
78-
} // how to create a button in JavaScript: stackoverflow.com/a/8650996/1148249
79-
80-
function div(divid, text) {
81-
var div = document.createElement('div');
82-
div.id = divid;
83-
div.className = divid;
84-
if(text !== undefined) { // if text is passed in render it in a "Text Node"
85-
var txt = document.createTextNode(text);
86-
div.appendChild(txt);
39+
/* The code block below ONLY Applies to tests run using Node.js */
40+
/* istanbul ignore next */
41+
if (typeof module !== 'undefined' && module.exports) {
42+
module.exports = {
43+
// view: view,
44+
mount: mount,
45+
// update: update,
46+
// div: div,
47+
// button: button,
48+
empty: empty,
49+
init: init
8750
}
88-
return div;
89-
}
51+
} else { init(document); }

Diff for: test/elmish.test.js

+30-11
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
1-
const test = require('tape'); // see: https://github.com/dwyl/learn-tape
1+
const test = require('tape'); // https://github.com/dwyl/learn-tape
22
const fs = require('fs');
33
const path = require('path');
4-
const { JSDOM } = require("jsdom"); // https://github.com/jsdom/jsdom
5-
6-
// const html = fs.readFileSync(path.resolve(__dirname,
7-
// '../examples/counter-reset/index.html'));
8-
// const DOM = new JSDOM(html); // create DOM based on HTML
9-
// const id = 'test-app';
10-
// const document = DOM.window.document; // shortcut to JSDOM document
11-
// const counter = require(path.resolve(__dirname,
12-
// '../examples/counter-reset/counter.js'));
4+
require('decache')('jsdom'); // https://github.com/dwyl/decache
5+
const JSDOM = require("jsdom").JSDOM; // https://github.com/jsdom/jsdom
6+
const elmish = require(path.resolve(__dirname,
7+
'../examples/todo-list/elmish.js'))
8+
const html = fs.readFileSync(path.resolve(__dirname,
9+
'../examples/todo-list/index.html'));
10+
const DOM = new JSDOM(html); // create DOM based on HTML
11+
const id = 'test-app';
12+
const document = DOM.window.document; // shortcut to JSDOM document
13+
elmish.init(document); // pass the JSDOM into counter.js
1314
// const { view, mount, update, div, button, empty, init} = counter;
14-
// init(document); // pass the JSDOM into counter.js
15+
16+
test('empty("root") removes DOM elements from container', function (t) {
17+
// setup the test div:
18+
const text = 'Hello World!'
19+
const root = document.getElementById(id);
20+
const div = document.createElement('div');
21+
div.id = 'mydiv';
22+
const txt = document.createTextNode(text);
23+
div.appendChild(txt);
24+
root.appendChild(div);
25+
// check text of the div:
26+
const actual = document.getElementById('mydiv').textContent;
27+
t.equal(actual, text, "Contents of mydiv is: " + actual + ' == ' + text);
28+
t.equal(root.childElementCount, 1, "Root element " + id + " has 1 child el");
29+
// empty the root DOM node:
30+
elmish.empty(root);
31+
t.equal(root.childElementCount, 0, "After empty(root) has 0 child elements!")
32+
t.end();
33+
});
1534

1635
// test('Mount app expect state to be Zero', function (t) {
1736
// mount(0, update, view, id);

0 commit comments

Comments
 (0)