Skip to content

Commit 43d2133

Browse files
committed
adds test for render_item in edit mode! #60
1 parent 72fd1a6 commit 43d2133

File tree

3 files changed

+99
-25
lines changed

3 files changed

+99
-25
lines changed

Diff for: edit-todo.md

+77-8
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,8 @@ Double-clicking the <label> activates editing mode, by toggling the .editing cla
386386

387387
> _**Note**: the sample TodoMVC Browser Tests:
388388
https://github.com/tastejs/todomvc/tree/master/tests#example-output
389-
does **not** include a test-case for **double-clicking**_
389+
does **not** include a test-case for **double-clicking**.
390+
We are going to add one below for "extra credit"._
390391

391392
Since Double-clicking/tapping is the _only_ way to edit a todo item,
392393
we feel that it deserves a test.
@@ -398,7 +399,9 @@ https://stackoverflow.com/questions/5497073/how-to-differentiate-single-click-ev
398399
Reading though all the answers, we determine that the most relevant (_to us_)
399400
is: https://stackoverflow.com/a/16033129/1148249 (_which uses "vanilla" JS_)
400401

401-
![stackoverflow-double-click-example](https://user-images.githubusercontent.com/194400/45124122-14942f80-b161-11e8-94c0-f54f2352bdd5.png)
402+
[![stackoverflow-double-click-example](https://user-images.githubusercontent.com/194400/45124122-14942f80-b161-11e8-94c0-f54f2352bdd5.png)](https://stackoverflow.com/a/16033129/1148249)
403+
404+
>_**Note**: when you find a StackOverflow question/answer **helpful, upvote**!_
402405
403406
```html
404407
<div onclick="doubleclick(this, function(){alert('single')}, function(){alert('double')})">click me</div>
@@ -421,19 +424,85 @@ is: https://stackoverflow.com/a/16033129/1148249 (_which uses "vanilla" JS_)
421424
```
422425
Given that we are using the Elm Architecture to manage the DOM,
423426
we don't want a function that _alters_ the DOM.
424-
So we are going to _borrow_ the _logic_ from this example but simplify it.
427+
So we are going to _borrow_ the _logic_ from this example but _simplify_ it.
428+
Since we are not mutating the DOM by setting `data-dblclick` attributes,
429+
we won't need to remove the attribute using a `setTimeout`,
430+
431+
432+
433+
434+
435+
#### 5.2 `render_item` view function with "Edit Mode" `<input class="edit">`
436+
437+
In order to edit an item the **`render_item`** function
438+
will require **3 modifications**:
439+
440+
1. Add the `signal('EDIT', item.id)` as an **`onclick` attribute** to `<label>`
441+
so that when a `<label>` is (double-)clicked
442+
the `model.editing` property is set by the `update` function (_see below_).
443+
2. Apply the **`"class=editing"`** to the list item which is being edited.
444+
3. Display the **`<input class="edit">`**
445+
with the Todo list item title as it's **`value`** property.
446+
447+
##### 5.2 `render_item` "Edit Mode" _Test_
448+
449+
For the above modifications (_requirements_) we can write a _single_ test
450+
with four assertions:
451+
452+
```js
453+
test.only('5. Editing: > Render an item in "editing mode"', function (t) {
454+
elmish.empty(document.getElementById(id));
455+
localStorage.removeItem('elmish_' + id);
456+
const model = {
457+
todos: [
458+
{ id: 0, title: "Make something people want.", done: false },
459+
{ id: 1, title: "Bootstrap for as long as you can", done: false },
460+
{ id: 2, title: "Let's solve our own problem", done: false }
461+
],
462+
hash: '#/', // the "route" to display
463+
editing: 2 // edit the 3rd todo list item (which has id == 2)
464+
};
465+
466+
// render the ONE todo list item in "editing mode" based on model.editing:
467+
document.getElementById(id).appendChild(
468+
app.render_item(model.todos[2], model, mock_signal),
469+
);
470+
// test that signal (in case of the test mock_signal) is onclick attribute:
471+
t.equal(document.querySelectorAll('.view > label')[0].onclick.toString(),
472+
mock_signal().toString(), "mock_signal is onclick attribute of label");
473+
474+
// test that the <li class="editing"> and <input class="edit"> was rendered:
475+
t.equal(document.querySelectorAll('.editing').length, 1,
476+
"<li class='editing'> element is visible");
477+
t.equal(document.querySelectorAll('.edit').length, 1,
478+
"<input class='edit'> element is visible");
479+
t.equal(document.querySelectorAll('.edit')[0].value, model.todos[2].title,
480+
"<input class='edit'> has value: " + model.todos[2].title);
481+
t.end();
482+
});
483+
```
425484

485+
There is quite a lot to "unpack" here, but the main gist is that
486+
based on the `model.editing` key being set to `2`, our `render_item` function,
487+
will add the `editing` CSS class to the `<li>` element and render an
488+
`<input>` with CSS class `edit`.
489+
The TodoMVC style sheet (`todomvc-app.css`) will take care of displaying
490+
the input correctly.
426491

492+
Setting the **`onclick`** attribute of the `<label>` element
493+
to whatever is passed in as the third argument of `redner_item`
494+
i.e. the `signal` will mean that a specific action will be dispatched/triggered
495+
when the `<label>` element is clicked.
427496

428497

498+
> **SPOILER ALERT**: If you want to _try_ to make the "Edit Mode" _Test_
499+
assertions _pass_ without reading the "solution",
500+
do it now before proceeding to the reading the _implementation_ section.
429501

502+
<br />
430503

431-
#### 5.2 `render_item` view function with Edit controls
504+
##### 5.2 `render_item` "Edit Mode" _Implementation_
432505

433-
The `render_item` function will require 3 changes:
434-
1. Add the `"class=editing"` to the list item which is being edited.
435-
2. Add the `signal('EDIT', item.id)` as an `onclick` attribute to `<label>`
436-
3. Display the **`<input class="edit">`** with the
437506

438507
BEFORE:
439508
```js

Diff for: examples/todo-list/todo.html

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<!-- this is the *sample* html generated by VanillaJS TodoMVC -->
12
<section class="todoapp">
23
<header class="header">
34
<h1>todos</h1>

Diff for: test/todo-app.test.js

+21-17
Original file line numberDiff line numberDiff line change
@@ -395,29 +395,33 @@ test('4.1 DELETE item by clicking <button class="destroy">', function (t) {
395395
});
396396

397397

398-
test.only('5. Editing: Edit an item > <input class="edit">', function (t) {
398+
test.only('5. Editing: > Render an item in "editing mode"', function (t) {
399399
elmish.empty(document.getElementById(id));
400400
localStorage.removeItem('elmish_' + id);
401401
const model = {
402402
todos: [
403-
{ id: 0, title: "Make something people want.", done: false }
403+
{ id: 0, title: "Make something people want.", done: false },
404+
{ id: 1, title: "Bootstrap for as long as you can", done: false },
405+
{ id: 2, title: "Let's solve our own problem", done: false }
404406
],
405-
hash: '#/' // the "route" to display
407+
hash: '#/', // the "route" to display
408+
editing: 2 // edit the 3rd todo list item (which has id == 2)
406409
};
407-
// render the view and append it to the DOM inside the `test-app` node:
408-
elmish.mount(model, app.update, app.view, id, app.subscriptions);
409-
// const todo_count = ;
410-
t.equal(document.querySelectorAll('.destroy').length, 1, "one destroy button")
411-
412-
const item = document.getElementById('0')
413-
t.equal(item.textContent, model.todos[0].title, 'Item contained in DOM.');
414-
// DELETE the item by clicking on the <button class="destroy">:
415-
const button = item.querySelectorAll('button.destroy')[0];
416-
button.click()
417-
// confirm that there is no loger a <button class="destroy">
418-
t.equal(document.querySelectorAll('button.destroy').length, 0,
419-
'there is no loger a <button class="destroy"> as the only item was DELETEd')
420-
t.equal(document.getElementById('0'), null, 'todo item successfully DELETEd');
410+
// render the ONE todo list item in "editing mode" based on model.editing:
411+
document.getElementById(id).appendChild(
412+
app.render_item(model.todos[2], model, mock_signal),
413+
);
414+
// test that signal (in case of the test mock_signal) is onclick attribute:
415+
t.equal(document.querySelectorAll('.view > label')[0].onclick.toString(),
416+
mock_signal().toString(), "mock_signal is onclick attribute of label");
417+
418+
// test that the <li class="editing"> and <input class="edit"> was rendered:
419+
t.equal(document.querySelectorAll('.editing').length, 1,
420+
"<li class='editing'> element is visible");
421+
t.equal(document.querySelectorAll('.edit').length, 1,
422+
"<input class='edit'> element is visible");
423+
t.equal(document.querySelectorAll('.edit')[0].value, model.todos[2].title,
424+
"<input class='edit'> has value: " + model.todos[2].title);
421425
t.end();
422426
});
423427

0 commit comments

Comments
 (0)