Commit f8793198 authored by Fatih Acet's avatar Fatih Acet

Merge branch 'add-todo-toggle-event' into 'master'

Add todo toggle event

## What does this MR do?
Adds a custom jQuery event `todo:toggle` to detect when a new todo is added so that the respective todo counters (header navigation and sidebar) can update as needed

## Are there points in the code the reviewer needs to double check?

*  I wasn't sure whether each `spec` should be modularized based on the html templates or whether they should be separated based on function. There are some crossovers between the `dashboard_spec` and the `header_spec`.
* The naming conventions for `sidebar` and `right_sidebar` were a little confusing since they were named the opposite in their `specs`. I made a few assumptions and named a few files based on what I thought they should be named. I'd be happy to change it to something else though 😄  

## Why was this MR needed?
This resolves an existing issue where the todo count on the sidebar would not update (until refresh) and refactors the existing methods that are used to update the todo counters (header navigation and sidebar)

## What are the relevant issue numbers?
Closes #20140

## Does this MR meet the acceptance criteria?

- [x] [CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added
- Tests
  - [x] Added for this feature/bug
  - [x] All builds are passing
- [x] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides)
- [x] Branch has no merge conflicts with `master` (if you do - rebase it please)
- [x] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)

See merge request !5724
parents 337c5ded f610f69a
...@@ -102,6 +102,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -102,6 +102,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Add more tests for calendar contribution (ClemMakesApps) - Add more tests for calendar contribution (ClemMakesApps)
- Update Gitlab Shell to fix some problems with moving projects between storages - Update Gitlab Shell to fix some problems with moving projects between storages
- Cache rendered markdown in the database, rather than Redis - 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 - Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references
- Simplify Mentionable concern instance methods - Simplify Mentionable concern instance methods
- API: Ability to retrieve version information (Robert Schilling) - API: Ability to retrieve version information (Robert Schilling)
......
(function() {
$(document).on('todo:toggle', function(e, count) {
var $todoPendingCount = $('.todos-pending-count');
$todoPendingCount.text(gl.text.addDelimiter(count));
$todoPendingCount.toggleClass('hidden', count === 0);
});
})();
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
if ((base = w.gl).text == null) { if ((base = w.gl).text == null) {
base.text = {}; base.text = {};
} }
gl.text.addDelimiter = function(text) {
return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
}
gl.text.randomString = function() { gl.text.randomString = function() {
return Math.random().toString(36).substring(7); return Math.random().toString(36).substring(7);
}; };
......
...@@ -82,16 +82,11 @@ ...@@ -82,16 +82,11 @@
}; };
Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) { Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) {
var $todoPendingCount; $(document).trigger('todo:toggle', data.count);
$todoPendingCount = $('.todos-pending-count');
$todoPendingCount.text(data.count);
$btn.enable(); $btn.enable();
$todoLoading.addClass('hidden'); $todoLoading.addClass('hidden');
if (data.count === 0) {
$todoPendingCount.addClass('hidden');
} else {
$todoPendingCount.removeClass('hidden');
}
if (data.delete_path != null) { if (data.delete_path != null) {
$btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path); $btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path);
return $btnText.text($btn.data('mark-text')); return $btnText.text($btn.data('mark-text'));
......
...@@ -38,7 +38,8 @@ ...@@ -38,7 +38,8 @@
.on('click', sidebarToggleSelector, () => this.toggleSidebar()) .on('click', sidebarToggleSelector, () => this.toggleSidebar())
.on('click', pinnedToggleSelector, () => this.togglePinnedState()) .on('click', pinnedToggleSelector, () => this.togglePinnedState())
.on('click', 'html, body', (e) => this.handleClickEvent(e)) .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(); this.renderState();
} }
...@@ -53,6 +54,10 @@ ...@@ -53,6 +54,10 @@
} }
} }
updateTodoCount(count) {
$('.js-todos-count').text(gl.text.addDelimiter(count));
}
toggleSidebar() { toggleSidebar() {
this.isExpanded = !this.isExpanded; this.isExpanded = !this.isExpanded;
this.renderState(); this.renderState();
......
...@@ -98,7 +98,8 @@ ...@@ -98,7 +98,8 @@
} }
updateBadges(data) { 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); return $('.todos-done .badge').text(data.done_count);
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
= link_to dashboard_todos_path, title: 'Todos' do = link_to dashboard_todos_path, title: 'Todos' do
%span %span
Todos 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 = nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
%span %span
......
...@@ -132,7 +132,6 @@ describe 'Dashboard Todos', feature: true do ...@@ -132,7 +132,6 @@ describe 'Dashboard Todos', feature: true do
end end
it 'shows "All done" message!' do 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 'To do 0'
expect(page).to have_content "You're all done!" expect(page).to have_content "You're all done!"
expect(page).not_to have_selector('.gl-pagination') expect(page).not_to have_selector('.gl-pagination')
......
/*= 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);
%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
%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
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
%div.block.issuable-sidebar-header %div.block.issuable-sidebar-header
%a.gutter-toggle.pull-right.js-sidebar-toggle %a.gutter-toggle.pull-right.js-sidebar-toggle
%i.fa.fa-angle-double-left %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 %form.issuable-context-form
%div.block.labels %div.block.labels
......
{
"count": 1,
"delete_path": "/dashboard/todos/1"
}
\ No newline at end of file
/*= 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);
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
/*= require jquery */ /*= require jquery */
/*= require js.cookie */ /*= require js.cookie */
/*= require extensions/jquery.js */
(function() { (function() {
var $aside, $icon, $labelsIcon, $page, $toggle, assertSidebarState; var $aside, $icon, $labelsIcon, $page, $toggle, assertSidebarState;
...@@ -56,12 +58,27 @@ ...@@ -56,12 +58,27 @@
$labelsIcon.click(); $labelsIcon.click();
return assertSidebarState('expanded'); 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(); $labelsIcon.click();
assertSidebarState('expanded'); assertSidebarState('expanded');
$toggle.click(); $toggle.click();
return assertSidebarState('collapsed'); 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); }).call(this);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment