From f610f69ab92827c49e9cbb528cacc26af10be081 Mon Sep 17 00:00:00 2001
From: Clement Ho <ClemMakesApps@gmail.com>
Date: Mon, 8 Aug 2016 16:19:46 -0500
Subject: [PATCH] Add todo toggle event

---
 CHANGELOG.md                                  |  1 +
 app/assets/javascripts/header.js              |  9 ++++
 .../javascripts/lib/utils/text_utility.js     |  3 ++
 app/assets/javascripts/right_sidebar.js       | 11 ++--
 app/assets/javascripts/sidebar.js.es6         |  7 ++-
 app/assets/javascripts/todos.js.es6           |  3 +-
 app/views/layouts/nav/_dashboard.html.haml    |  2 +-
 spec/features/todos/todos_spec.rb             |  1 -
 spec/javascripts/dashboard_spec.js.es6        | 38 +++++++++++++
 spec/javascripts/fixtures/dashboard.html.haml | 45 ++++++++++++++++
 spec/javascripts/fixtures/header.html.haml    | 35 ++++++++++++
 .../fixtures/right_sidebar.html.haml          |  4 ++
 spec/javascripts/fixtures/todos.json          |  4 ++
 spec/javascripts/header_spec.js               | 54 +++++++++++++++++++
 spec/javascripts/right_sidebar_spec.js        | 19 ++++++-
 15 files changed, 223 insertions(+), 13 deletions(-)
 create mode 100644 app/assets/javascripts/header.js
 create mode 100644 spec/javascripts/dashboard_spec.js.es6
 create mode 100644 spec/javascripts/fixtures/dashboard.html.haml
 create mode 100644 spec/javascripts/fixtures/header.html.haml
 create mode 100644 spec/javascripts/fixtures/todos.json
 create mode 100644 spec/javascripts/header_spec.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 86878e5af6c..197a74fd4b6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -77,6 +77,7 @@ Please view this file on the master branch, on stable branches it's out of date.
   - Add more tests for calendar contribution (ClemMakesApps)
   - Update Gitlab Shell to fix some problems with moving projects between storages
   - Cache rendered markdown in the database, rather than Redis
+  - Add todo toggle event (ClemMakesApps)
   - Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references
   - Simplify Mentionable concern instance methods
   - API: Ability to retrieve version information (Robert Schilling)
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
new file mode 100644
index 00000000000..5215ce9c4ba
--- /dev/null
+++ b/app/assets/javascripts/header.js
@@ -0,0 +1,9 @@
+(function() {
+
+  $(document).on('todo:toggle', function(e, count) {
+    var $todoPendingCount = $('.todos-pending-count');
+    $todoPendingCount.text(gl.text.addDelimiter(count));
+    $todoPendingCount.toggleClass('hidden', count === 0);
+  });
+
+})();
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index d761a844be9..6206c7f9237 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -7,6 +7,9 @@
     if ((base = w.gl).text == null) {
       base.text = {};
     }
+    gl.text.addDelimiter = function(text) {
+      return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
+    }
     gl.text.randomString = function() {
       return Math.random().toString(36).substring(7);
     };
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index e3d5f413c77..9f2385a3de8 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -74,16 +74,11 @@
     };
 
     Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) {
-      var $todoPendingCount;
-      $todoPendingCount = $('.todos-pending-count');
-      $todoPendingCount.text(data.count);
+      $(document).trigger('todo:toggle', data.count);
+
       $btn.enable();
       $todoLoading.addClass('hidden');
-      if (data.count === 0) {
-        $todoPendingCount.addClass('hidden');
-      } else {
-        $todoPendingCount.removeClass('hidden');
-      }
+
       if (data.delete_path != null) {
         $btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path);
         return $btnText.text($btn.data('mark-text'));
diff --git a/app/assets/javascripts/sidebar.js.es6 b/app/assets/javascripts/sidebar.js.es6
index 755fac8107b..50c9440de5f 100644
--- a/app/assets/javascripts/sidebar.js.es6
+++ b/app/assets/javascripts/sidebar.js.es6
@@ -37,7 +37,8 @@
         .on('click', sidebarToggleSelector, () => this.toggleSidebar())
         .on('click', pinnedToggleSelector, () => this.togglePinnedState())
         .on('click', 'html, body', (e) => this.handleClickEvent(e))
-        .on('page:change', () => this.renderState());
+        .on('page:change', () => this.renderState())
+        .on('todo:toggle', (e, count) => this.updateTodoCount(count));
       this.renderState();
     }
 
@@ -52,6 +53,10 @@
       }
     }
 
