@@ -110,65 +110,8 @@ which we will cover in **section 5.5** below, but for now we are
110
110
only considering the "happy path" which results in a successful edit._
111
111
112
112
113
- #### 5.1 Double-Click to Edit
114
-
115
- The TodoMVC *** spec*** for item
116
- https://github.com/tastejs/todomvc/blob/master/app-spec.md#item
117
- includes the line:
118
-
119
- ``` sh
120
- Double-clicking the < label> activates editing mode, by toggling the .editing class on its < li>
121
- ```
122
-
123
- > _ ** Note** : the sample TodoMVC Browser Tests:
124
- https://github.com/tastejs/todomvc/tree/master/tests#example-output
125
- does ** not** include a test-case for ** double-clicking** .
126
- We are going to add one below for "extra credit"._
127
-
128
- Since Double-clicking/tapping is the _ only_ way to edit a todo item,
129
- we feel that it deserves a test.
130
-
131
- When we don't know how to do something, a good place to start is to search
132
- for the keywords we want, e.g: "JavaScript detect double-click event"
133
- for which the top result is the following StackOverflow Q/A:
134
- https://stackoverflow.com/questions/5497073/how-to-differentiate-single-click-event-and-double-click-event
135
- Reading though all the answers, we determine that the most relevant (_ to us_ )
136
- is: https://stackoverflow.com/a/16033129/1148249 (_ which uses "vanilla" JS_ )
137
-
138
- [ ![ stackoverflow-double-click-example] ( https://user-images.githubusercontent.com/194400/45124122-14942f80-b161-11e8-94c0-f54f2352bdd5.png )] ( https://stackoverflow.com/a/16033129/1148249 )
139
-
140
- > _ ** Note** : when you find a StackOverflow question/answer ** helpful, upvote** !_
141
-
142
- ``` html
143
- <div onclick =" doubleclick(this, function(){alert('single')}, function(){alert('double')})" >click me</div >
144
- <script >
145
- function doubleclick (el , onsingle , ondouble ) {
146
- if (el .getAttribute (" data-dblclick" ) == null ) {
147
- el .setAttribute (" data-dblclick" , 1 );
148
- setTimeout (function () {
149
- if (el .getAttribute (" data-dblclick" ) == 1 ) {
150
- onsingle ();
151
- }
152
- el .removeAttribute (" data-dblclick" );
153
- }, 300 );
154
- } else {
155
- el .removeAttribute (" data-dblclick" );
156
- ondouble ();
157
- }
158
- }
159
- </script >
160
- ```
161
- Given that we are using the Elm Architecture to manage the DOM,
162
- we don't want a function that _ alters_ the DOM.
163
- So we are going to _ borrow_ the _ logic_ from this example but _ simplify_ it.
164
- Since we are not mutating the DOM by setting ` data-dblclick ` attributes,
165
- we won't need to remove the attribute using a ` setTimeout ` ,
166
-
167
-
168
-
169
113
170
-
171
- #### 5.2 ` render_item ` view function with "Edit Mode" ` <input class="edit"> `
114
+ #### 5.1 ` render_item ` view function with "Edit Mode" ` <input class="edit"> `
172
115
173
116
In order to edit an item the ** ` render_item ` ** function
174
117
will require ** 3 modifications** :
@@ -393,3 +336,227 @@ node test/todo-app.test.js
393
336
But we are building a _visual_ application and are not _seeing_ anything ...
394
337
395
338
#### Visualise Editing Mode?
339
+
340
+ Let's take a _brief_ detour to _visualise_ the progress we have made.
341
+
342
+ Open the `examples/todo-list/index.html` file
343
+ and alter the contents of the `<script>` tag:
344
+ ```html
345
+ <script>
346
+ var model = {
347
+ todos: [
348
+ { id: 0 , title: " Make something people want." , done: false },
349
+ { id: 1 , title: " Bootstrap for as long as you can" , done: false },
350
+ { id: 2 , title: " Let's solve our own problem" , done: false }
351
+ ],
352
+ hash: ' #/' , // the "route" to display
353
+ editing: 2 // edit the 3rd todo list item (which has id == 2)
354
+ };
355
+ mount (model, update, view, ' app' , subscriptions);
356
+ < / script>
357
+ ```
358
+
359
+ Then in your terminal, start the live-server:
360
+ ``` sh
361
+ npm start
362
+ ```
363
+ In your browser, vist: http://127.0.0.1:8000/examples/todo-list/
364
+ You should see that the _ third_ todo list item is in "edit mode".
365
+ ![ elm-todomvc-editing-item] ( https://user-images.githubusercontent.com/194400/45180706-0eab5680-b214-11e8-9dcf-a8c4476e4b11.png )
366
+
367
+ Nothing will happen (_ yet_ ) if you attempt to "save" any changes.
368
+ Let's work on the ` case ` (_ handler_ ) for ** ` signal('EDIT', item.id) ` **
369
+ which will handle the "double-click" event and set ` model.editing ` .
370
+
371
+
372
+ #### 5.2 Double-Click item ` <label> ` to Edit
373
+
374
+ The TodoMVC *** spec*** for item
375
+ https://github.com/tastejs/todomvc/blob/master/app-spec.md#item
376
+ includes the line:
377
+
378
+ ``` sh
379
+ Double-clicking the < label> activates editing mode, by toggling the .editing class on its < li>
380
+ ```
381
+
382
+ > _ ** Note** : the sample TodoMVC Browser Tests:
383
+ https://github.com/tastejs/todomvc/tree/master/tests#example-output
384
+ does ** not** include a test-case for ** double-clicking** .
385
+ We are going to add one below for "extra credit"._
386
+
387
+ Since Double-clicking/tapping is the _ only_ way to edit a todo item,
388
+ we feel that it deserves a test.
389
+
390
+ When we don't know how to do something, a good place to start is to search
391
+ for the keywords we want, e.g: "JavaScript detect double-click event"
392
+ for which the top result is the following StackOverflow Q/A:
393
+ https://stackoverflow.com/questions/5497073/how-to-differentiate-single-click-event-and-double-click-event
394
+ Reading though all the answers, we determine that the most relevant (_ to us_ )
395
+ is: https://stackoverflow.com/a/16033129/1148249 (_ which uses "vanilla" JS_ )
396
+
397
+ [ ![ stackoverflow-double-click-example] ( https://user-images.githubusercontent.com/194400/45124122-14942f80-b161-11e8-94c0-f54f2352bdd5.png )] ( https://stackoverflow.com/a/16033129/1148249 )
398
+
399
+ > _ ** Note** : when you find a StackOverflow question/answer ** helpful, upvote** !_
400
+
401
+ ``` html
402
+ <div onclick =" doubleclick(this, function(){alert('single')}, function(){alert('double')})" >click me</div >
403
+ <script >
404
+ function doubleclick (el , onsingle , ondouble ) {
405
+ if (el .getAttribute (" data-dblclick" ) == null ) {
406
+ el .setAttribute (" data-dblclick" , 1 );
407
+ setTimeout (function () {
408
+ if (el .getAttribute (" data-dblclick" ) == 1 ) {
409
+ onsingle ();
410
+ }
411
+ el .removeAttribute (" data-dblclick" );
412
+ }, 300 );
413
+ } else {
414
+ el .removeAttribute (" data-dblclick" );
415
+ ondouble ();
416
+ }
417
+ }
418
+ </script >
419
+ ```
420
+ Given that we are using the Elm Architecture to manage the DOM,
421
+ we don't want a function that _ alters_ the DOM.
422
+ So we are going to _ borrow_ the _ logic_ from this example but _ simplify_ it.
423
+ Since we are not mutating the DOM by setting ` data-dblclick ` attributes,
424
+ we won't need to remove the attribute using a ` setTimeout ` ,
425
+
426
+
427
+ ### 5.2 ` 'EDIT' update case ` _ Test_
428
+
429
+ In keeping with our TDD approach,
430
+ our first step when adding the ` case ` expression
431
+ for ` 'EDIT' ` in the ` update ` function is to write a _ test_ .
432
+
433
+ Append following test code to your ` test/todo-app.test.js ` file:
434
+
435
+ ``` js
436
+ test .only (' 5.2 Double-click an item <label> to edit it' , function (t ) {
437
+ elmish .empty (document .getElementById (id));
438
+ localStorage .removeItem (' todos-elmish_' + id);
439
+ const model = {
440
+ todos: [
441
+ { id: 0 , title: " Make something people want." , done: false },
442
+ { id: 1 , title: " Let's solve our own problem" , done: false }
443
+ ],
444
+ hash: ' #/' // the "route" to display
445
+ };
446
+ // render the view and append it to the DOM inside the `test-app` node:
447
+ elmish .mount (model, app .update , app .view , id, app .subscriptions );
448
+ const label = document .querySelectorAll (' .view > label' )[1 ]
449
+ // "double-click" i.e. click the <label> twice in quick succession:
450
+ label .click ();
451
+ label .click ();
452
+ // confirm that we are now in editing mode:
453
+ t .equal (document .querySelectorAll (' .editing' ).length , 1 ,
454
+ " <li class='editing'> element is visible" );
455
+ t .equal (document .querySelectorAll (' .edit' )[0 ].value , model .todos [1 ].title ,
456
+ " <input class='edit'> has value: " + model .todos [1 ].title );
457
+ t .end ();
458
+ });
459
+ ```
460
+ If you attempt to run this test: ` node test/todo-app.test.js `
461
+ you will see output similar to the following:
462
+
463
+ ![ edit-double-click-test-failing] ( https://user-images.githubusercontent.com/194400/45183202-54b7e880-b21b-11e8-84d8-7b3b50162113.png )
464
+
465
+ Let's write the code necessary to make the test assertions _ pass_ !
466
+ If you want to try this yourself based on the StackOverflow answer (_ above_ ),
467
+ go for it! (_ don't scroll down to the "answer" till you have tried..._ )
468
+
469
+ ### 5.2 ` 'EDIT' update case ` _ Implementation_
470
+
471
+ Given our "research" (_ above_ ) of how to implement a "double-click" handler,
472
+ we can write the ` 'EDIT' ` case as the following:
473
+
474
+ ``` js
475
+ case ' EDIT' :
476
+ // this code is inspired by: https://stackoverflow.com/a/16033129/1148249
477
+ // simplified as we are not altering the DOM!
478
+ if (new_model .clicked && new_model .clicked === data &&
479
+ Date .now () - 300 < new_model .click_time ) { // DOUBLE-CLICK < 300ms
480
+ new_model .editing = data;
481
+ console .log (' DOUBLE-CLICK' , " item.id=" , data,
482
+ " | model.editing=" , model .editing ,
483
+ " | diff Date.now() - new_model.click_time: " ,
484
+ Date .now (), " -" , new_model .click_time , " =" ,
485
+ Date .now () - new_model .click_time );
486
+ }
487
+ else { // first click
488
+ new_model .clicked = data; // so we can check if same item clicked twice!
489
+ new_model .click_time = Date .now (); // timer to detect double-click 300ms
490
+ new_model .editing = false ; // reset
491
+ console .log (' FIRST CLICK! data:' , data);
492
+ }
493
+ break ;
494
+ ```
495
+ If you ignore/remove the ` console.log ` lines (_ which we are using for now!_ ),
496
+ the code is only a few lines long:
497
+ ``` js
498
+ case ' EDIT' :
499
+ // this code is inspired by: https://stackoverflow.com/a/16033129/1148249
500
+ // simplified as we are not altering the DOM!
501
+ if (new_model .clicked && new_model .clicked === data &&
502
+ Date .now () - 300 < new_model .click_time ) { // DOUBLE-CLICK < 300ms
503
+ new_model .editing = data;
504
+ }
505
+ else { // first click
506
+ new_model .clicked = data; // so we can check if same item clicked twice!
507
+ new_model .click_time = Date .now (); // timer to detect double-click 300ms
508
+ new_model .editing = false ; // reset
509
+ }
510
+ break ;
511
+ ```
512
+ The main "purpose" of this code is to _ detect_ if a ` <label> ` was clicked
513
+ twice in the space of 300 milliseconds and apply the ` item.id ` to
514
+ the ` model.editing ` property so that we know which ` <li> ` to render in
515
+ "editing mode".
516
+
517
+ Run the test and watch it _ pass_ : ` node test/todo-app.test.js `
518
+ ![ edit-double-click-test-pass] ( https://user-images.githubusercontent.com/194400/45183878-3bb03700-b21d-11e8-9842-be62113bfe0a.png )
519
+
520
+ In this case the time between the two clicks was 31 milliseconds,
521
+ so they will count as a "double-click"!
522
+
523
+
524
+ If a ` <label> ` is clicked slowly, the ` model.editing ` will _ not_ be set,
525
+ and we will _ not_ enter "editing mode".
526
+ Let's add a quick test for the scenario
527
+ where two clicks are more than 300ms apart.
528
+
529
+ Append following test code to your ` test/todo-app.test.js ` file:
530
+
531
+ ``` js
532
+ test .only (' 5.2.2 Slow clicks do not count as double-click > no edit!' , function (t ) {
533
+ elmish .empty (document .getElementById (id));
534
+ localStorage .removeItem (' todos-elmish_' + id);
535
+ const model = {
536
+ todos: [
537
+ { id: 0 , title: " Make something people want." , done: false },
538
+ { id: 1 , title: " Let's solve our own problem" , done: false }
539
+ ],
540
+ hash: ' #/' // the "route" to display
541
+ };
542
+ // render the view and append it to the DOM inside the `test-app` node:
543
+ elmish .mount (model, app .update , app .view , id, app .subscriptions );
544
+ const label = document .querySelectorAll (' .view > label' )[1 ]
545
+ // "double-click" i.e. click the <label> twice in quick succession:
546
+ label .click ();
547
+ setTimeout (function (){
548
+ label .click ();
549
+ // confirm that we are now in editing mode:
550
+ t .equal (document .querySelectorAll (' .editing' ).length , 0 ,
551
+ " <li class='editing'> element is NOT visible" );
552
+ t .end ();
553
+ }, 301 )
554
+ });
555
+ ```
556
+
557
+ There is no need to write any code to make this test pass,
558
+ this is merely an additional test to _ confirm_ that our check for the
559
+ time between clicks works; clicks spaced more than 300ms will not count
560
+ as "double-click".
561
+
562
+ ![ edit-item-not-double-click] ( https://user-images.githubusercontent.com/194400/45184155-ff310b00-b21d-11e8-8f6c-ef6d699861cf.png )
0 commit comments