{"id":13476273,"url":"https://github.com/dwyl/javascript-todo-list-tutorial","last_synced_at":"2025-03-27T02:32:07.509Z","repository":{"id":36961236,"uuid":"143951877","full_name":"dwyl/javascript-todo-list-tutorial","owner":"dwyl","description":"✅ A step-by-step complete beginner example/tutorial for building a Todo List App (TodoMVC) from scratch in JavaScript following Test Driven Development (TDD) best practice.  🌱 ","archived":false,"fork":false,"pushed_at":"2024-09-24T07:24:42.000Z","size":987,"stargazers_count":625,"open_issues_count":3,"forks_count":103,"subscribers_count":159,"default_branch":"main","last_synced_at":"2024-10-11T14:19:04.414Z","etag":null,"topics":["beginner","beginners-guide","best-practices","elm-architecture","how-to","javascript","learn","step-by-step","tdd","test-driven-development","todolist","todomvc","tutorial","vanilla"],"latest_commit_sha":null,"homepage":"https://dwyl.github.io/javascript-todo-list-tutorial","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dwyl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-08T02:41:50.000Z","updated_at":"2024-10-03T14:14:05.000Z","dependencies_parsed_at":"2023-09-26T18:43:19.985Z","dependency_job_id":"bbf8a501-b937-43bc-bc25-d5459f0229b4","html_url":"https://github.com/dwyl/javascript-todo-list-tutorial","commit_stats":null,"previous_names":["dwyl/todomvc-vanilla-javascript-elm-architecture-example"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Fjavascript-todo-list-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Fjavascript-todo-list-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Fjavascript-todo-list-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwyl%2Fjavascript-todo-list-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dwyl","download_url":"https://codeload.github.com/dwyl/javascript-todo-list-tutorial/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222184184,"owners_count":16945013,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["beginner","beginners-guide","best-practices","elm-architecture","how-to","javascript","learn","step-by-step","tdd","test-driven-development","todolist","todomvc","tutorial","vanilla"],"created_at":"2024-07-31T16:01:28.367Z","updated_at":"2024-10-30T08:31:14.318Z","avatar_url":"https://github.com/dwyl.png","language":"JavaScript","readme":"\u003cdiv align=\"center\"\u003e\n\n# Todo List App JavaScript Tutorial\n\nA **_step-by-step_ tutorial** showing you how to\nbuild a **Todo List App _from scratch_** in **`JavaScript`**.\n\n[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/dwyl/javascript-todo-list-tutorial/ci.yml?label=build\u0026style=flat-square\u0026branch=main)](https://github.com/dwyl/javascript-todo-list-tutorial/actions)\n[![codecov.io](https://img.shields.io/codecov/c/github/dwyl/javascript-todo-list-tutorial/main.svg?style=flat-square)](https://codecov.io/github/dwyl/javascript-todo-list-tutorial?branch=main)\n[![Dependencies: None](https://img.shields.io/badge/dependencies-none-brightgreen.svg?style=flat-square)](https://github.com/dwyl/javascript-todo-list-tutorial/blob/main/package.json#L12 \"Zero Dependencies\")\n[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat-square)](https://github.com/dwyl/javascript-todo-list-tutorial/issues)\n[![HitCount](https://hits.dwyl.com/dwyl/todo-list-javascript-tutorial.svg)](https://hits.dwyl.com/dwyl/javascript-todo-list-tutorial)\n\u003c!-- uncomment when service is working ... [![Inline docs](http://inch-ci.org/github/dwyl/javascript-todo-list-tutorial.svg?branch=main\u0026style=flat-square)](http://inch-ci.org/github/dwyl/javascript-todo-list-tutorial) --\u003e\n\n\n\u003ca href=\"https://dwyl.github.io/javascript-todo-list-tutorial/\"\n alt=\"Try the Demo on GitHub Pages!\"\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/194400/45237254-10d5e980-b2d6-11e8-8281-b95452bde519.gif\"\n  alt=\"Step one: learn JavaScript!\"\u003e\n\u003c/a\u003e\n\n\u003c/div\u003e\n\n\u003e Before you continue, try the demo: https://dwyl.github.io/javascript-todo-list-tutorial/ \u003cbr /\u003e\n\n\u003e Add a few items to the list. Double-click/tap the item to edit it.\nCheck-off your todos and navigate the footer to filter for Active/Completed.\nTry and \"break\" it! Refresh the page and notice how your todo items\nare \"still there\" (_they were saved to `localStorage`!_).\nOnce you have had a \"play\" with the demo, come back and _build_ it!!\n\n\u003chr /\u003e\n\n## Why?\n\nThe _purpose_ of this **Todo List _mini_ project**\nis to _practice_ your \"VanillaJS\" skills and\n_consolidate_ your understanding of The Elm Architecture (TEA)\nby creating a real world _useable_ App following _strict_\nDocumentation and Test Driven Development.\n\nThis will _show_ you that it's not only _possible_\nto write docs and tests _first_,\nyou will see _first hand_ that **`code`** is **more concise**,\n**well-documented** and thus **_easier_ to maintain**\nand you will get your work done ***much faster***.\n\nThese are _foundational_ skills that will\npay **_immediate_ returns** on the time invested,\nand will **`continue`** to **`return`** \"**interest**\"\nfor as long as you write (_and people use your_) software!\n\n\u003e _It's **impossible** to \"**over-state**\" how **vital writing tests first**\nis to both your **personal effectiveness** and **long-term sanity**.\nThankfully, by the end of this chapter, you will see how **easy** it is._\n\n\n\n## What?\n\nBuild a fully functional \"Todo List\" Application! \u003cbr /\u003e\nAlong the way we will cover:\n\n+ [x] Building an App using a pre-made CSS Styles/Framework!\n+ [x] The Document Object Model (DOM) + JSDOM\n+ [x] Browser Routing/Navigation\n+ [x] Local Storage for Offline Support\n+ [x] Keyboard event listeners for rapid todo list creation and editing!\n\nWe will be abstracting all \"architecture\" related (\"generic\") code\ninto a \"mini frontend framework\" called \"***elmish***\".\n(_elmish is inspired by Elm but only meant for educational purposes!_)\n\nThe journey to creating **elmish** is captured in\n[**`elmish.md`**](https://github.com/dwyl/javascript-todo-list-tutorial/blob/main/elmish.md)\nand fully documented code is in **`elmish.js`**.\nThis means our Todo List App can be as concise\nand \"declarative\" as possible.\n\n### Todo List?\n\nIf you are _unfamiliar_ with Todo lists, simply put:\nthey are a way of keeping a list of the tasks that need to be done. \u003cbr /\u003e\nsee: https://en.wikipedia.org/wiki/Time_management#Setting_priorities_and_goals\n\nTodo Lists or \"Checklists\" are the _best_ way of tracking tasks. \u003cbr /\u003e\nAtul Gawande wrote a _superb_ book on this subject: \u003cbr /\u003e\nhttps://www.amazon.com/Checklist-Manifesto-How-Things-Right/dp/0312430000 \u003cbr /\u003e\nOr if you don't have time to read,\nwatch: https://www.youtube.com/results?search_query=checklist+manifesto\n\n### TodoMVC?\n\nIf you have not come across TodoMVC before,\nit's a website that showcases various \"frontend\" frameworks\nusing a common user interface (UI): a Todo List Application.\n![TodoMVC-intro](https://user-images.githubusercontent.com/194400/42624420-4528a3c6-85bd-11e8-8b92-9b1c8951ba35.png)\n\n\nWe _highly recommend_ checking out the following links:\n\n+ Website: https://todomvc.com\n+ GitHub project: https://github.com/tastejs/todomvc\n\nFor our purposes we will simply be re-using the **TodoMVC `CSS`**\nto make our TEA Todo List _look_ good\n(_not have to \"worry\" about styles so we can **focus on functionality**_).\nAll the JavaScript code will be written \"_from scratch_\"\nto ensure that everything is clear.\n\n## _Who?_\n\nThis tutorial is for anyone/everyone who wants\nto develop their \"core\" JavaScript skills (_without using a framework/library_)\nwhile building a \"real world\" (_fully functional_) Todo List Application.\n\n\u003e As always, if you get \"stuck\", _please_ open an issue:\nhttps://github.com/dwyl/javascript-todo-list-tutorial/issues\nby opening a question you help _everyone_ learn more effectively!\n\n\n### Prerequisites\n\nMost beginners with basic JavaScript and HTML knowledge\nshould be able to follow this example without any prior experience.\nThe code is commented and the most \"complex\" function is an event listener.\nWith that said, if you feel \"stuck\" at any point,\nplease consult the recommend reading (_and Google_)\nand if you cannot find an answer,\nplease open an issue!\n\n### Recommended reading:\n\n+ Test Driven Developement: https://github.com/dwyl/learn-tdd\n+ Tape-specific syntax: https://github.com/dwyl/learn-tape\n+ Elm Architecture: https://github.com/dwyl/learn-elm-architecture-in-javascript\n\n\n## _How?_\n\n\nStart by cloning this repository to your `localhost`\nso that you can follow the example/tutorial offline:\n\n```sh\ngit clone https://github.com/dwyl/javascript-todo-list-tutorial.git\n```\n\nInstall the `devDependencies` so you can run the tests:\n```sh\ncd javascript-todo-list-tutorial \u0026\u0026 npm install\n```\n\nNow you have _everything_ you need to build a Todo List from scratch!\n\n\n### `Elm`(_ish_) ?\n\nIn order to _simplify_ the code for our Todo List App,\nwe _abstracted_ much of the \"_generic_\" code\ninto a \"front-end micro framework\" called `Elm`(_ish_).\nThe functions \u0026 functionality of `Elm`(_ish_) should be _familiar_ to you\nso you _should_ be able to build the Todo List using the `Elm`(_ish_)\nhelper functions e.g: `mount`, `div`, `input` and `route`.\n\nYou can _opt_ to _either_: \u003cbr /\u003e\n\n**a)** read the `Elm`(_ish_) docs/tutorial\n[`elmish.md`](https://github.com/dwyl/learn-elm-architecture-in-javascript/blob/main/elmish.md)\n***`before`*** building the Todo List App -\nthis will give you both TDD practice\nand a deeper understanding of building a micro framework.\ni.e. \"**_prospective_ learning**\"\u003cbr /\u003e\n\n**b)** refer the `Elm`(_ish_) docs/tutorial\n[`elmish.md`](https://github.com/dwyl/learn-elm-architecture-in-javascript/blob/main/elmish.md)\n***`while`*** building the Todo List App when you \"**_need_ to know**\"\nhow one of the helper functions works. i.e. \"**_contextual_ learning**\" \u003cbr /\u003e\n\n**c)** **only _consult_** the `Elm`(_ish_) docs/tutorial\n[`elmish.md`](https://github.com/dwyl/learn-elm-architecture-in-javascript/blob/main/elmish.md)\n***`if`*** you are \"stuck\" ***`while`*** building the Todo List App.\ni.e. \"**_debug_ learning**\" \u003cbr /\u003e\n\nThe choice is yours; there is no \"_right_\" way to learn.\n\n\n\n### Testing \u0026 Documentation?\n\n_Before_ diving into _building_ the Todo List App,\nwe need to consider how we are going to _test_ it.\nBy ensuring that we follow **TDD** from the _start_ of an App,\nwe will have [\"***no surprises***\"](https://youtu.be/u5CVsCnxyXg)\nand _avoid_ having to \"correct\" any\n[\"***bad habits***\"](https://www.youtube.com/results?search_query=Destiny%27s+Child+Bad+Habit).\n\nWe will be using **Tape** and **JSDOM** for testing\nboth our functions and the final application.\nIf you are `new` to either of these tools,\nplease see:\n[github.com/dwyl/**learn-tape**](https://github.com/dwyl/learn-tape)\nand\n[**front-end**-with-tape.md](https://github.com/dwyl/learn-tape/blob/main/front-end-with-tape.md)\n\nWe will be using **JSDOC** for documentation.\nPlease see [our tutorial](https://github.com/dwyl/learn-jsdoc) if this is new to you.\n\n\u003cbr /\u003e\n\n#### Create Files\n\nCreate a **`new`** directory e.g: `/todo-app`\nSo that you can build the Todo List from scratch!\n\nIn your editor/terminal create the following files:\n\n+ `test/todo-app.test.js`\n+ `lib/todo-app.js`\n+ `index.html`\n\nThese file names should be self-explanatory, but if unclear,\n`todo-app.test.js` is where we will write the tests for our\nTodo List App.\n`todo-app.js` is where all the JSDOCs and functions\nfor our Todo List App will be written.\n\n#### Test Setup\n\nIn order to run our test(s), we need some \"setup\" code\nthat \"requires\" the libraries/files so we can _execute_ the functions.\n\nIn the `test/todo-app.test.js` file, type the following code:\n```js\nconst test = require('tape');       // https://github.com/dwyl/learn-tape\nconst fs = require('fs');           // to read html files (see below)\nconst path = require('path');       // so we can open files cross-platform\nconst html = fs.readFileSync(path.resolve(__dirname, '../index.html'));\nrequire('jsdom-global')(html);      // https://github.com/rstacruz/jsdom-global\nconst app = require('../lib/todo-app.js'); // functions to test\nconst id = 'test-app';              // all tests use 'test-app' as root element\n```\n\n\u003e Most of this code should be _familiar_ to you\nif you have followed previous tutorials.\n\u003e If anything is _unclear_ please revisit\n[https://github.com/dwyl/**learn-tape**](https://github.com/dwyl/learn-tape)\nand\n[**front-end**-with-tape.md](https://github.com/dwyl/learn-tape/blob/main/front-end-with-tape.md)\n\nIf you attempt to run the test file: `node test/todo-app.test.js`\nyou should see no output. \u003cbr /\u003e\n(_this is expected as we haven't written any tests yet!_)\n\n\n### `model`\n\nThe `model` for our Todo List App is **_boringly_ simple**.\nAll we need is an `Object` with a\n`todos` key which has an Array of Objects as it's value:\n\n```js\n{\n  todos: [\n    { id: 1, title: \"Learn Elm Architecture\", done: true },\n    { id: 2, title: \"Build Todo List App\",    done: false },\n    { id: 3, title: \"Win the Internet!\",      done: false }\n  ]\n}\n```\n`todos` is an `Array` of `Objects` and each Todo (Array) item\nhas 3 keys:\n+ `id`: the index in the list.\n+ `title`: the title/description of the todo item.\n+ `done`: a `boolean` indicating if the item is complete or still \"todo\".\n\n\n#### What about the `count` of items ?\n\n\u003e The TodoMVC Specification requires us to display a **`counter`**\nof the items in the Todo list:\nhttps://github.com/tastejs/todomvc/blob/main/app-spec.md#counter\n\n![javascript-todo-list-count](https://user-images.githubusercontent.com/194400/73112092-e73a5400-3f04-11ea-90f6-d4ae541a129c.png)\n\nIn order to display the `count` of items in the Todo list,\nwe _could_ store 3 values in the model:\n\n+ `total_items` - the total number of items, in this case 3.\n+ `completed_items` - the number of completed items. in this case 1.\n+ `incomplete_items` - the number of items still to be done; 2.\n\nEach time a `new item` is added to the list\nwe would need to update\nboth the `total_items`\nand the `incomplete_items`\nvalues in the `model`.\nAnd each time an `item` gets checked off as \"done\",\nwe would need to update _both_ the `incomplete_items`\nand the `completed_items`.\nThis is _unnecessary_ effort we can avoid.\nWe can simply _compute_ these values based on the data in the `todos` Array\nand display them for the user without storing any additional data.\n\nInstead of _storing_ any additional data for a `counter` in the model\n(_the count of active and completed Todo items_),\nwe will _compute_ the count and display the count at \"runtime\".\nWe don't _need_ to store any additional data in the `model`.\nThis may use a few CPU cycles computing the `count`\neach time the view is rendered but that's \"OK\"!\nEven on an _ancient_ Android device\nthis will only take a millisecond to compute and\nwon't \"slow down\" the app or affect UX.\n\nSee below for how the three counts are computed.\n\ne.g: in the model above there are 3 todo items in the `todos` Array;\n2 items which are \"active\" (`done=false`)\nand 1 which is \"done\" (`done=true`).\n\n#### `model` _Test_\n\nGiven that the `model` is \"just data\"\n(\n_it has **no** \"**methods**\" because `Elm`(ish) is_\n[\"***Functional***\"](https://en.wikipedia.org/wiki/Functional_programming)\n_**not**_\n[\"***Object Oriented***\"](https://en.wikipedia.org/wiki/Object-oriented_programming)\n),\nthere is no _functionality_ to test.\nWe are merely going to test for the \"shape\" of the data.\n\nIn the `test/todo-app.test.js` file, append following test code:\n\n```js\ntest('todo `model` (Object) has desired keys', function (t) {\n  const keys = Object.keys(app.model);\n  t.deepEqual(keys, ['todos', 'hash'], \"`todos` and `hash` keys are present.\");\n  t.true(Array.isArray(app.model.todos), \"model.todos is an Array\")\n  t.end();\n});\n```\n\nIf you _run_ this test in your terminal:\n```sh\nnode test/todo-app.test.js\n```\nYou should see _both_ assertions _fail_:\n![model-tests-failing](https://user-images.githubusercontent.com/194400/43508841-e8473e90-9568-11e8-85fd-6e0e30f244cb.png)\n\n\n\n#### `model` _Implementation_\n\nWrite the _minimum_ code required to _pass_ this test in `todo-app.js`.\ne.g:\n\n```js\n/**\n * initial_model is a simple JavaScript Object with two keys and no methods.\n * it is used both as the \"initial\" model when mounting the Todo List App\n * and as the \"reset\" state when all todos are deleted at once.\n */\nvar initial_model = {\n  todos: [], // empty array which we will fill shortly\n  hash: \"#/\" // the hash in the url (for routing)\n}\n\n/* module.exports is needed to run the functions using Node.js for testing! */\n/* istanbul ignore next */\nif (typeof module !== 'undefined' \u0026\u0026 module.exports) {\n  module.exports = {\n    model: initial_model\n  }\n}\n```\n\nOnce you save the `todo-app.js` file and re-run the tests.\n```sh\nnode test/todo-app.test.js\n```\nYou _should_ expect to see both assertions _passing_:\n![model-tests-passing](https://user-images.githubusercontent.com/194400/43508894-0df475cc-9569-11e8-8665-14320138ba79.png)\n\nWe're off to a _great_ start! Let's tackle some actual _functionality_ next!\n\n\u003cbr /\u003e\n\n### `update`\n\nThe `update` function is the\n[\"brain\"](https://www.youtube.com/results?search_query=Pinky+and+The+Brain)\nof the App.\n\n#### `update` JSDOC\n\nThe **`JSDOC`** for our `update` function is:\n```js\n/**\n * `update` transforms the `model` based on the `action`.\n * @param {String} action - the desired action to perform on the model.\n * @param {Object} model - the App's data (\"state\").\n * @return {Object} new_model - the transformed model.\n */\n```\n\n#### `update` Test \u003e `default case`\n\nAs with the `update` in our `counter` example\nthe function body is a `switch` statement\nthat \"decides\" how to handle a request based on the `action`\n(_also known as the \"message\"_).\n\nGiven that we _know_ that our `update` function \"skeleton\"\nwill be a `switch` statement\n(_because that is the \"TEA\" pattern_)\na good test to _start_ with is the `default case`.\n\nAppend the following test code in `test/todo-app.test.js`:\n\n```js\ntest('todo `update` default case should return model unmodified', function (t) {\n  const model = JSON.parse(JSON.stringify(app.model));\n  const unmodified_model = app.update('UNKNOWN_ACTION', model);\n  t.deepEqual(model, unmodified_model, \"model returned unmodified\");\n  t.end();\n});\n```\n\nIf you _run_ this test in your terminal:\n```sh\nnode test/todo-app.test.js\n```\nYou should see the assertion _fail_:\n![update-default-branch-test-failing](https://user-images.githubusercontent.com/194400/43580847-b78105c0-964e-11e8-81ac-61a1dd8ec535.png)\n\n#### `update` Function Implementation \u003e `default case`\n\nWrite the _minimum_ code necessary to pass the test.\n\n\u003e Yes, we could just write:\n\n```js\nfunction update (action, model) { return model; }\n```\n\nAnd that _would_ make the test _pass_. \u003cbr /\u003e\n\nBut, in light of the fact that we **know** the `update`\nfunction body will contain a `switch` statement,\nmake the test pass by returning the `model` _unmodified_ in the `default` case.\n\ne.g:\n```js\n/**\n * `update` transforms the `model` based on the `action`.\n * @param {String} action - the desired action to perform on the model.\n * @param {Object} model - the App's (current) model (or \"state\").\n * @return {Object} new_model - the transformed model.\n */\nfunction update(action, model) {\n switch (action) {                  // action (String) determines which case\n   default:                         // if action unrecognised or undefined,\n     return model;                  // return model unmodified\n }    // default? https://softwareengineering.stackexchange.com/a/201786/211301\n}\n```\n\nWhen you re-run the test(s) in your terminal:\n```sh\nnode test/todo-app.test.js\n```\nYou should see this assertion pass:\n![update-default-branch-test-passing](https://user-images.githubusercontent.com/194400/43581137-c6aa236e-964f-11e8-96d0-ef724659761e.png)\n\nNow that we have a _passing_ test\nfor the `default case` in our `update` function,\nwe can move on to\nthinking about the first (_and most fundamental_) piece\nof _functionality_ in the Todo List App: Adding an item to the list.\n\n\n### `ADD` an `item` to the Todo List\n\nThis is both the _first_ \"feature\" a \"user\" will encounter and\n_by_ far the most _used_ feature of a Todo List. \u003cbr /\u003e\n(_by **definition** people add more items to their list than they finish,\n  to finish everything we would have to_\n  [***live forever***!](https://youtu.be/TDe1DqxwJoc))\n\n#### `ADD` item _Acceptance Criteria_\n\nAdding a new todo item's text should\nappend the todo item `Object` to the `model.todos` Array. \u003cbr /\u003e\nSuch that the `model` is transformed (_data is added_) in the following way:\n\n_BEFORE_:\n```js\n{\n  todos: [],\n  hash: \"#/\"\n}\n```\n_AFTER_:\n```js\n{\n  todos: [\n    {id: 1, \"Add Todo List Item\", done: false }\n  ],\n  hash: \"#/\"\n}\n```\n\n#### Hold On, That Doesn't Seem \"_Right_\" How Does Todo Item _Text_ Get Added?\n\n![sotp-sign-fail](https://user-images.githubusercontent.com/194400/43678248-ba12f248-9807-11e8-8ebc-0afd8fd8bb0e.jpg)\n\nWhile considering the \"Acceptance Criteria\"\nfor adding an item to the Todo List,\nwe _notice_ that our `update` **`JSDOC`**\nand corresponding function \"signature\" (_defined above_) as:\n```js\n/**\n * `update` transforms the `model` based on the `action`.\n * @param {String} action - the desired action to perform on the model.\n * @param {Object} model - the App's (current) model (or \"state\").\n * @return {Object} updated_model - the transformed model.\n */\nfunction update(action, model) {\n switch (action) {                  // action (String) determines which case\n   default:                         // if action unrecognised or undefined,\n     return model;                  // return model unmodified\n }    // default? https://softwareengineering.stackexchange.com/a/201786/211301\n}\n```\ndoes not have a **parameter** for passing in the Todo List item Text (`title`),\ni.e. how do we add \"data\" to the `model`...?\n\n\nThat's \"_Oh kay_\"! (_don't panic_!) \u003cbr /\u003e\nIf we **`try`** to think about implementation up-front,\nwe would _invariably_ be \"over-thinking\" things\nand get \"stuck\" in the\n[\"analysis paralysis\"](https://en.wikipedia.org/wiki/Analysis_paralysis)\nof\n[\"***waterfall***\"](https://en.wikipedia.org/wiki/Waterfall_model)\n\nAs you are _about_ to see, we can _easily_ change the function signature,\nin the _next_ test _without affecting_ our exiting (_passing_) test!\n\nAs you _practice_ \"DDD\" \u0026 \"TDD\" you will begin to _appreciate_\nand even _embrace_ the _mental agility_ that comes from\n_not_ \"over-thinking\" things.\n\nWhenever you encounter a \"New Requirement\"\n(_or realise that you didn't **fully consider** the **original requirements**_),\nyou know that your _suite_ of tests has\n\"\n[got your](https://www.urbandictionary.com/define.php?term=Got%20your%20back)\n[back](https://youtu.be/gk2yOxTuLck)\n\". \u003cbr /\u003e\nYou can \"_refactor_\" a function's _implementation_ to your heart's content,\nsafe in the knowledge that all your _existing_ tests still pass.\ni.e. the _rest_ of the app \"**still works**\" **_exactly_ as expected**.\n\nWe don't want to \"mess with\" either of the other two (_existing_) parameters,\nboth `action` and `model` have clearly defined purposes,\nbut we _need_ a way to pass \"data\" into the `update` function!\n\nWith that in mind, let's _amend_ the `update` **`JSDOC`** comment\nand function signature to:\n\n```js\n/**\n * `update` transforms the `model` based on the `action`.\n * @param {String} action - the desired action to perform on the model.\n * @param {Object} model - the App's (current) model (or \"state\").\n * @param {String} data - data we want to \"apply\" to the item. e.g: item Title.\n * @return {Object} updated_model - the transformed model.\n */\nfunction update(action, model, data) {\n  switch (action) {                  // action (String) determines which case\n    default:                         // if action unrecognised or undefined,\n      return model;                  // return model unmodified\n  }    // default? https://softwareengineering.stackexchange.com/a/201786/211301\n}\n```\n\nWithout making _any_ other changes, re-run the tests:\n\n```sh\nnode test/todo-app.test.js\n```\n_Everything_ should still pass:\n![update-default-branch-test-passing](https://user-images.githubusercontent.com/194400/43581137-c6aa236e-964f-11e8-96d0-ef724659761e.png)\n\nCongratulations! You just _extended_ a function (_signature_)\nwithout affecting any _existing_ tests.\n\n\n\n#### `ADD` item _Test_\n\nAppend the following test code to your `test/todo-app.test.js` file:\n\n```js\ntest('`ADD` a new todo item to model.todos Array via `update`', function (t) {\n  const model = JSON.parse(JSON.stringify(app.model)); // initial state\n  t.equal(model.todos.length, 0, \"initial model.todos.length is 0\");\n  const updated_model = app.update('ADD', model, \"Add Todo List Item\");\n  const expected = { id: 1, title: \"Add Todo List Item\", done: false };\n  t.equal(updated_model.todos.length, 1, \"updated_model.todos.length is 1\");\n  t.deepEqual(updated_model.todos[0], expected, \"Todo list item added.\");\n  t.end();\n});\n```\n\nIf you _run_ this test in your terminal:\n```sh\nnode test/todo-app.test.js\n```\nYou should see the assertion _fail_:\n\n![update-add-item-test-failing](https://user-images.githubusercontent.com/194400/43639131-206b632c-9713-11e8-83ee-d0ecab0ac4ef.png)\n\n\n#### `ADD` item _Implementation_\n\nWith the above test as your \"guide\",\nwrite the _bare minimum_ code necessary to make all assertions pass.\n\n_Sample_ implementation:\n```js\n/**\n * `update` transforms the `model` based on the `action`.\n * @param {String} action - the desired action to perform on the model.\n * @param {Object} model - the App's (current) model (or \"state\").\n * @param {String} data - the data we want to \"apply\" to the item.\n * @return {Object} updated_model - the transformed model.\n */\nfunction update(action, model, data) {\n  var new_model = JSON.parse(JSON.stringify(model)) // \"clone\" the model\n  switch(action) {                   // and an action (String) runs a switch\n    case 'ADD':\n      new_model.todos.push({\n        id: model.todos.length + 1,\n        title: data,\n        done: false\n      });\n      break;\n    default: // if action unrecognised or undefined,\n      return model; // return model unmodified\n  }   // see: https://softwareengineering.stackexchange.com/a/201786/211301\n  return new_model;\n}\n```\nthe `case 'ADD'` is the _relevant_ code. \u003cbr /\u003e\n\n\u003e Was _your_ implementation _similar_...? \u003cbr /\u003e\n\u003e If you were able to make it _simpler_,\n[please share!](https://github.com/dwyl/learn-elm-architecture-in-javascript/issues/48)\n\nOnce you have the test(s) _passing_ e.g:\n![todo-add-item-tests-passing](https://user-images.githubusercontent.com/194400/43678110-2688ea7a-9805-11e8-9003-97b5450d0cf1.png)\n\nLet's move on to the _next_ functionality!\n\n\u003cbr /\u003e\n\n### `TOGGLE` a Todo `item` to `done=true`\n\n![todomvc-two-items-1-done](https://user-images.githubusercontent.com/194400/43686242-150d8f66-98ba-11e8-9f63-df7523666fd8.png)\n\nChecking off a todo item involves changing the value of the `done` property\nfrom `false` to `true`. e.g:\n\n_FROM_:\n```js\n{\n  todos: [\n    {id: 1, \"Toggle a todo list item\", done: false }\n  ]\n}\n```\n_TO_:\n```js\n{\n  todos: [\n    {id: 1, \"Toggle a todo list item\", done: true }\n  ]\n}\n```\n\nGiven that we have already defined our `update` function above,\nwe can dive straight into writing a _test_:\n\n\n#### `TOGGLE` item _Test_\n\nAppend the following test code to your `test/todo-app.test.js` file:\n\n```js\ntest('`TOGGLE` a todo item from done=false to done=true', function (t) {\n  const model = JSON.parse(JSON.stringify(app.model)); // initial state\n  const model_with_todo = app.update('ADD', model, \"Toggle a todo list item\");\n  const item = model_with_todo.todos[0];\n  const model_todo_done = app.update('TOGGLE', model_with_todo, item.id);\n  const expected = { id: 1, title: \"Toggle a todo list item\", done: true };\n  t.deepEqual(model_todo_done.todos[0], expected, \"Todo list item Toggled.\");\n  t.end();\n});\n```\n_execute_ the test:\n```sh\nnode test/todo-app.test.js\n```\nYou should see something _similar_ to the following:\n![toggle-todo-list-item](https://user-images.githubusercontent.com/194400/43686329-8cdffc12-98bb-11e8-9b04-5d2ef2dc54a3.png)\n\n\n#### `TOGGLE` item _Implementation_\n\nWith the above test as your \"guide\",\nwrite the _minimum_ code necessary to make the test pass.\n(_ensure that you continue to make a \"copy\" of the `model`\nrather than \"mutate\" it_)\n\nOnce you make it _pass_ you should see:\n\n![todo-item-toggled](https://user-images.githubusercontent.com/194400/43686401-fcdd417c-98bc-11e8-8766-2b967b6e4481.png)\n\n\u003e Try to make the test pass alone (or with your pairing partner).\nIf you get \"stuck\" see: [**`todo-app.js`**](https://github.com/dwyl/learn-elm-architecture-in-javascript/pull/45/commits/5d4ebc546101efe05644a05833d73caec77c32ae)\n\n\n#### Hold On, Does This Work _Both_ Ways?\n\n_Yes_, you _guessed_ it!\nChoosing to name the `action` as \"`TOGGLE`\"\nis _precisely_ because we don't _need_\nto have a _**separate**_ function\nto \"undo\" an item if it has been \"checked off\".\n\nAppend the following test code to your `test/todo-app.test.js` file:\n\n```js\ntest('`TOGGLE` (undo) a todo item from done=true to done=false', function (t) {\n  const model = JSON.parse(JSON.stringify(app.model)); // initial state\n  const model_with_todo = app.update('ADD', model, \"Toggle a todo list item\");\n  const item = model_with_todo.todos[0];\n  const model_todo_done = app.update('TOGGLE', model_with_todo, item.id);\n  const expected = { id: 1, title: \"Toggle a todo list item\", done: true };\n  t.deepEqual(model_todo_done.todos[0], expected, \"Toggled done=false \u003e\u003e true\");\n  // add another item before \"undoing\" the original one:\n  const model_second_item = app.update('ADD', model_todo_done, \"Another todo\");\n  t.equal(model_second_item.todos.length, 2, \"there are TWO todo items\");\n  // Toggle the original item such that: done=true \u003e\u003e done=false\n  const model_todo_undone = app.update('TOGGLE', model_second_item, item.id);\n  const undone = { id: 1, title: \"Toggle a todo list item\", done: false };\n  t.deepEqual(model_todo_undone.todos[0],undone, \"Todo item Toggled \u003e undone!\");\n  t.end();\n});\n```\n\nYou should not _need_ to modify any of the code in the `update` function.\nThe above test should just _pass_ based on the code you wrote above.\nIf it does _not_, then _revise_ your implementation\nof the `TOGGLE case` in `update` until _all_ tests pass:\n\n![undo-a-todo-item](https://user-images.githubusercontent.com/194400/43686533-b25d4608-98bf-11e8-809e-1153fcfb1db1.png)\n\n### `view` Function\n\nIt won't have \"_escaped_\" you that _so far_ we have not written _any_ code\nthat a _user_ can actually _interact_ with.\n\n_So far_ we have _successfully_ added two `case` blocks in the `switch` statement\nof our `update` function. We now have the two _basic_ functions required\nto both `ADD` a new todo list item to the `model.todos` Array\n_and_ check-off a todo list item as \"done\" using the `TOGGLE action`.\nThis is \"_enough_\" functionality to start _using_ the todo list (_ourselves_)\nand **UX-testing** it with _prospective_ \"***users***\".\n\nIf you followed through the \"Elm(ish)\" tutorial\n[`elmish.md`](https://github.com/dwyl/learn-elm-architecture-in-javascript/blob/main/elmish.md)\nyou will have seen that we created a _sample_ `view` in the last few _tests_\nto \"_exercise_\" the DOM element creation functions.\nThis means that we _already know_ how to build a `view` for our Todo List App!\nWe \"_just_\" need to _adapt_ the `view` we made in `Elm`(_ish_) to display\nthe data in our `model`.\n\n#### Sample `model` to Render in Our `view`\n\nLet's return to the sample `model` from above:\n\n```js\n{\n  todos: [\n    { id: 1, title: \"Learn Elm Architecture\", done: true },\n    { id: 2, title: \"Build Todo List App\",    done: false },\n    { id: 3, title: \"Win the Internet!\",      done: false }\n  ],\n  hash: '#/' // the \"route\" to display\n}\n```\nThe model contains _three_ items in the `todos` Array. \u003cbr /\u003e\nThe first is complete (`done=true`)\nwhereas the second and third items are still \"todo\" (`done=false`).\n\nThis is what this `model` looks like in the \"VanillaJS\"\nTodoMVC:\n\n![todomvc-3-items-1-done](https://user-images.githubusercontent.com/194400/43689907-e9caa548-98f8-11e8-8fd1-7b63e7fc5e30.png)\n\nOur _quest_ in the next \"pomodoro\" is to re-create this\nusing the DOM functions we created in `Elm`(_ish_)!\n\n#### Focus on Rendering The _List_ First\n\nFor now, _ignore_ the `\u003cfooter\u003e` (_below the Todo List_)\nand _just_ focus on rendering the _list_ itself.\n\n![todomvc-3-items-1-done](https://user-images.githubusercontent.com/194400/43690122-b72bcb0e-98fc-11e8-83c2-8b8703b177ed.png)\n\n\nIn your web browser, open **Dev**eloper **Tools**\nand _inspect_ the HTML for the Todo list:\nhttps://todomvc.com/examples/vanillajs/\n\n![todomvc-main-section-todo-list-html](https://user-images.githubusercontent.com/194400/43717480-9fb80982-997f-11e8-9ffe-6aa90a89a042.png)\n\nThis is the HTML copied directly from the browser:\n```html\n\u003csection class=\"main\" style=\"display: block;\"\u003e\n  \u003cinput class=\"toggle-all\" type=\"checkbox\"\u003e\n  \u003clabel for=\"toggle-all\"\u003eMark all as complete\u003c/label\u003e\n  \u003cul class=\"todo-list\"\u003e\n    \u003cli data-id=\"1533501855500\" class=\"completed\"\u003e\n      \u003cdiv class=\"view\"\u003e\n        \u003cinput class=\"toggle\" type=\"checkbox\"\u003e\n        \u003clabel\u003eLearn Elm Architecture\u003c/label\u003e\n        \u003cbutton class=\"destroy\"\u003e\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/li\u003e\n    \u003cli data-id=\"1533501861171\" class=\"\"\u003e\n      \u003cdiv class=\"view\"\u003e\n        \u003cinput class=\"toggle\" type=\"checkbox\"\u003e\n        \u003clabel\u003eBuild Todo List App\u003c/label\u003e\n        \u003cbutton class=\"destroy\"\u003e\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/li\u003e\n    \u003cli data-id=\"1533501867123\" class=\"\"\u003e\n      \u003cdiv class=\"view\"\u003e\u003cinput class=\"toggle\" type=\"checkbox\"\u003e\n        \u003clabel\u003eWin the Internet!\u003c/label\u003e\n        \u003cbutton class=\"destroy\"\u003e\u003c/button\u003e\n      \u003c/div\u003e\n    \u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/section\u003e\n```\n\u003e _**Note**: there is \"redundant\" markup in this HTML in the form of a `\u003cdiv\u003e`\ninside the `\u003cli\u003e`, for now we are just replicating the HTML \"faithfully\",\nwe can \"prune\" it later._\n\nFrom this HTMl we can write our\n[\"**_Technical_ Acceptance Criteria**\"](https://github.com/dwyl/learn-elm-architecture-in-javascript/issues/51):\n\n+ [ ] Todo List items should be displayed as list items **`\u003cli\u003e`**\nin an _unordered list_ **`\u003cul\u003e`**.\n+ [ ] Each Todo List item **`\u003cli\u003e`**  should contain a **`\u003cdiv\u003e`**\nwith a **`class=\"view\"`** which \"wraps\":\n  + [ ] **`\u003cinput class=\"toggle\" type=\"checkbox\"\u003e`** - the \"checkbox\"\n  that people can \"Toggle\" to change the \"state\"\n  of the Todo item from \"active\" to \"done\"\n  (_which updates the model\n    From: `model.todos[id].done=false`\n    To: `model.todos[id].done=true`_)\n  + [ ] **`\u003clabel\u003e`** - the text content (\"title\") of the todo list item\n  + [ ] **`\u003cbutton class=\"destroy\"\u003e`** - the button the person\n  can click/tap to **`delete`** a Todo item.\n\n\n### Todo List `view` Test Assertions\n\nGiven the `model` (_above_),\n+ [ ] There is a `\u003cul class=\"todo-list\"\u003e` with 3 **`\u003cli\u003e`** (_list items_)\nrendered in the `view`.\n+ [ ] The ***first*** **`\u003cli\u003e`** has an **`\u003cinput type=\"checkbox\"\u003e`**\nwhich is _checked_ (`done=true`)\n+ [ ] The ***remaining*** **`\u003cli\u003e's`** have **`\u003cinput type=\"checkbox\"\u003e`**\nthat are _unchecked_ (`done=false`)\n\nLet's \"tackle\" the _first_ assertion _first_:\n\n#### Render a _Single_ Todo List Item Using `render_list` Test\n\nIt's _always_ a good idea to \"break apart\" a test into smaller tests\nbecause it means we will write smaller\n(_and thus **more maintainable**_) \"_composable_\" functions.\nWith that in mind, let's add the following _test_ to `test/todo-app.test.js`:\n\n```js\ntest.only('render_item HTML for a single Todo Item', function (t) {\n  const model = {\n    todos: [\n      { id: 1, title: \"Learn Elm Architecture\", done: true },\n    ],\n    hash: '#/' // the \"route\" to display\n  };\n  // render the ONE todo list item:\n  document.getElementById(id).appendChild(app.render_item(model.todos[0]))\n\n  const done = document.querySelectorAll('.completed')[0].textContent;\n  t.equal(done, 'Learn Elm Architecture', 'Done: Learn \"TEA\"');\n\n  const checked = document.querySelectorAll('input')[0].checked;\n  t.equal(checked, true, 'Done: ' + model.todos[0].title + \" is done=true\");\n\n  elmish.empty(document.getElementById(id)); // clear DOM ready for next test\n  t.end();\n});\n```\n\nAfter saving the `test/todo-app.test.js` file, if you attempt to run it:\n```sh\nnode test/todo-app.test.js\n```\nyou will see something like this:\n\n![render_item-test-failing](https://user-images.githubusercontent.com/194400/43743931-b397cd7a-99cf-11e8-81a6-3218207ca05b.png)\n\n#### `render_list` Implementation\n\nGiven the test above, I added the following code to my `todo-app.js` file:\n\n```js\n/* if require is available, it means we are in Node.js Land i.e. testing! */\n/* istanbul ignore next */\nif (typeof require !== 'undefined' \u0026\u0026 this.window !== this) {\n  var { a, button, div, empty, footer, input, h1, header, label, li, mount,\n    route, section, span, strong, text, ul } = require('./elmish.js');\n}\n\n/**\n * `render_item` creates an DOM \"tree\" with a single Todo List Item\n * using the \"elmish\" DOM functions (`li`, `div`, `input`, `label` and `button`)\n * returns an `\u003cli\u003e` HTML element with a nested `\u003cdiv\u003e` which in turn has the:\n *   `\u003cinput type=checkbox\u003e` which lets users to \"Toggle\" the status of the item\n *   `\u003clabel\u003e` which displays the Todo item text (`title`) in a `\u003ctext\u003e` node\n *   `\u003cbutton class=\"destroy\"\u003e` lets people \"delete\" a todo item.\n * see: https://github.com/dwyl/learn-elm-architecture-in-javascript/issues/52\n * @param  {Object} item the todo item object\n * @return {Object} \u003cli\u003e DOM Tree which is nested in the \u003cul\u003e.\n * @example\n * // returns \u003cli\u003e DOM element with \u003cdiv\u003e, \u003cinput\u003e. \u003clabel\u003e \u0026 \u003cbutton\u003e nested\n * var DOM = render_item({id: 1, title: \"Build Todo List App\", done: false});\n */\nfunction render_item(item) {\n  return (\n    li([\n      \"data-id=\" + item.id,\n      \"id=\" + item.id,\n      item.done ? \"class=completed\" : \"\"\n    ], [\n      div([\"class=view\"], [\n        input([\"class=toggle\", \"type=checkbox\",\n          (item.done ? \"checked=true\" : \"\")], []),\n        label([], [text(item.title)]),\n        button([\"class=destroy\"])\n      ]) // \u003c/div\u003e\n    ]) // \u003c/li\u003e\n  )\n}\n```\nAdd the `render_item` to the `module.exports` at the end of the file:\n```js\nif (typeof module !== 'undefined' \u0026\u0026 module.exports) {\n  module.exports = {\n    model: initial_model,\n    update: update,\n    render_item: render_item, // export so that we can unit test\n  }\n}\n```\n\nThis will make the test pass:\n![image](https://user-images.githubusercontent.com/194400/43762133-f6c21de0-9a1e-11e8-871d-e6f5b86d1d55.png)\n\n\nNow that we have a `render_item` function\nthat renders a _single_ `\u003cli\u003e` (_todo list item_),\nwe can create another function which _uses_ the `render_item` in a \"loop\",\nto create _several_ `\u003cli\u003e` nested in a `\u003cul\u003e`.\n\n#### `render_main` Test\n\nAppend the following test code to your `test/todo-app.test.js` file:\n\n```js\ntest('render \"main\" view using (elmish) HTML DOM functions', function (t) {\n  const model = {\n    todos: [\n      { id: 1, title: \"Learn Elm Architecture\", done: true },\n      { id: 2, title: \"Build Todo List App\",    done: false },\n      { id: 3, title: \"Win the Internet!\",      done: false }\n    ],\n    hash: '#/' // the \"route\" to display\n  };\n  // render the \"main\" view and append it to the DOM inside the `test-app` node:\n  document.getElementById(id).appendChild(app.render_main(model));\n  // test that the title text in the model.todos was rendered to \u003clabel\u003e nodes:\n  document.querySelectorAll('.view').forEach(function (item, index) {\n    t.equal(item.textContent, model.todos[index].title,\n      \"index #\" + index + \" \u003clabel\u003e text: \" + item.textContent)\n  })\n\n  const inputs = document.querySelectorAll('input'); // todo items are 1,2,3\n  [true, false, false].forEach(function(state, index){\n    t.equal(inputs[index + 1].checked, state,\n      \"Todo #\" + index + \" is done=\" + state)\n  })\n  elmish.empty(document.getElementById(id)); // clear DOM ready for next test\n  t.end();\n});\n```\n\nIf you attempt to run this test:\n```sh\nnode test/todo-app.test.js\n```\n\nyou will see something like this:\n![main-test-failing](https://user-images.githubusercontent.com/194400/43741630-f03f1fe8-99c6-11e8-8b7b-e44ee397b38e.png)\n\n\nGiven your knowledge of implementing the `render_item` function above,\nand your skills with JavaScript loops, create your `render_main` function,\nto make the tests pass.\n\n\u003e If you get \"stuck\" there is a _reference_ implementation in:\n[**`todo-app.js`**](https://github.com/dwyl/learn-elm-architecture-in-javascript/pull/45/commits/b6607478c3dbed048781932261af2981f4c6c405#diff-6be3e16fe7cfb4c00788d4d587374afdR76)\n\nAll our tests pass _and_ we have **100% test coverage**:\n\n![render_main-tests-pass-100-coverage](https://user-images.githubusercontent.com/194400/43766409-4189ce4e-9a2a-11e8-8d73-3ea636b22928.png)\n\nThis means we are writing the \"_bare minimum_\" code necessary\nto meet all acceptance criteria (_requirements_),\nwhich is _both **faster** and **more maintainable**_! \u003cbr /\u003e\nOnwards!\n\n\n\u003cbr /\u003e\n\n\n### `\u003cfooter\u003e` Element [issues/53](https://github.com/dwyl/learn-elm-architecture-in-javascript/issues/53)\n\nReferring again to the _rendered_ HTML\non https://todomvc.com/examples/vanillajs as our \"guide\":\n\n![footer-screenshot](https://user-images.githubusercontent.com/194400/42633421-5eb20f24-85d8-11e8-94ad-bb653dd93ab0.png)\n\n####  Dev Tools \u003e Elements (inspector)\n\n![todo-list-mvc-](https://user-images.githubusercontent.com/194400/43768735-f1f7798e-9a2f-11e8-9f73-c69ea63b1064.png)\n\n#### Copy-paste the _rendered_ HTML\n\n\"_copy-pasted_\" of the _rendered_ HTML from the Dev Tools:\n![todo-list-mvc-copy-html](https://user-images.githubusercontent.com/194400/43769759-6f18ca4c-9a32-11e8-8f96-7b19ed364c07.png)\n\n```html\n\u003cfooter class=\"footer\" style=\"display: block;\"\u003e\n  \u003cspan class=\"todo-count\"\u003e\n    \u003cstrong\u003e2\u003c/strong\u003e items left\n  \u003c/span\u003e\n  \u003cul class=\"filters\"\u003e\n    \u003cli\u003e\n      \u003ca href=\"#/\" class=\"selected\"\u003eAll\u003c/a\u003e\n    \u003c/li\u003e\n    \u003cli\u003e\n      \u003ca href=\"#/active\"\u003eActive\u003c/a\u003e\n    \u003c/li\u003e\n    \u003cli\u003e\n      \u003ca href=\"#/completed\"\u003eCompleted\u003c/a\u003e\n    \u003c/li\u003e\n  \u003c/ul\u003e\n  \u003cbutton class=\"clear-completed\" style=\"display: block;\"\u003e\n    Clear completed\n  \u003c/button\u003e\n\u003c/footer\u003e\n```\n\n#### Technical Acceptance Criteria\n\nThese are the criteria (_checklist_) as described in [issues/53](https://github.com/dwyl/learn-elm-architecture-in-javascript/issues/53):\n\n+ [ ] **`render_footer`** returns a  **`\u003cfooter\u003e`** DOM element which can be rendered directly to the `document` or _nested_ in another DOM element.\n+ [ ]  **`\u003cfooter\u003e`** contains:\n  + [ ] **`\u003cspan class=\"todo-count\"\u003e`** which contains\n    + [ ] a **`text`** node with: **\"`{count}` item(s) left\"**.\n   _pseudocode_:\n  `{model.todos.filter( (i) =\u003e { i.done==false })}`\n    item`{model.todos.length \u003e 1 ? 's' : '' }` left\n  + [ ] **`\u003cul\u003e`** containing 3 **`\u003cli\u003e`** with the following links (**`\u003ca\u003e`**):\n    + [ ] Show **`All`**: **`\u003ca href=\"#/\" class=\"selected\"\u003eAll\u003c/a\u003e`**\n      + [ ] **`class=\"selected\"`** should only appear on the selected menu/navigation item.\n       this should be \"driven\" by the `model.hash` property.\n    + [ ] Show **`Active`**: **` \u003ca href=\"#/active\"\u003eActive\u003c/a\u003e`**\n    + [ ] Show **`Completed`**: **`\u003ca href=\"#/completed\"\u003eCompleted\u003c/a\u003e`**\n  + [ ] **`\u003cbutton class=\"clear-completed\" style=\"display: block;\"\u003e`**\n    will **_Clear_ all `Completed`** items.\n    _sample code_: \u003cbr /\u003e\n`new_model.todos =  model.todos.filter(function(item) { return item.done === false })`\n\n#### _Estimate_ Time Required to Write `render_footer` Function\n\n\"_armed_\" with the acceptance criteria _checklist_\nand the\n[\"***informative prior***\"](https://en.wikipedia.org/wiki/Prior_probability#Informative_priors)\n(_the **experience** we have **already** gained_)\nfrom building the previous view functions\n**`render_item`** and **`render_main`**\nwe ***estimate*** with _reasonable confidence_\nthat it will take us\n**25 minutes** (_**one** \"**pomodoro**_)\nto:\n+ [ ] Craft the **`JSDOC`** comment _documenting_ the `render_footer` function\nso that all future developers will _easily_ understand what the function does.\n+ [ ] Write a (unit) **test** covering the acceptance criteria (_test first!_)\n+ [ ] Write the (_bare minimum_) code to ***pass*** the test assertions.\n\n\u003e _**Note On Time Estimates**: if it takes **longer** than **25 mins** \"budget\",\n**don't panic** or feel like you have \"failed\",\nit's not a \"problem\" ...\nit's just \"**more data**\" (knowledge/experience)\nthat you can incorporate into improving **future estimates**!\nover time you will get **really good** at estimating,\nthis is just a **starting point**_\n\n#### `render_footer` `JSDOC` Comment Documentation\n\nHere is a sample comment which documents the **`render_footer`** function:\n\n```js\n/**\n * `render_footer` renders the `\u003cfooter class=\"footer\"\u003e` of the Todo List App\n * which contains count of items to (still) to be done and a `\u003cul\u003e` \"menu\"\n * with links to filter which todo items appear in the list view.\n * @param {Object} model - the App's (current) model (or \"state\").\n * @return {Object} \u003csection\u003e DOM Tree which containing the \u003cfooter\u003e element.\n * @example\n * // returns \u003cfooter\u003e DOM element with other DOM elements nested:\n * var DOM = render_footer(model);\n */\n```\n\nWrite your _own_ JSDOC or add these lines to your **`todo-app.js`** file.\n\n#### `render_footer` Test\n\nHere is a sample test you can add to your `test/todo-app.test.js` file:\n(_if you feel confident in your TDD skills,\n  you could **`try`** to write your own test/assertions..._)\n\n```js\ntest.only('render_footer view using (elmish) HTML DOM functions', function (t) {\n  const model = {\n    todos: [\n      { id: 1, title: \"Learn Elm Architecture\", done: true },\n      { id: 2, title: \"Build Todo List App\",    done: false },\n      { id: 3, title: \"Win the Internet!\",      done: false }\n    ],\n    hash: '#/' // the \"route\" to display\n  };\n  // render_footer view and append it to the DOM inside the `test-app` node:\n  document.getElementById(id).appendChild(app.render_footer(model));\n\n  // todo-count should display 2 items left (still to be done):\n  const left = document.getElementById('count').innerHTML;\n  t.equal(left, \"\u003cstrong\u003e2\u003c/strong\u003e items left\", \"Todos remaining: \" + left);\n\n  // count number of footer \u003cli\u003e items:\n  t.equal(document.querySelectorAll('li').length, 3, \"3 \u003cli\u003e in \u003cfooter\u003e\");\n\n  // check footer link text and href:\n  const link_text = ['All', 'Active', 'Completed'];\n  const hrefs = ['#/', '#/active', '#/completed'];\n  document.querySelectorAll('a').forEach(function (a, index) {\n    // check link text:\n    t.equal(a.textContent, link_text[index], \"\u003cfooter\u003e link #\" + index\n      + \" is: \" + a.textContent + \" === \" + link_text[index]);\n    // check hrefs:\n    t.equal(a.href.replace('about:blank', ''), hrefs[index],\n    \"\u003cfooter\u003e link #\" + index + \" href is: \" + hrefs[index]);\n  });\n\n  // check for \"Clear completed\" button in footer:\n  const clear = document.querySelectorAll('.clear-completed')[0].textContent;\n  t.equal(clear, 'Clear completed', '\u003cbutton\u003e in \u003cfooter\u003e \"Clear completed\"');\n\n  elmish.empty(document.getElementById(id)); // clear DOM ready for next test\n  t.end();\n});\n```\n\nRun this test:\n```sh\nnode test/todo-app.test.js\n```\n\nyou will see something like this:\n![render_footer-test-failing](https://user-images.githubusercontent.com/194400/43774185-3be99666-9a40-11e8-9387-f172f95dd80b.png)\n\n#### `render_footer` Implementation\n\nGiven the docs and test above, attempt to write the `render_footer` function.\n\n\u003e _**Note**: for now we are **not** \"concerned\"\nwith what happens when the \"Clear completed\" **`\u003cbuton\u003e`** is clicked/tapped.\nWe will \"cover\" that below. For now, focus on rendering the DOM._\n\n\u003e If you get \"stuck\" trying to make the tests pass, first keep trying! \u003cbr /\u003e\nThen \"ask a friend\" and finally, consult the _reference_ implementation in:\n[**`todo-app.js`**](https://github.com/dwyl/learn-elm-architecture-in-javascript/pull/45/commits/68e2afa3bd95c46da7df559007d90dedcbae500f#diff-6be3e16fe7cfb4c00788d4d587374afdR103)\n\n\nFor good measure, we add a _second_ test to check our \"pluarisation\":\n\n```js\ntest('render_footer 1 item left (pluarisation test)', function (t) {\n  const model = {\n    todos: [\n      { id: 1, title: \"Be excellent to each other!\", done: false }\n    ],\n    hash: '#/' // the \"route\" to display\n  };\n  // render_footer view and append it to the DOM inside the `test-app` node:\n  document.getElementById(id).appendChild(app.render_footer(model));\n\n  // todo-count should display \"1 item left\" (still to be done):\n  const left = document.getElementById('count').innerHTML;\n  t.equal(left, \"\u003cstrong\u003e1\u003c/strong\u003e item left\", \"Todos remaining: \" + left);\n\n  elmish.empty(document.getElementById(id)); // clear DOM ready for next test\n  t.end();\n});\n```\nThis test _should_ pass without any further code needing to be written.\n\nOnce you have written the code to pass the tests,\nyou should see something like this:\n\n![render_footer-tests-passing-coverage-100percent](https://user-images.githubusercontent.com/194400/43776336-6a3b8fe0-9a47-11e8-8155-deb0fcf44e5a.png)\n\n\n### `view` Function\n\nNow that we have the individual (\"_lower order_\") functions\n**`render_main`**\n[#51](https://github.com/dwyl/learn-elm-architecture-in-javascript/issues/51),\n**`render_item`**\n[#52](https://github.com/dwyl/learn-elm-architecture-in-javascript/issues/52),\nand **`render_footer`**\n[#53](https://github.com/dwyl/learn-elm-architecture-in-javascript/issues/53)\nfor rendering the _sections_ of the todo app,\nwe can write the `view` function to render the _entire_ app!\n\nWith the `main` and `footer` \"_partial_\" views built,\nthe overall **`view`** is quite simple:\n\n![todoapp-view](https://user-images.githubusercontent.com/194400/43779964-6fb92176-9a51-11e8-8b78-64c60242990d.png)\n\nTo save on repetition, and illustrate just how _simple_\nthe **`view`** is,\nthis is the \"HTML\" with the\n**`\u003csection class\"main\"\u003e`** and **`\u003cfooter class=\"footer\"\u003e`**\npartials replaced by invocations\nto the respective functions\n**`render_main`** and **`render_footer`**:\n\n```html\n\u003csection class=\"todoapp\"\u003e\n  \u003cheader class=\"header\"\u003e\n    \u003ch1\u003etodos\u003c/h1\u003e\n    \u003cinput class=\"new-todo\" placeholder=\"What needs to be done?\" autofocus=\"\"\u003e\n  \u003c/header\u003e\n  render_main(model)\n  render_footer(model)\n\u003c/section\u003e\n```\n\n#### `view` Acceptance Criteria\n\nThe `view` displays:\n+ [ ] **`\u003csection class=\"todo-app\"\u003e`** inside which the app is rendered.\n+ [ ] **`\u003ch1\u003e`** containing the title text \"**todos**\".\n+ [ ] **`\u003cinput class=\"new-todo\"\u003e`**\n  has placeholder text **\"What needs to be done?\"**\n+ [ ] **`\u003cul class=\"todo-list\"\u003e`** list of todo items\n  has `zero` items by default (_based on the `initial_model`_)\n+ [ ] `\u003cfooter\u003e` count is Zero when the app is first\n  rendered with no todos in the `model`.\n\n#### `view` JSDOC Comment Documentation\n\nHere is a sample JSDOC comment you can add to your **`todo-app.js`** file:\n\n```js\n/**\n * `view` renders the entire Todo List App\n * which contains count of items to (still) to be done and a `\u003cul\u003e` \"menu\"\n * with links to filter which todo items appear in the list view.\n * @param {Object} model - the App's (current) model (or \"state\").\n * @return {Object} \u003csection\u003e DOM Tree which containing all other DOM elements.\n * @example\n * // returns \u003csection class=\"todo-app\"\u003e DOM element with other DOM els nested:\n * var DOM = view(model);\n */\n```\nThese should be pretty familiar to you by now.\nIf you feel comfortable extending it with more detail, go for it!\n\n#### `view` _Tests_\n\nA sample test for the `view` function\nyou can add to your `test/todo-app.test.js` file:\n(_if you feel confident in your TDD skills,\n  you could **`try`** to write your own test/assertions..._)\n\n```js\ntest.only('view renders the whole todo app using \"partials\"', function (t) {\n  // render the view and append it to the DOM inside the `test-app` node:\n  document.getElementById(id).appendChild(app.view(app.model)); // initial_model\n\n  t.equal(document.querySelectorAll('h1')[0].textContent, \"todos\", \"\u003ch1\u003etodos\");\n  // placeholder:\n  const placeholder = document.getElementById('new-todo')\n    .getAttribute(\"placeholder\");\n  t.equal(placeholder, \"What needs to be done?\", \"paceholder set on \u003cinput\u003e\");\n\n  // todo-count should display \"0 items left\" (based on initial_model):\n  const left = document.getElementById('count').innerHTML;\n  t.equal(left, \"\u003cstrong\u003e0\u003c/strong\u003e items left\", \"Todos remaining: \" + left);\n\n  elmish.empty(document.getElementById(id)); // clear DOM ready for next test\n  t.end();\n});\n```\n\nRun this test:\n```sh\nnode test/todo-app.test.js\n```\n\nyou will see something like this (\"_Red_\"):\n![app.view-not-a-function](https://user-images.githubusercontent.com/194400/43782111-721805c2-9a56-11e8-970b-681b1499b3a8.png)\n\n#### `view` Function _Implementation_\n\nYou should have the knowledge \u0026 skill\nto write the `view` function and make the test pass.\n\n\u003e If you get \"stuck\" trying to make the tests pass, first keep trying! \u003cbr /\u003e\nThen \"ask a friend\" and finally, consult the _reference_ implementation in:\n[**`todo-app.js`**](https://github.com/dwyl/learn-elm-architecture-in-javascript/pull/45/commits/3096d81a777392c07a132136db496224871ff4c9#diff-6be3e16fe7cfb4c00788d4d587374afdR145)\n\nWhen you run `npm test` you should see something like this:\n![image](https://user-images.githubusercontent.com/194400/43782895-48496f22-9a58-11e8-9fde-dbb5554f43a0.png)\n\n\n## Checkpoint!\n\nSo far we have made a _lot_ of progress with our Todo List App _quest_,\n_however_ if we were to _stop_ working on this _now_ we would have\n_nothing_ to show a \"user\".\nUsers can't _interact_ with functions,\neven those with _great_ test coverage!\n\nWhat we _need_ is to start putting all the pieces together\ninto a functioning app!\n\n### Mount the App in `index.html`\n\nOpen your **`index.html`** file\nand ensure that the following lines are in the **`\u003cbody\u003e`**:\n\n```html\n\u003cbody\u003e\n  \u003cdiv id=\"app\"\u003e\u003c/div\u003e\n  \u003c!-- CSS Styles are 100% optional. but they make it look *much* nicer --\u003e\n  \u003clink rel=\"stylesheet\" href=\"todomvc-common-base.css\"\u003e\n  \u003clink rel=\"stylesheet\" href=\"todomvc-app.css\"\u003e\n\n  \u003cscript src=\"elmish.js\"\u003e\u003c/script\u003e\n  \u003cscript src=\"todo-app.js\"\u003e\u003c/script\u003e\n  \u003cscript\u003e\n    var model = {\n      todos: [\n        { id: 1, title: \"Learn Elm Architecture\", done: true },\n        { id: 2, title: \"Build Todo List App\",    done: false },\n        { id: 3, title: \"Win the Internet!\",      done: false }\n      ],\n      hash: '#/' // the \"route\" to display\n    };\n    mount(model, update, view, 'app');\n  \u003c/script\u003e\n\n  \u003c!-- Below this point is all related to the Tests for the App --\u003e\n  \u003cdiv id=\"test-app\"\u003e\u003c/div\u003e \u003c!-- Create a test-app div to mount the app --\u003e\n\u003c/body\u003e\n```\n\nFor a complete \"snapshot\" of the `index.html` file here,\nsee: [**`index.html`**](https://github.com/dwyl/learn-elm-architecture-in-javascript/blob/ef56c490a48db8a900f1832d0cc373b75838b4d4/examples/todo-list/index.html)\n\n\nIf you run the project with command **`npm start`**\nand navigate to: http://127.0.0.1:8000/\n\nYou should see:\n![view-working](https://user-images.githubusercontent.com/194400/43786145-e476bdd0-9a5f-11e8-9043-cf997be615ae.png)\n\nSo the **`view`** _looks_ like a TodoMVC Todo List\n(_mostly thanks to the imported CSS_),\n_however_ we still cannot _interact_ with the app.\n\n_Next_ we're going to move to \"wiring-up\" the _functionality_\nto construct the UX.\n\n## Functionality - The _Fun_ Part!\n\nWith all the \"foundation\" well defined and tested,\nwe can _confidently_ move on to building out the _features_\npeople _using_ the app will interact with!\n\n#### Requirements?\n\nTake a look at this list of test output:\nhttps://github.com/tastejs/todomvc/tree/main/tests#example-output\n\n```\nTodoMVC\n  1. No Todos\n    ✓ should hide #main and #footer (201ms)\n  2. New Todo\n    ✓ should allow me to add todo items (548ms)\n    ✓ should clear text input field when an item is added (306ms)\n    ✓ should trim text input (569ms)\n    ✓ should show #main and #footer when items added (405ms)\n  3. Mark all as completed\n    ✓ should allow me to mark all items as completed (1040ms)\n    ✓ should allow me to clear the completion state of all items (1014ms)\n    ✓ complete all checkbox should update state when items are completed (1413ms)\n  4. Item\n    ✓ should allow me to mark items as complete (843ms)\n    ✓ should allow me to un-mark items as complete (978ms)\n    ✓ should allow me to edit an item (1155ms)\n    ✓ should show the remove button on hover\n  5. Editing\n    ✓ should hide other controls when editing (718ms)\n    ✓ should save edits on enter (1093ms)\n    ✓ should save edits on blur (1256ms)\n    ✓ should trim entered text (1163ms)\n    ✓ should remove the item if an empty text string was entered (1033ms)\n    ✓ should cancel edits on escape (1115ms)\n  6. Counter\n    ✓ should display the current number of todo items (462ms)\n  7. Clear completed button\n    ✓ should display the number of completed items (873ms)\n    ✓ should remove completed items when clicked (898ms)\n    ✓ should be hidden when there are no items that are completed (893ms)\n  8. Persistence\n    ✓ should persist its data (3832ms)\n  9. Routing\n    ✓ should allow me to display active items (871ms)\n    ✓ should allow me to display completed items (960ms)\n    ✓ should allow me to display all items (1192ms)\n    ✓ should highlight the currently applied filter (1095ms)\n\n27 passing (1m)\n```\n\nWe are going to write each one of these tests and then\n\n#### 1. No Todos, should hide #footer and #main\n\n\nAdd the following test to your `test/todo-app.test.js` file:\n```js\ntest.only('1. No Todos, should hide #footer and #main', function (t) {\n  // render the view and append it to the DOM inside the `test-app` node:\n  document.getElementById(id).appendChild(app.view({todos: []})); // No Todos\n\n  const main_display = window.getComputedStyle(document.getElementById('main'));\n  t.equal('none', main_display._values.display, \"No Todos, hide #main\");\n\n  const main_footer= window.getComputedStyle(document.getElementById('footer'));\n  t.equal('none', main_footer._values.display, \"No Todos, hide #footer\");\n\n  elmish.empty(document.getElementById(id)); // clear DOM ready for next test\n  t.end();\n});\n```\n\nRun the test with:\n```sh\nnode test/todo-app.js\n```\nYou should see the following output:\n![image](https://user-images.githubusercontent.com/194400/43868621-59e1aba0-9b66-11e8-95c1-0034892128cd.png)\n\n##### Make it Pass!\n\nSimply replace the instances of `\"style=display: block;\"` in the view code\nwith a reference to a \"_computed style_\" e.g:\n\n```js\n// Requirement #1 - No Todos, should hide #footer and #main\nvar display = \"style=display:\"\n  + (model.todos.length \u003e 0 ? + \"block\" : \"none\");\n```\n\nYou should see:\n![no-todos-test-passing](https://user-images.githubusercontent.com/194400/43868724-e3e2249c-9b66-11e8-8228-a5c1528c17b0.png)\n\nTesting it in your web browser you should see the desired result:\n\n![no-todos-hide-main-and-footer](https://user-images.githubusercontent.com/194400/43869170-1982648e-9b69-11e8-8f7a-4730edbc07ca.png)\n\n\u003e If you get stuck trying to make the test pass, see:\n[todo-app.js](https://github.com/dwyl/learn-elm-architecture-in-javascript/pull/45/commits/d1bd85e4d75afdc69fcf38b9d58947cdce18a9cf#diff-6be3e16fe7cfb4c00788d4d587374afdR85)\n\nRecommended reading on CSS `visibility:hidden` vs. `display:none`\nthe difference is _important_ for UI:\nhttps://stackoverflow.com/questions/133051/what-is-the-difference-between-visibilityhidden-and-displaynone\n\n\u003cbr /\u003e\n\n#### 2. New Todo, should allow me to add todo items\n\nThe second batch of tests involves adding a new todo item to the list:\n\n```\n2. New Todo\n  ✓ should allow me to add todo items (548ms)\n  ✓ should clear text input field when an item is added (306ms)\n  ✓ should trim text input (569ms)\n  ✓ should show #main and #footer when items added (405ms)\n```\nLet's create a test with these 4 assertions.\n\nAdd the following code/test to your `test/todo-app.test.js` file:\n```js\n// Testing localStorage requires \"polyfil\" because:\n// https://github.com/jsdom/jsdom/issues/1137 ¯\\_(ツ)_/¯\n// globals are usually bad! but a \"necessary evil\" here.\nglobal.localStorage = global.localStorage ? global.localStorage : {\n  getItem: function(key) {\n   const value = this[key];\n   return typeof value === 'undefined' ? null : value;\n },\n setItem: function (key, value) {\n   this[key] = value;\n },\n removeItem: function (key) {\n   delete this[key]\n }\n}\nlocalStorage.removeItem('elmish_store');\n\ntest('2. New Todo, should allow me to add todo items', function (t) {\n  elmish.empty(document.getElementById(id));\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount({todos: []}, app.update, app.view, id, app.subscriptions);\n  const new_todo = document.getElementById('new-todo');\n  // \"type\" content in the \u003cinput id=\"new-todo\"\u003e:\n  const todo_text = 'Make Everything Awesome!     '; // deliberate whitespace!\n  new_todo.value = todo_text;\n  // trigger the [Enter] keyboard key to ADD the new todo:\n  new_todo.dispatchEvent(new KeyboardEvent('keyup', {'keyCode': 13}));\n  const items = document.querySelectorAll('.view');\n\n  t.equal(items.length, 1, \"should allow me to add todo items\");\n  // check if the new todo was added to the DOM:\n  const actual = document.getElementById('1').textContent;\n  t.equal(todo_text.trim(), actual, \"should trim text input\")\n\n  // subscription keyCode trigger \"branch\" test (should NOT fire the signal):\n  const clone = document.getElementById(id).cloneNode(true);\n  new_todo.dispatchEvent(new KeyboardEvent('keyup', {'keyCode': 42}));\n  t.deepEqual(document.getElementById(id), clone, \"#\" + id + \" no change\");\n\n  // check that the \u003cinput id=\"new-todo\"\u003e was reset after the new item was added\n  t.equal(new_todo.value, '',\n    \"should clear text input field when an item is added\")\n\n  const main_display = window.getComputedStyle(document.getElementById('main'));\n  t.equal('block', main_display._values.display,\n    \"should show #main and #footer when items added\");\n  const main_footer= window.getComputedStyle(document.getElementById('footer'));\n  t.equal('block', main_footer._values.display, \"item added, show #footer\");\n\n  elmish.empty(document.getElementById(id)); // clear DOM ready for next test\n  localStorage.removeItem('elmish_store'); // clear \"localStorage\" for next test\n  t.end();\n});\n```\n\nRun the test with:\n```sh\nnode test/todo-app.js\n```\nYou should see the following output:\n\n![test-failing](https://user-images.githubusercontent.com/194400/43929259-1880b41e-9c2c-11e8-9615-1372928c905d.png)\n\n\n#### Todo List `subscriptions`\n\nSo far in the Todo List App\nwe have not implemented any **`subscriptions`**,\nhowever, in order to \"listen\" for the **`[Enter]`** key \"event\"\n(_to add a Todo List item_), we need to dive into event listeners.\n\nThankfully, we touched upon this while building `Elm`(_ish_),\nif you need a recap, see:\n[**elmish.md#subscriptions-for-event-listeners**](https://github.com/dwyl/learn-elm-architecture-in-javascript/blob/main/elmish.md#subscriptions-for-event-listeners)\n\nTry to make the \"**2. New Todo**\" batch of tests _pass_\nby creating (_and exporting_) a **`subscriptions`** function\nin your **`lib/todo-app.js`** file.\n\nIf you get \"_stuck_\", checkout the sample code:\n[**`todo-app.js \u003e subscriptions`**](https://github.com/dwyl/learn-elm-architecture-in-javascript/pull/45/files#diff-6be3e16fe7cfb4c00788d4d587374afdR320)\n\nOnce you see the tests passing:\n\n![add-todo-tests-passing](https://user-images.githubusercontent.com/194400/43982691-08d81eee-9cef-11e8-92c3-341433884092.png)\n\nLet's add some _interaction_!\n\n\n#### 3. Mark all as completed\n\nThe third batch of tests involves \"Toggling\" all todos as \"done=true\":\n\n```\n3. Mark all as completed\n  ✓ should allow me to mark all items as completed\n  ✓ should allow me to clear the completion state of all items\n  ✓ complete all checkbox should update state when items are completed\n```\nLuckily, given that we know how to use a _boolean_ value,\nthese _three_ assertions can be \"solved\" with _minimal_ code.\nLet's create a test with these 3 assertions.\n\nAdd the following code/test to your `test/todo-app.test.js` file:\n```js\ntest.only('3. Mark all as completed (\"TOGGLE_ALL\")', function (t) {\n  elmish.empty(document.getElementById(id));\n  localStorage.removeItem('elmish_' + id);\n  const model = {\n    todos: [\n      { id: 0, title: \"Learn Elm Architecture\", done: true },\n      { id: 1, title: \"Build Todo List App\",    done: false },\n      { id: 2, title: \"Win the Internet!\",      done: false }\n    ],\n    hash: '#/' // the \"route\" to display\n  };\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  // confirm that the ONLY the first todo item is done=true:\n  const items = document.querySelectorAll('.view');\n\n  document.querySelectorAll('.toggle').forEach(function(item, index) {\n    t.equal(item.checked, model.todos[index].done,\n      \"Todo #\" + index + \" is done=\" + item.checked\n      + \" text: \" + items[index].textContent)\n  })\n\n  // click the toggle-all checkbox to trigger TOGGLE_ALL: \u003e\u003e true\n  document.getElementById('toggle-all').click(); // click toggle-all checkbox\n  document.querySelectorAll('.toggle').forEach(function(item, index) {\n    t.equal(item.checked, true,\n      \"TOGGLE each Todo #\" + index + \" is done=\" + item.checked\n      + \" text: \" + items[index].textContent)\n  });\n  t.equal(document.getElementById('toggle-all').checked, true,\n    \"should allow me to mark all items as completed\")\n\n\n  // click the toggle-all checkbox to TOGGLE_ALL (again!) true \u003e\u003e false\n  document.getElementById('toggle-all').click(); // click toggle-all checkbox\n  document.querySelectorAll('.toggle').forEach(function(item, index) {\n    t.equal(item.checked, false,\n      \"TOGGLE_ALL Todo #\" + index + \" is done=\" + item.checked\n      + \" text: \" + items[index].textContent)\n  })\n  t.equal(document.getElementById('toggle-all').checked, false,\n    \"should allow me to clear the completion state of all items\")\n\n  // *manually* \"click\" each todo item:\n  document.querySelectorAll('.toggle').forEach(function(item, index) {\n    item.click(); // this should \"toggle\" the todo checkbox to done=true\n    t.equal(item.checked, true,\n      \".toggle.click() (each) Todo #\" + index + \" which is done=\" + item.checked\n      + \" text: \" + items[index].textContent)\n  });\n  // the toggle-all checkbox should be \"checked\" as all todos are done=true!\n  t.equal(document.getElementById('toggle-all').checked, true,\n    \"complete all checkbox should update state when items are completed\")\n\n  elmish.empty(document.getElementById(id)); // clear DOM ready for next test\n  localStorage.removeItem('elmish_store');\n  t.end();\n});\n```\n\n\u003e _Yes, it's a \"big\" test with several assertions.\nWe prefer to keep them \"clustered\" together because they test\nthe functionality as a \"block\".\nSome people prefer to split the assertions out into individual unit tests,\nour advice to the \"practical developer\" is: be pragmatic!\nIf you are testing the functionality and the test is legible,\nthere's no \"harm\" in having several assertions._\n\nIf you attempt to run the test file:\n```sh\nnode test/todo-app.test.js\n```\nYou will see something like this:\n\n![toggle-all-test-failing](https://user-images.githubusercontent.com/194400/43985804-3c8dbe02-9d02-11e8-9876-cd7e35602754.png)\n\nWhile there may _appear_ to be \"_many_\" assertions in this test,\nin reality there are only two bits of functionality.\n\n_Firstly_, we need a new `case`\nin the `update` `switch` statement: `TOGGLE_ALL`. \u003cbr /\u003e\nand _second_ we need to add a couple of lines to our `TOGGLE`\nblock to _check_ if _all_ todos are `done=true` or `done=false`.\nIn the case where _all_ todos are `done=true` we should reflect\nthis in the \"state\" of the `toggle-all` checkbox.\nThe _easiest_ way of representing this in the `model` is\nwith a new property, e.g: `model.all_done=true`\nwhen _all_ todos are `done=true`.\n\nThe only other thing we need to update is the `render_main`\nfunction to include `signal('TOGGLE_ALL')` in the attributes array.\n\nTry and make this test pass by yourself before consulting the\nsample code:\n[**`lib/todo-app.js`**](https://github.com/dwyl/learn-elm-architecture-in-javascript/pull/45/files#diff-6be3e16fe7cfb4c00788d4d587374afdR46)\n\n\n### 4. Item (Toggle, Edit \u0026 Delete)\n\n```\n4. Item\n  ✓ should allow me to mark items as complete (843ms)\n  ✓ should allow me to un-mark items as complete (978ms)\n  ✓ should allow me to edit an item (1155ms)\n  ✓ should show the remove button on hover\n```\n\nOf these requirements, we already have the first two \"_covered_\"\nbecause we implemented the `TOGGLE` feature (_above_).\n\nWe can add another \"proxy\" test just for \"_completeness_\":\n\n```js\ntest.only('4. Item: should allow me to mark items as complete', function (t) {\n  elmish.empty(document.getElementById(id));\n  localStorage.removeItem('elmish_' + id);\n  const model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false }\n    ],\n    hash: '#/' // the \"route\" to display\n  };\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  const item = document.getElementById('0')\n  t.equal(item.textContent, model.todos[0].title, 'Item contained in model.');\n  // confirm that the todo item is NOT done (done=false):\n  t.equal(document.querySelectorAll('.toggle')[0].checked, false,\n  'Item starts out \"active\" (done=false)');\n\n\n  // click the checkbox to toggle it to done=true\n  document.querySelectorAll('.toggle')[0].click()\n  t.equal(document.querySelectorAll('.toggle')[0].checked, true,\n  'Item should allow me to mark items as complete');\n\n  // click the checkbox to toggle it to done=false \"undo\"\n  document.querySelectorAll('.toggle')[0].click()\n  t.equal(document.querySelectorAll('.toggle')[0].checked, false,\n  'Item should allow me to un-mark items as complete');\n  t.end();\n});\n```\nYou should not need to write any additional code\nin order to make this test pass; just run it and move on.\n\n![toggle-todo-tests-passing](https://user-images.githubusercontent.com/194400/43992979-a4d00ab6-9d7e-11e8-891b-9f699f474dd5.png)\n\n\n\n#### 4.1 `DELETE` an Item\n\n```\nshould show the remove button on hover\n```\n\n##### Acceptance Criteria\n\n+ [ ] should show the `\u003cbutton class=\"destroy\"\u003e`\non hover (over the item) ... thankfully the TodoMVC CSS\nhandles this for us, we just need our `view`\nto render the `\u003cbutton\u003e`\n+ [ ] Clicking/tapping the `\u003cbutton class=\"destroy\"\u003e`\nsends the `signal('DELETE', todo.id, model)`\n+ [ ] The `DELETE` update case receives the `todo.id`\nand removes it from the `model.todos` Array.\n\n\n##### `DELETE` Item _Test_\n\nAppend the following test code to your `test/todo-app.test.js` file:\n\n```js\ntest.only('4.1 DELETE item by clicking \u003cbutton class=\"destroy\"\u003e', function (t) {\n  elmish.empty(document.getElementById(id));\n  localStorage.removeItem('elmish_' + id);\n  const model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false }\n    ],\n    hash: '#/' // the \"route\" to display\n  };\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  // const todo_count = ;\n  t.equal(document.querySelectorAll('.destroy').length, 1, \"one destroy button\")\n\n  const item = document.getElementById('0')\n  t.equal(item.textContent, model.todos[0].title, 'Item contained in DOM.');\n  // DELETE the item by clicking on the \u003cbutton class=\"destroy\"\u003e:\n  const button = item.querySelectorAll('button.destroy')[0];\n  button.click()\n  // confirm that there is no loger a \u003cbutton class=\"destroy\"\u003e\n  t.equal(document.querySelectorAll('button.destroy').length, 0,\n    'there is no loger a \u003cbutton class=\"destroy\"\u003e as the only item was DELETEd')\n  t.equal(document.getElementById('0'), null, 'todo item successfully DELETEd');\n  t.end();\n});\n```\n\nIf you run the tests `node test/todo-app.test.js`\nyou should now see:\n![delete-test-one-assertion-failing](https://user-images.githubusercontent.com/194400/44953479-21313300-ae96-11e8-971a-51757702bacc.png)\n\nThe first two assertions are _optional_ and _should_ (_always_)\npass given that they rely on functionality defined previously.\nThe second two will only pass once you _make_ them pass!\n\n##### `DELETE` Item _Implementation_\n\nThe _first_ step is to add an invocation of `signal('DELETE' ...)`\nto the `render_item` view rendering function. _Specifically_ the\n`button` line:\n\n```js\nbutton([\"class=destroy\"])\n```\nAdd the `signal` function invocation:\n```js\nbutton([\"class=destroy\", signal('DELETE', item.id)])\n```\n\nsimply adding this function invocation as an Array element will set it\nas an `onclick` attribute for the `\u003cbutton\u003e`\ntherefore when the _user_ clicks the button it will\n\"trigger\" the `signal` function with the appropriate arguments.\nThere is no \"magic\" just code we tested/wrote earlier.\n\n\n_Second_ we need to add a `case` statement\nto the `update` function.\nYou should attempt to \"solve\" this yourself.\nThere is no \"right\" answer, there are at least\n5 ways of solving this, as always, you should write the code\nthat you feel is most _readable_.\n\nIf you get \"_stuck_\" or want to confirm your understanding\nof the implementation of the `DELETE` functionality,\ncheck the code in [`todo-app.js` \u003e `update`](https://github.com/dwyl/learn-elm-architecture-in-javascript/pull/45/files#diff-6be3e16fe7cfb4c00788d4d587374afdR57)\nfunction.\n\n\n\u003e Rather bizarrely the edit functionality is mentioned\n_both_ in the Item and Editing sections. \u003cbr /\u003e\n\n```\nshould allow me to edit an item\n```\n\n\u003e This is kinda _meaningless_ as an assertion.\nWhat does \"edit an item\" actually _mean_? \u003cbr /\u003e\n(_we have expanded the acceptance criteria below..._)\n\n\u003cbr /\u003e\n\n### 5. `EDIT` an Item\n\nEditing a Todo List item is (_by far_)\nthe most \"advanced\" functionality in the TodoMVC app\nbecause it involves multiple steps and \"dynamic UI\".\n\nDon't panic! Just because something has \"more steps\" than we have seen before,\ndoesn't mean we should be \"overwhelmed\" by its' complexity.\nWe just need to \"break it down\" into \"bitesize chunks\"!\n\n\u003e _**Note**: the most \"**difficult**\" part of implementing the \"edit an item\"\nfunctionality is having a \"mental picture\" of the UX\nso that we can write the **tests first**\nand isolate the required functions (update actions) from the keyboard/mouse\ninteractions. i.e. breaking down the steps into distinct \"units\"_.\n\n\nFirst let's review the TodoMVC \"Editing\" test assertions:\n\n\n#### `EDIT` Item Test Titles \u0026 Acceptance Criteria\n\n```\n5. Editing\n  ✓ should hide other controls when editing (718ms)\n  ✓ should save edits on enter (1093ms)\n  ✓ should save edits on blur (1256ms)\n  ✓ should trim entered text (1163ms)\n  ✓ should remove the item if an empty text string was entered (1033ms)\n  ✓ should cancel edits on escape (1115ms)\n```\n\nFurther reading of the TodoMVC Spec:\nhttps://github.com/tastejs/todomvc/blob/main/app-spec.md#item\nreveals the following acceptance criteria:\n\n\n+ [ ] Double-click on Item **`\u003clabel\u003etitle\u003c/label\u003e`**\nto begin editing (_that item_)\n+ [ ] Render an **`\u003cinput class=\"edit\"\u003e`**\nif in \"**editing _mode_**\"\n(_see screenshot and markup below_)\n  + [ ] Add `class=\"editing\"` to `\u003cli\u003e` when editing\n  + [ ] Remove (_don't add_) `class=\"editing\"` from `\u003cli\u003e`\n  when no longer editing.\n+ [ ] Set the `item.id` as the `id` of the **`\u003cinput class=\"edit\"\u003e`**\n  so that we know which item is being edited.\n+ [ ] Add `case` in `keyup` Event Listener\n  for **`[Enter]`** keyup (_see **`subscriptions`** above_)\n  if we are in \"**editing _mode_**\",\n  get the text value from the **`\u003cinput class=\"edit\"\u003e`**\n  _instead_ of **`\u003cinput id=\"new-todo\"\u003e`**\n  so that we _update_ the _existing_ Todo Item title (text).\n+ [ ] When **`[Enter]`** is pressed while in \"**editing _mode_**\",\n\"_dispatch_\" the **`SAVE`** action: `signal('SAVE')`\n  + [ ] If the **`\u003cinput class=\"edit\"\u003e`** is _blank_, `delete` the todo item.\n\nBy _inspecting_ the DOM for the VanillaJS TodoMVC example:\nhttps://todomvc.com/examples/vanillajs \u003cbr /\u003e\nwe can see that _two_ things change in the DOM when in \"**editing _mode_**\":\n\n+ **`\u003cli class=\"editing\"\u003e`** the CSS `class=\"editing\"` is added\nto the todo list item being _edited_.\n+ **`\u003cinput class=\"edit\"\u003e`** is inserted into the DOM _inside_ the `\u003cli\u003e`\nso the item title can be edited.\n\n![todo-edit-html](https://user-images.githubusercontent.com/194400/43995210-f4f484e0-9da1-11e8-8cc5-09f7309db963.png)\n\nHere is the _sample_ HTML in \"**editing _mode_**\"\n(_copy-pasted_) from the VanillaJS TodoMVC implementation\nthe **`\u003cli\u003e`** is being edited (_as per screenshot above_):\n```HTML\n\u003cul class=\"todo-list\"\u003e\n  \u003cli data-id=\"1533987109280\" class=\"completed \"\u003e\n    \u003cdiv class=\"view\"\u003e\n      \u003cinput class=\"toggle\" type=\"checkbox\" checked=\"\"\u003e\n      \u003clabel\u003ehello world\u003c/label\u003e\n      \u003cbutton class=\"destroy\"\u003e\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/li\u003e\n  \u003cli data-id=\"1534013859716\" class=\"editing\"\u003e\n    \u003cdiv class=\"view\"\u003e\u003cinput class=\"toggle\" type=\"checkbox\"\u003e\n      \u003clabel\u003etotes editing this todo item\u003c/label\u003e\n      \u003cbutton class=\"destroy\"\u003e\n      \u003c/button\u003e\n    \u003c/div\u003e\n    \u003cinput class=\"edit\"\u003e\n  \u003c/li\u003e\n\u003c/ul\u003e\n```\n\nFrom the HTML/DOM we can see that \"editing\" a Todo item is _deceptively_ simple\nfrom a markup perspective, we _just_ need to know _which_ item we are editing\nand render the appropriate tags/classes.\n\n#### Three Steps to `EDIT` an Item\n\nThere are _three_ steps to Editing a Todo List item:\n\n1. ***Trigger*** the \"double-click\" event listener/handler\n  1.1. Receiving the `singal('EDIT', item.id)` _activates_ \"**editing _mode_**\".\n2. ***Edit*** the todo list item's `title` property\n3. ***Save*** the updated item `title`: `singal('SAVE', item.id)`\n\nFor these _three_ steps there are two `update` actions: `EDIT` and `SAVE`\nwhich will require two new `case` statements in the `update` function.\n\n\u003e _**Note**: there is a \"fourth\" step which is \"**Cancelling**\" an edit,\nwhich we will cover in **section 5.5** below, but for now we are\nonly considering the \"happy path\" which results in a successful edit._\n\n\n\n#### 5.1 `render_item` view function with \"Edit Mode\" `\u003cinput class=\"edit\"\u003e`\n\nIn order to edit an item the **`render_item`** function\nwill require **3 modifications**:\n\n1. Add the `signal('EDIT', item.id)` as an **`onclick` attribute** to `\u003clabel\u003e`\nso that when a `\u003clabel\u003e` is (double-)clicked\nthe `model.editing` property is set by the `update` function (_see below_).\n2. Apply the **`\"class=editing\"`** to the list item which is being edited.\n3. Display the **`\u003cinput class=\"edit\"\u003e`**\nwith the Todo list item title as it's **`value`** property.\n\n#### 5.2 `render_item` \"Edit Mode\" _Test_\n\nFor the above modifications (_requirements_) we can write a _single_ test\nwith four assertions. Append the following code to `test/todo-app.test.js`:\n\n```js\ntest.only('5. Editing: \u003e Render an item in \"editing mode\"', function (t) {\n  elmish.empty(document.getElementById(id));\n  localStorage.removeItem('elmish_' + id);\n  const model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false },\n      { id: 1, title: \"Bootstrap for as long as you can\", done: false },\n      { id: 2, title: \"Let's solve our own problem\", done: false }\n    ],\n    hash: '#/', // the \"route\" to display\n    editing: 2 // edit the 3rd todo list item (which has id == 2)\n  };\n\n  // render the ONE todo list item in \"editing mode\" based on model.editing:\n  document.getElementById(id).appendChild(\n    app.render_item(model.todos[2], model, mock_signal),\n  );\n  // test that signal (in case of the test mock_signal) is onclick attribute:\n  t.equal(document.querySelectorAll('.view \u003e label')[0].onclick.toString(),\n    mock_signal().toString(), \"mock_signal is onclick attribute of label\");\n\n  // test that the \u003cli class=\"editing\"\u003e and \u003cinput class=\"edit\"\u003e was rendered:\n  t.equal(document.querySelectorAll('.editing').length, 1,\n    \"\u003cli class='editing'\u003e element is visible\");\n  t.equal(document.querySelectorAll('.edit').length, 1,\n    \"\u003cinput class='edit'\u003e element is visible\");\n  t.equal(document.querySelectorAll('.edit')[0].value, model.todos[2].title,\n    \"\u003cinput class='edit'\u003e has value: \" + model.todos[2].title);\n  t.end();\n});\n```\n\nThere is quite a lot to \"unpack\" here, but the main gist is that\nbased on the `model.editing` key being set to `2`, our `render_item` function,\nwill add the `editing` CSS class to the `\u003cli\u003e` element and render an\n`\u003cinput\u003e` with CSS class `edit`.\nThe TodoMVC style sheet (`todomvc-app.css`) will take care of displaying\nthe input correctly.\n\nSetting the **`onclick`** attribute of the `\u003clabel\u003e` element\nto whatever is passed in as the third argument of `redner_item`\ni.e. the `signal` will mean that a specific action will be dispatched/triggered\nwhen the `\u003clabel\u003e` element is clicked.\n\n\n\u003e **SPOILER ALERT**: If you want to _try_ to make the \"Edit Mode\" _Test_\nassertions _pass_ without reading the \"solution\",\ndo it now before proceeding to the reading the _implementation_ section.\n\n\u003cbr /\u003e\n\n#### 5.2 `render_item` \"Edit Mode\" _Implementation_\n\nGiven that there are 4 assertions that need to pass\nand we know there are 3 changes that need to be made\nto the `render_item` function,\nrather than leaving you (_the reader_) wondering \"_where do I start?!_\",\nhere is the code that makes the tests pass:\n\n_Before_:\n```js\nfunction render_item (item, model, signal) {\n  return (\n    li([\n      \"data-id=\" + item.id,\n      \"id=\" + item.id,\n      item.done ? \"class=completed\" : \"\"\n    ], [\n      div([\"class=view\"], [\n        input([\n          item.done ? \"checked=true\" : \"\",\n          \"class=toggle\",\n          \"type=checkbox\",\n          typeof signal === 'function' ? signal('TOGGLE', item.id) : ''\n          ],[]), // \u003cinput\u003e does not have any nested elements\n        label([], [text(item.title)]),\n        button([\"class=destroy\",\n        typeof signal === 'function' ? signal('DELETE', item.id) : ''])\n      ]) // \u003c/div\u003e\n    ]) // \u003c/li\u003e\n  )\n}\n```\n\n_After_:\n```js\nfunction render_item (item, model, signal) {\n  return (\n    li([\n      \"data-id=\" + item.id,\n      \"id=\" + item.id,\n      item.done ? \"class=completed\" : \"\",\n      model \u0026\u0026 model.editing \u0026\u0026 model.editing === item.id ? \"class=editing\" : \"\"\n    ], [\n      div([\"class=view\"], [\n        input([\n          item.done ? \"checked=true\" : \"\",\n          \"class=toggle\",\n          \"type=checkbox\",\n          typeof signal === 'function' ? signal('TOGGLE', item.id) : ''\n          ], []), // \u003cinput\u003e does not have any nested elements\n        label([ typeof signal === 'function' ? signal('EDIT', item.id) : '' ],\n          [text(item.title)]),\n        button([\"class=destroy\",\n          typeof signal === 'function' ? signal('DELETE', item.id) : ''])\n        ]\n      ), // \u003c/div\u003e\n    ].concat(model \u0026\u0026 model.editing \u0026\u0026 model.editing === item.id ? [ // editing?\n      input([\"class=edit\", \"id=\" + item.id, \"value=\" + item.title, \"autofocus\"])\n    ] : [])) // \u003c/li\u003e\n  )\n}\n```\nLet's walk through the three code changes made:\n\n1. Adding `\"class=editing\"` to the `\u003cli\u003e` based on `model.editing`\nis the simplest code modification, similar to the conditional attribute\n`class=completed` on the previous line.\n\n```js\nmodel \u0026\u0026 model.editing \u0026\u0026 model.editing === item.id ? \"class=editing\" : \"\"\n````\n\nWe include the check for `model \u0026\u0026 model.editing` because if either of these\ntwo are `undefined` there's no need to keep checking.\nOnly if the `model.editing` matches the `item.id`\n(_the todo list item being rendered_) do we render the **`\"class=editing\"`**.\nOnly one todo list item `title` will be edited at once,\nso this will only match (_at most_) _one_ item in the `model.todos` array.\n\n2. Setting the **`signal('EDIT', item.id)`**\n\n_Why_ do we need the `typeof signal` (_type-checking_)...?\n\n```js\nlabel([ typeof signal === 'function' ? signal('EDIT', item.id) : '' ],\n  [text(item.title)]),\n```\n\nWhy _can't_ we just write this:\n\n```js\nlabel([signal('EDIT', item.id)], [text(item.title)]),\n```\n\nGiven that **`signal`** is the final argument to the `render_item` function,\nit is considered an _optional_ argument.\nIf for any reason the `render_item` function is invoked _without_ the `singal`\nparameter, then attempting to _invoke_ **`signal('EDIT', item.id)`**\nwill result in a **`ReferenceError: signal is not defined`** which will\n\"crash\" the app _fatally_.\n\nIf you are the _only_ person who is going to write code that will invoke\n`render_item`, you don't need to \"worry\" about the `typeof signal`\nbecause there is \"no need\" for type-checking the `signal`;\nsurely you won't _forget_ to invoke it with a valid `signal` ...\n_however_ we _always_ approach our JavaScript code a\n[\"***defensive programming***\"](https://en.wikipedia.org/wiki/Defensive_programming)\nperspective\nbecause we _know_ from _experience_\nthat banking on the\n[\"***happy path***\"](https://en.wikipedia.org/wiki/Happy_path)\nin JS code\nis like driving without a seatbelt;\nyou might be \"fine\" most of the time, but when something \"bad\" happens,\nyou will go flying through the windscreen and have a _really_ bad day!\n\n![dilbert-bugs](https://user-images.githubusercontent.com/194400/45164547-8f555d00-b1ea-11e8-9767-e23350c1e9a0.png)\n\nIf you want to _avoid_ having to do _manual_ \"type-checking\",\nuse **`Elm`**, it does all this for you _transparently_.\n\n3. Append the **`\u003cinput class=\"edit\"\u003e`**\nto the `\u003cli\u003e` if in \"**editing _mode_**\":\n\n```js\n].concat(model \u0026\u0026 model.editing \u0026\u0026 model.editing === item.id ? [ // editing?\n  input([\"class=edit\", \"id=\" + item.id, \"value=\" + item.title, \"autofocus\"])\n] : [])) // \u003c/li\u003e\n```\nThe _reason_ we use `.concat` is to allow us to\n_optionally_ render the element or _nothing_ then _append_ it to the\nArray of child elements nested in the `\u003cli\u003e`.\n\nAn _alternative_ to using `.concat()` could be an empty `div` node:\n```js\nmodel \u0026\u0026 model.editing \u0026\u0026 model.editing === item.id ? // editing?\n  input([\"class=edit\", \"id=\" + item.id, \"value=\" + item.title, \"autofocus\"])\n  : div() // empty element.\n```\nThis is because attempting to return anything other than a DOM element\nwill result in the following error:\n```js\nTypeError: Argument 1 of Node.appendChild does not implement interface Node\n```\nWe are not \"fans\" of having \"empty\" elements in the DOM, it's \"sloppy\". \u003cbr /\u003e\nHence the `concat()` approach which results in \"clean\" DOM.\n\nAt this point our test assertions all pass:\n```sh\nnode test/todo-app.test.js\n```\n\n![render_item-tests-pass](https://user-images.githubusercontent.com/194400/45167506-2c1af900-b1f1-11e8-9898-af46f979fdbd.png)\n\nBut we are building a _visual_ application and are not _seeing_ anything ...\n\n#### _Visualise_ Editing Mode?\n\nLet's take a _brief_ detour to _visualise_ the progress we have made.\n\nOpen the `index.html` file\nand alter the contents of the `\u003cscript\u003e` tag:\n```html\n\u003cscript\u003e\n  var model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false },\n      { id: 1, title: \"Bootstrap for as long as you can\", done: false },\n      { id: 2, title: \"Let's solve our own problem\", done: false }\n    ],\n    hash: '#/', // the \"route\" to display\n    editing: 2 // edit the 3rd todo list item (which has id == 2)\n  };\n  mount(model, update, view, 'app', subscriptions);\n\u003c/script\u003e\n```\n\nThen in your terminal, start the live-server:\n```sh\nnpm start\n```\nIn your browser, vist: http://127.0.0.1:8000/ \u003cbr /\u003e\nYou should see that the _third_ todo list item is in \"**editing _mode_**\":\n\n![elm-todomvc-editing-item](https://user-images.githubusercontent.com/194400/45180706-0eab5680-b214-11e8-9dcf-a8c4476e4b11.png)\n\nNothing will happen (_yet_) if you attempt to \"save\" any changes.\nLet's work on the `case` (_handler_) for **`signal('EDIT', item.id)`**\nwhich will handle the \"double-click\" event and set `model.editing`.\n\n\n### 5.2 Double-Click item `\u003clabel\u003e` to Edit\n\nThe TodoMVC ***spec*** for item\nhttps://github.com/tastejs/todomvc/blob/main/app-spec.md#item\nincludes the line:\n\n```sh\nDouble-clicking the \u003clabel\u003e activates editing mode, by toggling the .editing class on its \u003cli\u003e\n```\n\n\u003e _**Note**: the sample TodoMVC Browser Tests:\nhttps://github.com/tastejs/todomvc/tree/main/tests#example-output\ndoes **not** include a test-case for **double-clicking**.\nWe are going to add one below for \"extra credit\"._\n\nSince Double-clicking/tapping is the _only_ way to edit a todo item,\nwe feel that it deserves a test.\n\n#### _How_ do we Track Double-Clicking?\n\nWhen we don't know how to do something, a good place to start is to search\nfor the keywords we want, e.g: \"JavaScript detect double-click event\"\nfor which the top result is the following StackOverflow Q/A:\nhttps://stackoverflow.com/questions/5497073/how-to-differentiate-single-click-event-and-double-click-event\n\nReading though all the answers, we determine that the most relevant (_to us_)\nis: https://stackoverflow.com/a/16033129/1148249 (_which uses \"vanilla\" JS_):\n\n[![stackoverflow-double-click-example](https://user-images.githubusercontent.com/194400/45124122-14942f80-b161-11e8-94c0-f54f2352bdd5.png)](https://stackoverflow.com/a/16033129/1148249)\n\n\u003e_**Note**: when you find a StackOverflow question/answer **helpful,\nupvote** to show your appreciation!_\n\n```html\n\u003cdiv onclick=\"doubleclick(this, function(){alert('single')}, function(){alert('double')})\"\u003eclick me\u003c/div\u003e\n\u003cscript\u003e\n  function doubleclick(el, onsingle, ondouble) {\n    if (el.getAttribute(\"data-dblclick\") == null) {\n      el.setAttribute(\"data-dblclick\", 1);\n      setTimeout(function () {\n        if (el.getAttribute(\"data-dblclick\") == 1) {\n          onsingle();\n        }\n        el.removeAttribute(\"data-dblclick\");\n      }, 300);\n    } else {\n      el.removeAttribute(\"data-dblclick\");\n      ondouble();\n    }\n  }\n\u003c/script\u003e\n```\nGiven that we are using the Elm Architecture to manage the DOM,\nwe don't want a function that _alters_ the DOM.\nSo we are going to _borrow_ the _logic_ from this example but _simplify_ it.\nSince we are not mutating the DOM by setting `data-dblclick` attributes,\nwe won't need to remove the attribute using a `setTimeout`,\n\n\n### 5.2 `'EDIT' update case` _Test_\n\nIn keeping with our TDD approach,\nour _first_ step when adding the `case` expression\nfor `'EDIT'` in the `update` function is to write a _test_.\n\nAppend following test code to your `test/todo-app.test.js` file:\n\n```js\ntest.only('5.2 Double-click an item \u003clabel\u003e to edit it', function (t) {\n  elmish.empty(document.getElementById(id));\n  localStorage.removeItem('todos-elmish_' + id);\n  const model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false },\n      { id: 1, title: \"Let's solve our own problem\", done: false }\n    ],\n    hash: '#/' // the \"route\" to display\n  };\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  const label = document.querySelectorAll('.view \u003e label')[1]\n  // \"double-click\" i.e. click the \u003clabel\u003e twice in quick succession:\n  label.click();\n  label.click();\n  // confirm that we are now in editing mode:\n  t.equal(document.querySelectorAll('.editing').length, 1,\n    \"\u003cli class='editing'\u003e element is visible\");\n  t.equal(document.querySelectorAll('.edit')[0].value, model.todos[1].title,\n    \"\u003cinput class='edit'\u003e has value: \" + model.todos[1].title);\n  t.end();\n});\n```\nIf you attempt to run this test: `node test/todo-app.test.js`\nyou will see output similar to the following:\n\n![edit-double-click-test-failing](https://user-images.githubusercontent.com/194400/45183202-54b7e880-b21b-11e8-84d8-7b3b50162113.png)\n\nLet's write the code necessary to make the test assertions _pass_!\nIf you want to try this yourself based on the StackOverflow answer (_above_),\ngo for it! (_don't scroll down to the \"answer\" till you have tried..._)\n\n### 5.2 `'EDIT' update case` _Implementation_\n\nGiven our \"research\" (_above_) of how to implement a \"double-click\" handler,\nwe can write the `'EDIT'` case as the following:\n\n```js\ncase 'EDIT':\n  // this code is inspired by: https://stackoverflow.com/a/16033129/1148249\n  // simplified as we are not altering the DOM!\n  if (new_model.clicked \u0026\u0026 new_model.clicked === data \u0026\u0026\n    Date.now() - 300 \u003c new_model.click_time ) { // DOUBLE-CLICK \u003c 300ms\n      new_model.editing = data;\n      console.log('DOUBLE-CLICK', \"item.id=\", data,\n      \"| model.editing=\", model.editing,\n      \"| diff Date.now() - new_model.click_time: \",\n      Date.now(), \"-\", new_model.click_time, \"=\",\n      Date.now() - new_model.click_time);\n  }\n  else { // first click\n    new_model.clicked = data; // so we can check if same item clicked twice!\n    new_model.click_time = Date.now(); // timer to detect double-click 300ms\n    new_model.editing = false; // reset\n    console.log('FIRST CLICK! data:', data);\n  }\n  break;\n```\nIf you ignore/remove the `console.log` lines (_which we are using for now!_),\nthe code is only a few lines long:\n```js\ncase 'EDIT':\n  // this code is inspired by: https://stackoverflow.com/a/16033129/1148249\n  // simplified as we are not altering the DOM!\n  if (new_model.clicked \u0026\u0026 new_model.clicked === data \u0026\u0026\n    Date.now() - 300 \u003c new_model.click_time ) { // DOUBLE-CLICK \u003c 300ms\n      new_model.editing = data;\n  }\n  else { // first click\n    new_model.clicked = data; // so we can check if same item clicked twice!\n    new_model.click_time = Date.now(); // timer to detect double-click 300ms\n    new_model.editing = false; // reset\n  }\n  break;\n```\nThe main \"purpose\" of this code is to _detect_ if a `\u003clabel\u003e` was clicked\ntwice in the space of 300 milliseconds and apply the `item.id` to\nthe `model.editing` property so that we know which `\u003cli\u003e` to render in\n\"editing mode\".\n\nRun the test and watch it _pass_: `node test/todo-app.test.js`\n![edit-double-click-test-pass](https://user-images.githubusercontent.com/194400/45183878-3bb03700-b21d-11e8-9842-be62113bfe0a.png)\n\nIn this case the time between the two clicks was 31 milliseconds,\nso they will count as a \"double-click\"!\n\n\nIf a `\u003clabel\u003e` is clicked slowly, the `model.editing` will _not_ be set,\nand we will _not_ enter \"editing mode\".\nLet's add a quick test for the scenario\nwhere two clicks are more than 300ms apart.\n\nAppend following test code to your `test/todo-app.test.js` file:\n\n```js\ntest.only('5.2.2 Slow clicks do not count as double-click \u003e no edit!', function (t) {\n  elmish.empty(document.getElementById(id));\n  localStorage.removeItem('todos-elmish_' + id);\n  const model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false },\n      { id: 1, title: \"Let's solve our own problem\", done: false }\n    ],\n    hash: '#/' // the \"route\" to display\n  };\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  const label = document.querySelectorAll('.view \u003e label')[1]\n  // \"double-click\" i.e. click the \u003clabel\u003e twice in quick succession:\n  label.click();\n  setTimeout(function (){\n    label.click();\n    // confirm that we are now in editing mode:\n    t.equal(document.querySelectorAll('.editing').length, 0,\n      \"\u003cli class='editing'\u003e element is NOT visible\");\n    t.end();\n  }, 301)\n});\n```\n\nThere is no need to write any code to make this test pass,\nthis is merely an additional test to _confirm_ that our check for the\ntime between clicks works; clicks spaced more than 300ms will not count\nas \"double-click\".\n\n![edit-item-not-double-click](https://user-images.githubusercontent.com/194400/45184155-ff310b00-b21d-11e8-8f6c-ef6d699861cf.png)\n\n\n### 5.3 `'SAVE'` a Revised Todo Item Title after Editing it\n\nOnce you are done editing a todo list item title,\nyou want to _save_ your changes!\n\n\n### 5.3 `'SAVE' update case` _Test_\n\nAppend following test code to your `test/todo-app.test.js` file:\n\n```js\ntest.only('5.3 [ENTER] Key in edit mode triggers SAVE action', function (t) {\n  elmish.empty(document.getElementById(id));\n  localStorage.removeItem('todos-elmish_' + id);\n  const model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false },\n      { id: 1, title: \"Let's solve our own problem\", done: false }\n    ],\n    hash: '#/', // the \"route\" to display\n    editing: 1 // edit the 3rd todo list item (which has id == 2)\n  };\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  // change the\n  const updated_title = \"Do things that don\\'t scale!  \"\n  // apply the updated_title to the \u003cinput class=\"edit\"\u003e:\n  document.querySelectorAll('.edit')[0].value = updated_title;\n  // trigger the [Enter] keyboard key to ADD the new todo:\n  document.dispatchEvent(new KeyboardEvent('keyup', {'keyCode': 13}));\n  // confirm that the todo item title was updated to the updated_title:\n  const label = document.querySelectorAll('.view \u003e label')[1].textContent;\n  t.equal(label, updated_title.trim(),\n      \"item title updated to:\" + updated_title + ' (trimmed)');\n  t.end();\n});\n```\nIf you attempt to run this test: `node test/todo-app.test.js`\nyou will see output similar to the following:\n\n![save-edit-test-fails](https://user-images.githubusercontent.com/194400/45187886-2c83b600-b22a-11e8-8782-6d3fcaa240df.png)\n\n\n### 5.3 `'SAVE' update case` _Implementation_\n\nThe _first_ step in the implementation is to create the `'SAVE'` case\nin `update` function:\n\n```js\ncase 'SAVE':\n  var edit = document.getElementsByClassName('edit')[0];\n  var value = edit.value;\n  var id = parseInt(edit.id, 10);\n  // End Editing\n  new_model.clicked = false;\n  new_model.editing = false;\n\n  if (!value || value.length === 0) { // delete item if title is blank:\n    return update('DELETE', new_model, id);\n  }\n  // update the value of the item.title that has been edited:\n  new_model.todos = new_model.todos.map(function (item) {\n    if (item.id === id \u0026\u0026 value \u0026\u0026 value.length \u003e 0) {\n      item.title = value.trim();\n    }\n    return item; // return all todo items.\n  });\n  break;\n```\n\nThe _second_ step is _triggering_ this `case` in the `subscriptions`\nevent listener for `keyup`:\n\n_Before_:\n\n```js\ndocument.addEventListener('keyup', function handler (e) {\n  switch(e.keyCode) {\n    case ENTER_KEY:\n      var new_todo = document.getElementById('new-todo');\n      if(new_todo.value.length \u003e 0) {\n        signal('ADD')(); // invoke singal inner callback\n        new_todo.value = ''; // reset \u003cinput\u003e so we can add another todo\n        document.getElementById('new-todo').focus();\n      }\n      break;\n  }\n});\n```\n\n\n_After_:\n\n```js\ndocument.addEventListener('keyup', function handler (e) {\n  switch(e.keyCode) {\n    case ENTER_KEY:\n      var editing = document.getElementsByClassName('editing');\n      if (editing \u0026\u0026 editing.length \u003e 0) {\n        signal('SAVE')(); // invoke singal inner callback\n      }\n\n      var new_todo = document.getElementById('new-todo');\n      if(new_todo.value.length \u003e 0) {\n        signal('ADD')(); // invoke singal inner callback\n        new_todo.value = ''; // reset \u003cinput\u003e so we can add another todo\n        document.getElementById('new-todo').focus();\n      }\n      break;\n  }\n});\n```\n\nWhen you run the tests: `node test/todo-app.test.js`\nthey should now _pass_:\n![save-update-test-pass](https://user-images.githubusercontent.com/194400/45188350-d879d100-b22b-11e8-8669-94e080a25ef7.png)\n\n\n### 5.4 `'SAVE'` a _Blank_ item.title _deletes_ the item _Test_\n\nOur mini-mission is to make the following TodoMVC test assertion _pass_:\n\n```\n✓ should remove the item if an empty text string was entered (1033ms)\n```\n\nAppend following test code to your `test/todo-app.test.js` file:\n\n```js\ntest.only('5.4 SAVE should remove the item if an empty text string was entered',\n  function (t) {\n  elmish.empty(document.getElementById(id));\n  localStorage.removeItem('todos-elmish_' + id);\n  const model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false },\n      { id: 1, title: \"Let's solve our own problem\", done: false }\n    ],\n    hash: '#/', // the \"route\" to display\n    editing: 1 // edit the 3rd todo list item (which has id == 2)\n  };\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  t.equal(document.querySelectorAll('.view').length, 2, 'todo count: 2');\n  // apply empty string to the \u003cinput class=\"edit\"\u003e:\n  document.querySelectorAll('.edit')[0].value = '';\n  // trigger the [Enter] keyboard key to ADD the new todo:\n  document.dispatchEvent(new KeyboardEvent('keyup', {'keyCode': 13}));\n  // confirm that the todo item was removed!\n  t.equal(document.querySelectorAll('.view').length, 1, 'todo count: 1');\n  t.end();\n});\n```\nIf you attempt to run this test: `node test/todo-app.test.js`\nyou will see output similar to the following:\n\n![save-blank-title-test-failing](https://user-images.githubusercontent.com/194400/45188593-e4b25e00-b22c-11e8-9623-26c8b017e9b1.png)\n\n### 5.4 `'SAVE'` a _Blank_ item.title _deletes_ the item _Implementation_\n\nTo make this test pass we just need to add a couple of lines to the\n`'SAVE'` case in the `update` function:\n\n```js\nif (!value || value.length === 0) { // delete item if title is blank:\n  return update('DELETE', new_model, id);\n}\n```\n\nwhen you _re-run_ the tests, they will _pass_:\n\n![save-blank-title-test-pass](https://user-images.githubusercontent.com/194400/45188666-41ae1400-b22d-11e8-8154-176b5aaaea42.png)\n\n### 5.5 `'CANCEL'` edit on [esc] Key Press\n\nWhen a user presses the [esc] (\"escape\") key, editing should be \"cancelled\"\nwithout saving the changes:\n\n```\n✓ should cancel edits on escape\n```\n\n#### 5.5 `'CANCEL'` edit on [esc] _Test_\n\n\nAppend following test code to your `test/todo-app.test.js` file:\n\n```js\ntest.only('5.5 CANCEL should cancel edits on escape', function (t) {\n  elmish.empty(document.getElementById(id));\n  localStorage.removeItem('todos-elmish_' + id);\n  const model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false },\n      { id: 1, title: \"Let's solve our own problem\", done: false }\n    ],\n    hash: '#/', // the \"route\" to display\n    editing: 1 // edit the 3rd todo list item (which has id == 2)\n  };\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  t.equal(document.querySelectorAll('.view \u003e label')[1].value,\n    model.todos[1].title, 'todo id 1 has title: ' + model.todos[1].title);\n  // apply empty string to the \u003cinput class=\"edit\"\u003e:\n  document.querySelectorAll('.edit')[0].value = 'Hello World';\n  // trigger the [esc] keyboard key to CANCEL editing\n  document.dispatchEvent(new KeyboardEvent('keyup', {'keyCode': 27}));\n  // confirm the item.title is still the original title:\n  t.equal(document.querySelectorAll('.view \u003e label')[1].value,\n      model.todos[1].title, 'todo id 1 has title: ' + model.todos[1].title);\n  t.end();\n});\n```\nIf you attempt to run this test: `node test/todo-app.test.js`\nit should fail.\n\n### 5.5 `'CANCEL'` edit on [esc] _Implementation_\n\nTo make this test pass we _first_ need to add a `'CANCEL'`\n`case` to the `update` function:\n\n```js\ncase 'CANCEL':\n  new_model.clicked = false;\n  new_model.editing = false;\n  break;\n```\n_Second_ we need to _trigger_ the `'CANCEL'` action\nwhen the `[esc]` key is pressed, so we need to add a `case`\nto the `switch(e.keyCode) {` in the subscriptions event listener:\n\n_Before_:\n\n```js\ndocument.addEventListener('keyup', function handler (e) {\nswitch(e.keyCode) {\n  case ENTER_KEY:\n    var editing = document.getElementsByClassName('editing');\n    if (editing \u0026\u0026 editing.length \u003e 0) {\n      signal('SAVE')(); // invoke singal inner callback\n    }\n\n    var new_todo = document.getElementById('new-todo');\n    if(new_todo.value.length \u003e 0) {\n      signal('ADD')(); // invoke singal inner callback\n      new_todo.value = ''; // reset \u003cinput\u003e so we can add another todo\n      document.getElementById('new-todo').focus();\n    }\n    break;\n}\n});\n```\n\n\n_After_:\n\n```js\ndocument.addEventListener('keyup', function handler (e) {\n  console.log('e.keyCode:', e.keyCode, '| key:', e.key);\n\n  switch(e.keyCode) {\n    case ENTER_KEY:\n      var editing = document.getElementsByClassName('editing');\n      if (editing \u0026\u0026 editing.length \u003e 0) {\n        signal('SAVE')(); // invoke singal inner callback\n      }\n\n      var new_todo = document.getElementById('new-todo');\n      if(new_todo.value.length \u003e 0) {\n        signal('ADD')(); // invoke singal inner callback\n        new_todo.value = ''; // reset \u003cinput\u003e so we can add another todo\n        document.getElementById('new-todo').focus();\n      }\n      break;\n    case ESCAPE_KEY:\n      signal('CANCEL')();\n      break;\n  }\n});\n```\n\nwhen you re-run the tests, they will pass:\n![cancel-editing-on-esc-keypress-test-passing](https://user-images.githubusercontent.com/194400/45189286-15e05d80-b230-11e8-938c-4df80e49fda9.png)\n\n### 6. Counter\n\n```\n✓ should display the current number of todo items\n```\n\n#### 6. Counter _Test_\n\nAppend following test code to your `test/todo-app.test.js` file:\n\n```js\ntest.only('6. Counter \u003e should display the current number of todo items',\n  function (t) {\n  elmish.empty(document.getElementById(id));\n  const model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false },\n      { id: 1, title: \"Bootstrap for as long as you can\", done: false },\n      { id: 2, title: \"Let's solve our own problem\", done: false }\n    ],\n    hash: '#/'\n  };\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  // count:\n  const count = parseInt(document.getElementById('count').textContent, 10);\n  t.equal(count, model.todos.length, \"displays todo item count: \" + count);\n\n  elmish.empty(document.getElementById(id)); // clear DOM ready for next test\n  localStorage.removeItem('todos-elmish_' + id);\n  t.end();\n});\n```\n\nThankfully, the counter was already implemented above\nso this test **_already_ passes**:\n\n![counter-test-passing](https://user-images.githubusercontent.com/194400/45190621-ca7d7d80-b236-11e8-92b3-5a4fbda6f12a.png)\n\nJust keep on [_movin_'](https://youtu.be/uvRBUw_Ls2o)\n\n\n### 7. Clear Completed Button\n\nWhen items are complete we should be able to `delete` them in bulk.\n\n```\n✓ should display the number of completed items\n✓ should remove completed items when clicked\n✓ should be hidden when there are no items that are completed\n```\n\n#### 7. Clear Completed Button _Test_\n\nAppend following test code to your `test/todo-app.test.js` file:\n\n```js\ntest.only('7. Clear Completed \u003e should display the number of completed items',\n  function (t) {\n  elmish.empty(document.getElementById(id));\n  const model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false },\n      { id: 1, title: \"Bootstrap for as long as you can\", done: true },\n      { id: 2, title: \"Let's solve our own problem\", done: true }\n    ],\n    hash: '#/'\n  };\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  // count todo items in DOM:\n  t.equal(document.querySelectorAll('.view').length, 3,\n    \"at the start, there are 3 todo items in the DOM.\");\n\n  // count completed items\n  const completed_count =\n    parseInt(document.getElementById('completed-count').textContent, 10);\n  const done_count = model.todos.filter(function(i) {return i.done }).length;\n  t.equal(completed_count, done_count,\n    \"displays completed items count: \" + completed_count);\n\n  // clear completed items:\n  const button = document.querySelectorAll('.clear-completed')[0];\n  button.click();\n\n  // confirm that there is now only ONE todo list item in the DOM:\n  t.equal(document.querySelectorAll('.view').length, 1,\n    \"after clearing completed items, there is only 1 todo item in the DOM.\");\n\n  // no clear completed button in the DOM when there are no \"done\" todo items:\n  t.equal(document.querySelectorAll('clear-completed').length, 0,\n    'no clear-completed button when there are no done items.')\n\n  elmish.empty(document.getElementById(id)); // clear DOM ready for next test\n  localStorage.removeItem('todos-elmish_' + id);\n  t.end();\n});\n```\n\n#### 7. Clear Completed Button _Implementation_\n\nFirst we need to update the `button` section in the `render_footer` function\nto include the `done` count:\n\n_Before:_\n\n```js\nbutton([\"class=clear-completed\", \"style=display:\" + display_clear],\n  [\n    text(\"Clear completed\")\n  ]\n)\n```\n\n_After:_\n\n```js\nbutton([\"class=clear-completed\", \"style=display:\" + display_clear,\n  signal('CLEAR_COMPLETED')\n  ],\n  [\n    text(\"Clear completed [\"),\n    span([\"id=completed-count\"], [\n      text(done)\n    ]),\n    text(\"]\")\n  ]\n)\n```\n\n_Seconde_ we need to add a `'CLEAR_COMPLETED'` `case` to the `update` function:\n\n```js\ncase 'CLEAR_COMPLETED':\n  new_model.todos = new_model.todos.filter(function (item) {\n    return !item.done; // only return items which are item.done = false\n  });\n  break;\n```\n\nThe tests should pass:\n\n![clear-completed-button-tests-passing](https://user-images.githubusercontent.com/194400/45191359-a58b0980-b23a-11e8-91bf-dcb016d6f4bb.png)\n\n\u003cbr /\u003e\n\n### 8. Persistence \u003e Save Todo List items to `localStorage`\n\n```\n✓ should persist its data\n```\n\n#### 8. Persistence _Test_\n\nWe have already covered saving the `model`\nto `localStorage` in _detail_ (_above_),\nwe are adding a \"proxy\" test for completeness:\n\n```js\ntest.only('8. Persistence \u003e should persist its data', function (t) {\n  elmish.empty(document.getElementById(id));\n  const model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false }\n    ],\n    hash: '#/'\n  };\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  // confirm that the model is saved to localStorage\n  console.log('localStorage', localStorage.getItem('todos-elmish_' + id));\n  t.equal(localStorage.getItem('todos-elmish_' + id),\n    JSON.stringify(model), \"data is persisted to localStorage\");\n\n  elmish.empty(document.getElementById(id)); // clear DOM ready for next test\n  localStorage.removeItem('todos-elmish_' + id);\n  t.end();\n});\n```\n\nAgain, this test should _already_ pass:\n\n![persistence-test-passing](https://user-images.githubusercontent.com/194400/45190477-0a903080-b236-11e8-991c-bab61efb3d22.png)\n\n### 9. Routing\n\nThe following assertions:\n```\n✓ should allow me to display active items\n✓ should allow me to display completed items\n✓ should allow me to display all items\n✓ should highlight the currently applied filter\n```\n\n+ `'SHOW_ALL'` the default view.\n+ `'SHOW_ACTIVE'` item.done === false\n+ `'SHOW_COMPLETED'` item.done === true\n\n\n#### 9. Routing _Test_\n\nAppend following test code to your `test/todo-app.test.js` file:\n\n```js\ntest.only('9. Routing \u003e should allow me to display active/completed/all items',\n  function (t) {\n  elmish.empty(document.getElementById(id));\n  const model = {\n    todos: [\n      { id: 0, title: \"Make something people want.\", done: false },\n      { id: 1, title: \"Bootstrap for as long as you can\", done: true },\n      { id: 2, title: \"Let's solve our own problem\", done: true }\n    ],\n    hash: '#/active' // ONLY ACTIVE items\n  };\n  // render the view and append it to the DOM inside the `test-app` node:\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  t.equal(document.querySelectorAll('.view').length, 1, \"one active item\");\n  let selected = document.querySelectorAll('.selected')[0]\n  t.equal(selected.id, 'active', \"active footer filter is selected\");\n\n  // empty:\n  elmish.empty(document.getElementById(id));\n  localStorage.removeItem('todos-elmish_' + id);\n  // show COMPLTED items:\n  model.hash = '#/completed';\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  t.equal(document.querySelectorAll('.view').length, 2,\n    \"two completed items\");\n  selected = document.querySelectorAll('.selected')[0]\n  t.equal(selected.id, 'completed', \"completed footer filter is selected\");\n\n  // empty:\n  elmish.empty(document.getElementById(id));\n  localStorage.removeItem('todos-elmish_' + id);\n  // show ALL items:\n  model.hash = '#/';\n  elmish.mount(model, app.update, app.view, id, app.subscriptions);\n  t.equal(document.querySelectorAll('.view').length, 3,\n    \"three items total\");\n  selected = document.querySelectorAll('.selected')[0]\n  t.equal(selected.id, 'all', \"all footer filter is selected\");\n\n  elmish.empty(document.getElementById(id)); // clear DOM ready for next test\n  localStorage.removeItem('todos-elmish_' + id);\n  t.end();\n});\n```\n\n\n#### 9. Routing _Implementation_\n\nGiven that we are using \"hash\" based routing,\nwhere the content of the app changes in response to the hash portion of the URL\nimplementing routing is a matter of _filtering_ the Todo List items\nin response to the hash.\n\nThere 3 steps to implementing this:\n\n1. Create an Event Listener for the `window.onhashchange` event\nwhich invokes `signal('ROUTE')`.\n\n2. Create a `'ROUTE'` case in the `update` function\nwhich sets the `model.hash` value.\n\n3. Based on the `model.hash` value defined above,\nfilter the `model.todos`.\n\nSince this is the _final_ quest in the TodoMVC/Todo List App,\nthe we encourage you to attempt to write this\nbefore/without looking at the \"solution\".\n\nRemember that you only want to write the _minimum_ code\nnecessary to make the test assertions pass.\n\nIf you get \"_stuck_\" consult the code in `todo-app.js`.\n\n\n#### 9.1 Routing _Event Listener_\n\nAdd the following event listener to your `subscriptions`\nto \"listen\" for when the URL hash changes:\n\n\n```js\nwindow.onhashchange = function route () {\n  signal('ROUTE')();\n}\n```\n\n#### 9.2 ROUTE `case`\n\nAdd the `'ROUTE'` `case`\nto your `update` function:\n\n```js\ncase 'ROUTE':\n  new_model.hash = (window \u0026\u0026 window.location \u0026\u0026 window.location.hash) ?\n    window.location.hash : '#/';\n  break;\n```\n***OR***, if you are confident that your app\nwill _always_ run in a Web Browser with a `window.location.hash` property:\n\n```js\ncase 'ROUTE':\n  new_model.hash = window.location.hash;\n  break;\n```\n\n#### But _Why...?_\n\n**Question**: Why do we \"copy\" the `window.location.hash`\nto `model.hash` instead of just \"getting\" it from `window.location.hash`\neach time we need to know what the hash is? \u003cbr /\u003e\n\n**Answer**: technically, we could _avoid_ having\nthe `'ROUTE'` case in `update` completely\nand just use the `window.location.hash`\ninstead of `model.hash`,\nthe _reason_ we add this \"step\"\nis that we want to have a \"single source of truth\" in the `model`.\nThis is a _good_ habit to have\nas it makes _debugging_ your application\n_much_ easier because you _know **exactly**_\nwhat the \"full state\" of the application is/was at any point in time.  \n\nYou will often read/hear the expression \"_easier to **reason about**_\",\nall this means is that you can \"work through\" something in your head\nwithout getting \"confused\" by having \"too many things to keep track of\".\n\n\n#### 9.3 _Filter_ the `model.todos` based on `model.hash`\n\nWe need to do the filtering \"_non-destructively_\",\nso it needs to happen in the **`view`** function `render_main`\n(_just before rendering_).\n\n`render_main` function _Before_ (_without filter_):\n\n```js\nfunction render_main (model, signal) {\n  // Requirement #1 - No Todos, should hide #footer and #main\n  var display = \"style=display:\"\n    + (model.todos \u0026\u0026 model.todos.length \u003e 0 ? \"block\" : \"none\");\n  // console.log('display:', display);\n  return (\n    section([\"class=main\", \"id=main\", display], [ // hide if no todo items.\n      input([\"id=toggle-all\", \"type=checkbox\",\n        typeof signal === 'function' ? signal('TOGGLE_ALL') : '',\n        (model.all_done ? \"checked=checked\" : \"\"),\n        \"class=toggle-all\"\n      ], []),\n      label([\"for=toggle-all\"], [ text(\"Mark all as complete\") ]),\n      ul([\"class=todo-list\"],\n        (model.todos \u0026\u0026 model.todos.length \u003e 0) ?\n        model.todos.map(function (item) {\n          return render_item(item, model, signal)\n        }) : null\n      ) // \u003c/ul\u003e\n    ]) // \u003c/section\u003e\n  )\n}\n\n```\n\n`render_main` function _After_ (_with `model.hash` filter_):\n\n```js\nfunction render_main (model, signal) {\n  // Requirement #1 - No Todos, should hide #footer and #main\n  var display = \"style=display:\"\n    + (model.todos \u0026\u0026 model.todos.length \u003e 0 ? \"block\" : \"none\");\n  // console.log('display:', display);\n  return (\n    section([\"class=main\", \"id=main\", display], [ // hide if no todo items.\n      input([\"id=toggle-all\", \"type=checkbox\",\n        typeof signal === 'function' ? signal('TOGGLE_ALL') : '',\n        (model.all_done ? \"checked=checked\" : \"\"),\n        \"class=toggle-all\"\n      ], []),\n      label([\"for=toggle-all\"], [ text(\"Mark all as complete\") ]),\n      ul([\"class=todo-list\"],\n        (model.todos \u0026\u0026 model.todos.length \u003e 0) ?\n        model.todos\n        .filter(function (item) {\n          switch(model.hash) {\n            case '#/active':\n              return !item.done;\n            case '#/completed':\n              return item.done;\n            default: // if hash doesn't match Active/Completed render ALL todos:\n              return item;\n          }\n        })\n        .map(function (item) {\n          return render_item(item, model, signal)\n        }) : null // if there are no todos, don't show anything.\n      ) // \u003c/ul\u003e\n    ]) // \u003c/section\u003e\n  )\n}\n```\nThe important lines are:\n```js\n.filter(function (item) {\n  switch(model.hash) {\n    case '#/active':\n      return !item.done;\n    case '#/completed':\n      return item.done;\n    default: // if hash doesn't match Active/Completed render ALL todos:\n      return item;\n  }\n})\n```\n`Array.filter` returns a ***`new`*** Array\n(_it does not \"mutate\" the Array it is filtering_)\nso we will only see the todo items that match the `hash` in the URL.\n`'#/active'` means any todos which are not yet done i.e. `!done`\nand `'#/completed'` are the items which are `done=true`.\nIf the URL `hash` does not match either of these two filters,\nthen simply \"show everything\".\n\n\u003e _**Question**: is this \"**logic in the view**\"...?_ \u003cbr /\u003e\n\u003e _**Answer**: **Yes**, it is **presentation logic**.\nThe `view` function, **`render_main` in this case\nis merely **filtering** the data **non-destructively** before rendering it.\nUsing `Array.filter` is a \"fancy\" (concise) way of writing an `if` statement.\n`if` statements are \"OK\" in views because they are\n\"conditional presentation logic\"\ni.e. only show this section `if` a certain variable is set. \u003cbr /\u003e_\n_By using `Array.filter` followed by `Array.map` we render a **subset**\nof the `model.todos` **without** \"**mutating**\" the `model.todos` Array.\nIn other words if the URL hash is `'#/completed'`\nthe user only wants to see the \"completed\" items,\nwe don't want to \"lose\" the todos that are not yet complete,\nwe just want to \"hide\" them temporarily,\nif we were to apply this filter in the `update` function it would\n\"lose\" the other todos (i.e. destroy the data!)\nthe best way to filter data non-destructively is in the **view**_\n\n# Done!\n\nIn your terminal, run:\n\n```sh\nnpm start\n```\n\nYou should have a fully-featured Todo list App!\n\n![elm-todo](https://user-images.githubusercontent.com/194400/45237254-10d5e980-b2d6-11e8-8281-b95452bde519.gif)\n\nTry out your Todo List App!\n\nIf you found this tutorial _useful_,\nplease \"star\" the project on GitHub ⭐️ to show your appreciation\nand share it with others in the community who might find it useful! Thanks! ✨\n\nConsider sharing your creation with your friends\nby deploying it to GitHub Pages!\nhttps://github.com/dwyl/learn-github-pages\n\n\n# Thanks for Learning with Us!\n\n\n\u003c!--\n## What _Next_?\n\nIf you feel _confident_ with your \"TEA\" skills you can _either_:\n+ **`try`** and use them to **create your _own_ App** using \"TEA\"\ne.g: https://github.com/nelsonic/time-mvp\n+ Move on and **Learn Elm**: https://github.com/dwyl/learn-elm\n+ (Join the herd and) Learn \u0026 use React/Redux.\n--\u003e\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwyl%2Fjavascript-todo-list-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdwyl%2Fjavascript-todo-list-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwyl%2Fjavascript-todo-list-tutorial/lists"}