+    updateTodoCount(count) {
+      $('.js-todos-count').text(gl.text.addDelimiter(count));
+    }
+
     toggleSidebar() {
       this.isExpanded = !this.isExpanded;
       this.renderState();
diff --git a/app/assets/javascripts/todos.js.es6 b/app/assets/javascripts/todos.js.es6
index 055228c5df8..42539ef43e2 100644
--- a/app/assets/javascripts/todos.js.es6
+++ b/app/assets/javascripts/todos.js.es6
@@ -97,7 +97,8 @@
     }
 
     updateBadges(data) {
-      $('.todos-pending .badge, .todos-pending-count').text(data.count);
+      $(document).trigger('todo:toggle', data.count);
+      $('.todos-pending .badge').text(data.count);
       return $('.todos-done .badge').text(data.done_count);
     }
 
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 67f558c854b..3b1295dc3c0 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -7,7 +7,7 @@
     = link_to dashboard_todos_path, title: 'Todos' do
       %span
         Todos
-        %span.count= number_with_delimiter(todos_pending_count)
+        %span.count.js-todos-count= number_with_delimiter(todos_pending_count)
   = nav_link(path: 'dashboard#activity') do
     = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
       %span
diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb
index bf93c1d1251..230543cd175 100644
--- a/spec/features/todos/todos_spec.rb
+++ b/spec/features/todos/todos_spec.rb
@@ -132,7 +132,6 @@ describe 'Dashboard Todos', feature: true do
         end
 
         it 'shows "All done" message!' do
-          within('.todos-pending-count') { expect(page).to have_content '0' }
           expect(page).to have_content 'To do 0'
           expect(page).to have_content "You're all done!"
           expect(page).not_to have_selector('.gl-pagination')
diff --git a/spec/javascripts/dashboard_spec.js.es6 b/spec/javascripts/dashboard_spec.js.es6
new file mode 100644
index 00000000000..8021145d8e8
--- /dev/null
+++ b/spec/javascripts/dashboard_spec.js.es6
@@ -0,0 +1,38 @@
+/*= require sidebar */
+/*= require jquery */
+/*= require jquery.cookie */
+/*= require lib/utils/text_utility */
+
+((global) => {
+  describe('Dashboard', () => {
+    const fixtureTemplate = 'dashboard.html';
+
+    function todosCountText() {
+      return $('.js-todos-count').text();
+    }
+
+    function triggerToggle(newCount) {
+      $(document).trigger('todo:toggle', newCount);
+    }
+
+    fixture.preload(fixtureTemplate);
+    beforeEach(() => {
+      fixture.load(fixtureTemplate);
+      new global.Sidebar();
+    });
+
+    it('should update todos-count after receiving the todo:toggle event', () => {
+      triggerToggle(5);
+      expect(todosCountText()).toEqual('5');
+    });
+
+    it('should display todos-count with delimiter', () => {
+      triggerToggle(1000);
+      expect(todosCountText()).toEqual('1,000');
+
+      triggerToggle(1000000);
+      expect(todosCountText()).toEqual('1,000,000');
+    });
+  });
+
+})(window.gl);
diff --git a/spec/javascripts/fixtures/dashboard.html.haml b/spec/javascripts/fixtures/dashboard.html.haml
new file mode 100644
index 00000000000..32446acfd60
--- /dev/null
+++ b/spec/javascripts/fixtures/dashboard.html.haml
@@ -0,0 +1,45 @@
+%ul.nav.nav-sidebar
+  %li.home.active
+    %a.dashboard-shortcuts-projects
+      %span
+        Projects
+  %li
+    %a
+      %span
+        Todos
+        %span.count.js-todos-count
+        1
+  %li
+    %a.dashboard-shortcuts-activity
+      %span
+        Activity
+  %li
+    %a
+      %span
+        Groups
+  %li
+    %a
+      %span
+        Milestones
+  %li
+    %a.dashboard-shortcuts-issues
+      %span
+        Issues
+        %span
+        1
+  %li
+    %a.dashboard-shortcuts-merge_requests
+      %span
+        Merge Requests
+  %li
+    %a
+      %span
+        Snippets
+  %li
+    %a
+      %span
+        Help
+  %li
+    %a
+      %span
+        Profile Settings
diff --git a/spec/javascripts/fixtures/header.html.haml b/spec/javascripts/fixtures/header.html.haml
new file mode 100644
index 00000000000..4db2ef604de
--- /dev/null
+++ b/spec/javascripts/fixtures/header.html.haml
@@ -0,0 +1,35 @@
+%header.navbar.navbar-fixed-top.navbar-gitlab.nav_header_class
+  .container-fluid
+    .header-content
+      %button.side-nav-toggle
+      %span.sr-only
+        Toggle navigation
+        %i.fa.fa-bars
+      %button.navbar-toggle
+        %span.sr-only
+        Toggle navigation
+        %i.fa.fa-ellipsis-v
+      .navbar-collapse.collapse
+        %ui.nav.navbar-nav
+          %li.hidden-sm.hidden-xs
+          %li.visible-sm.visible-xs
+          %li
+            %a
+              %i.fa.fa-bell.fa-fw
+              %span.badge.todos-pending-count
+          %li
+            %a
+              %i.fa.fa-plus.fa-fw
+          %li.header-user.dropdown
+            %a
+              %img
+              %span.caret
+            .dropdown-menu-nav
+            .dropdown-menu-align-right
+              %ul
+                %li
+                  %a.profile-link
+                %li
+                  %a
+                %li.divider
+                %li.sign-out-link
diff --git a/spec/javascripts/fixtures/right_sidebar.html.haml b/spec/javascripts/fixtures/right_sidebar.html.haml
index 95efaff4b69..d48b77cf0ce 100644
--- a/spec/javascripts/fixtures/right_sidebar.html.haml
+++ b/spec/javascripts/fixtures/right_sidebar.html.haml
@@ -5,6 +5,10 @@
     %div.block.issuable-sidebar-header
       %a.gutter-toggle.pull-right.js-sidebar-toggle
         %i.fa.fa-angle-double-left
+      %button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", data: { todo_text: "Add Todo", mark_text: "Mark Done", issuable_id: "1", issuable_type: "issue", url: "/todos" }}
+        %span.js-issuable-todo-text
+          Add Todo
+        %i.fa.fa-spin.fa-spinner.js-issuable-todo-loading.hidden
 
     %form.issuable-context-form
       %div.block.labels
diff --git a/spec/javascripts/fixtures/todos.json b/spec/javascripts/fixtures/todos.json
new file mode 100644
index 00000000000..62c2387d515
--- /dev/null
+++ b/spec/javascripts/fixtures/todos.json
@@ -0,0 +1,4 @@
+{
+    "count": 1,
+    "delete_path": "/dashboard/todos/1"
+}
\ No newline at end of file
diff --git a/spec/javascripts/header_spec.js b/spec/javascripts/header_spec.js
new file mode 100644
index 00000000000..b9d023a9aae
--- /dev/null
+++ b/spec/javascripts/header_spec.js
@@ -0,0 +1,54 @@
+/*= require header */
+/*= require lib/utils/text_utility */
+/*= require jquery */
+
+(function() {
+
+  describe('Header', function() {
+    var todosPendingCount = '.todos-pending-count';
+    var fixtureTemplate = 'header.html';
+
+    function isTodosCountHidden() {
+      return $(todosPendingCount).hasClass('hidden');
+    }
+
+    function triggerToggle(newCount) {
+      $(document).trigger('todo:toggle', newCount);
+    }
+
+    fixture.preload(fixtureTemplate);
+    beforeEach(function() {
+      fixture.load(fixtureTemplate);
+    });
+
+    it('should update todos-pending-count after receiving the todo:toggle event', function() {
+      triggerToggle(5);
+      expect($(todosPendingCount).text()).toEqual('5');
+    });
+
+    it('should hide todos-pending-count when it is 0', function() {
+      triggerToggle(0);
+      expect(isTodosCountHidden()).toEqual(true);
+    });
+
+    it('should show todos-pending-count when it is more than 0', function() {
+      triggerToggle(10);
+      expect(isTodosCountHidden()).toEqual(false);
+    });
+
+    describe('when todos-pending-count is 1000', function() {
+      beforeEach(function() {
+        triggerToggle(1000);
+      });
+
+      it('should show todos-pending-count', function() {
+        expect(isTodosCountHidden()).toEqual(false);
+      });
+
+      it('should add delimiter to todos-pending-count', function() {
+        expect($(todosPendingCount).text()).toEqual('1,000');
+      });
+    });
+  });
+
+}).call(this);
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index c937a4706f7..9c7f44c6427 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -3,6 +3,8 @@
 /*= require jquery */
 /*= require jquery.cookie */
 
+/*= require extensions/jquery.js */
+
 (function() {
   var $aside, $icon, $labelsIcon, $page, $toggle, assertSidebarState;
 
@@ -55,12 +57,27 @@
       $labelsIcon.click();
       return assertSidebarState('expanded');
     });
-    return it('should collapse when the icon arrow clicked while it is floating on page', function() {
+    it('should collapse when the icon arrow clicked while it is floating on page', function() {
       $labelsIcon.click();
       assertSidebarState('expanded');
       $toggle.click();
       return assertSidebarState('collapsed');
     });
+
+    it('should broadcast todo:toggle event when add todo clicked', function() {
+      spyOn(jQuery, 'ajax').and.callFake(function() {
+        var d = $.Deferred();
+        var response = fixture.load('todos.json');
+        d.resolve(response);
+        return d.promise();
+      });
+
+      var todoToggleSpy = spyOnEvent(document, 'todo:toggle');
+
+      $('.js-issuable-todo').click();
+
+      expect(todoToggleSpy.calls.count()).toEqual(1);
+    })
   });
 
 }).call(this);
-- 
GitLab