Merge branch '27114-add-undo-to-todos-in-the-done-tab' into 'master'
Add 'Undo' to Todos in the Done tab Closes #27114 See merge request !8782
This commit is contained in:
commit
cc64eda987
|
@ -1,7 +1,6 @@
|
||||||
/* eslint-disable class-methods-use-this, no-new, func-names, no-unneeded-ternary, object-shorthand, quote-props, no-param-reassign, max-len */
|
/* eslint-disable class-methods-use-this, no-unneeded-ternary, quote-props */
|
||||||
/* global UsersSelect */
|
/* global UsersSelect */
|
||||||
|
|
||||||
((global) => {
|
|
||||||
class Todos {
|
class Todos {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.initFilters();
|
this.initFilters();
|
||||||
|
@ -17,30 +16,30 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
unbindEvents() {
|
unbindEvents() {
|
||||||
$('.js-done-todo, .js-undo-todo').off('click', this.updateStateClickedWrapper);
|
$('.js-done-todo, .js-undo-todo, .js-add-todo').off('click', this.updateRowStateClickedWrapper);
|
||||||
$('.js-todos-mark-all').off('click', this.allDoneClickedWrapper);
|
$('.js-todos-mark-all').off('click', this.allDoneClickedWrapper);
|
||||||
$('.todo').off('click', this.goToTodoUrl);
|
$('.todo').off('click', this.goToTodoUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
this.updateStateClickedWrapper = this.updateStateClicked.bind(this);
|
this.updateRowStateClickedWrapper = this.updateRowStateClicked.bind(this);
|
||||||
this.allDoneClickedWrapper = this.allDoneClicked.bind(this);
|
this.allDoneClickedWrapper = this.allDoneClicked.bind(this);
|
||||||
|
|
||||||
$('.js-done-todo, .js-undo-todo').on('click', this.updateStateClickedWrapper);
|
$('.js-done-todo, .js-undo-todo, .js-add-todo').on('click', this.updateRowStateClickedWrapper);
|
||||||
$('.js-todos-mark-all').on('click', this.allDoneClickedWrapper);
|
$('.js-todos-mark-all').on('click', this.allDoneClickedWrapper);
|
||||||
$('.todo').on('click', this.goToTodoUrl);
|
$('.todo').on('click', this.goToTodoUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
initFilters() {
|
initFilters() {
|
||||||
new UsersSelect();
|
|
||||||
this.initFilterDropdown($('.js-project-search'), 'project_id', ['text']);
|
this.initFilterDropdown($('.js-project-search'), 'project_id', ['text']);
|
||||||
this.initFilterDropdown($('.js-type-search'), 'type');
|
this.initFilterDropdown($('.js-type-search'), 'type');
|
||||||
this.initFilterDropdown($('.js-action-search'), 'action_id');
|
this.initFilterDropdown($('.js-action-search'), 'action_id');
|
||||||
|
|
||||||
$('form.filter-form').on('submit', function (event) {
|
$('form.filter-form').on('submit', function applyFilters(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
gl.utils.visitUrl(`${this.action}&${$(this).serialize()}`);
|
gl.utils.visitUrl(`${this.action}&${$(this).serialize()}`);
|
||||||
});
|
});
|
||||||
|
return new UsersSelect();
|
||||||
}
|
}
|
||||||
|
|
||||||
initFilterDropdown($dropdown, fieldName, searchFields) {
|
initFilterDropdown($dropdown, fieldName, searchFields) {
|
||||||
|
@ -50,14 +49,13 @@
|
||||||
filterable: searchFields ? true : false,
|
filterable: searchFields ? true : false,
|
||||||
search: { fields: searchFields },
|
search: { fields: searchFields },
|
||||||
data: $dropdown.data('data'),
|
data: $dropdown.data('data'),
|
||||||
clicked: function () {
|
clicked: () => $dropdown.closest('form.filter-form').submit(),
|
||||||
return $dropdown.closest('form.filter-form').submit();
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStateClicked(e) {
|
updateRowStateClicked(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const target = e.target;
|
const target = e.target;
|
||||||
target.setAttribute('disabled', '');
|
target.setAttribute('disabled', '');
|
||||||
target.classList.add('disabled');
|
target.classList.add('disabled');
|
||||||
|
@ -69,8 +67,8 @@
|
||||||
'_method': target.getAttribute('data-method'),
|
'_method': target.getAttribute('data-method'),
|
||||||
},
|
},
|
||||||
success: (data) => {
|
success: (data) => {
|
||||||
this.updateState(target);
|
this.updateRowState(target);
|
||||||
this.updateBadges(data);
|
return this.updateBadges(data);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -94,28 +92,30 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateState(target) {
|
updateRowState(target) {
|
||||||
const row = target.closest('li');
|
const row = target.closest('li');
|
||||||
const restoreBtn = row.querySelector('.js-undo-todo');
|
const restoreBtn = row.querySelector('.js-undo-todo');
|
||||||
const doneBtn = row.querySelector('.js-done-todo');
|
const doneBtn = row.querySelector('.js-done-todo');
|
||||||
|
|
||||||
|
target.classList.add('hidden');
|
||||||
target.removeAttribute('disabled');
|
target.removeAttribute('disabled');
|
||||||
target.classList.remove('disabled');
|
target.classList.remove('disabled');
|
||||||
target.classList.add('hidden');
|
|
||||||
|
|
||||||
if (target === doneBtn) {
|
if (target === doneBtn) {
|
||||||
row.classList.add('done-reversible');
|
row.classList.add('done-reversible');
|
||||||
restoreBtn.classList.remove('hidden');
|
restoreBtn.classList.remove('hidden');
|
||||||
} else {
|
} else if (target === restoreBtn) {
|
||||||
row.classList.remove('done-reversible');
|
row.classList.remove('done-reversible');
|
||||||
doneBtn.classList.remove('hidden');
|
doneBtn.classList.remove('hidden');
|
||||||
|
} else {
|
||||||
|
row.parentNode.removeChild(row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateBadges(data) {
|
updateBadges(data) {
|
||||||
$(document).trigger('todo:toggle', data.count);
|
$(document).trigger('todo:toggle', data.count);
|
||||||
$('.todos-pending .badge').text(data.count);
|
document.querySelector('.todos-pending .badge').innerHTML = data.count;
|
||||||
$('.todos-done .badge').text(data.done_count);
|
document.querySelector('.todos-done .badge').innerHTML = data.done_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
goToTodoUrl(e) {
|
goToTodoUrl(e) {
|
||||||
|
@ -142,5 +142,5 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Todos = Todos;
|
window.gl = window.gl || {};
|
||||||
})(window.gl || (window.gl = {}));
|
gl.Todos = Todos;
|
||||||
|
|
|
@ -42,3 +42,8 @@
|
||||||
= link_to restore_dashboard_todo_path(todo), method: :patch, class: 'btn btn-loading js-undo-todo hidden' do
|
= link_to restore_dashboard_todo_path(todo), method: :patch, class: 'btn btn-loading js-undo-todo hidden' do
|
||||||
Undo
|
Undo
|
||||||
= icon('spinner spin')
|
= icon('spinner spin')
|
||||||
|
- else
|
||||||
|
.todo-actions
|
||||||
|
= link_to restore_dashboard_todo_path(todo), method: :patch, class: 'btn btn-loading js-add-todo' do
|
||||||
|
Add todo
|
||||||
|
= icon('spinner spin')
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
title: Add Undo to Todos in the Done tab
|
||||||
|
merge_request: 8782
|
||||||
|
author: Jacopo Beschi @jacopo-beschi
|
|
@ -38,7 +38,9 @@ describe 'Dashboard Todos', feature: true do
|
||||||
|
|
||||||
shared_examples 'deleting the todo' do
|
shared_examples 'deleting the todo' do
|
||||||
before do
|
before do
|
||||||
first('.js-done-todo').click
|
within first('.todo') do
|
||||||
|
click_link 'Done'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'is marked as done-reversible in the list' do
|
it 'is marked as done-reversible in the list' do
|
||||||
|
@ -62,9 +64,11 @@ describe 'Dashboard Todos', feature: true do
|
||||||
|
|
||||||
shared_examples 'deleting and restoring the todo' do
|
shared_examples 'deleting and restoring the todo' do
|
||||||
before do
|
before do
|
||||||
first('.js-done-todo').click
|
within first('.todo') do
|
||||||
|
click_link 'Done'
|
||||||
wait_for_ajax
|
wait_for_ajax
|
||||||
first('.js-undo-todo').click
|
click_link 'Undo'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'is marked back as pending in the list' do
|
it 'is marked back as pending in the list' do
|
||||||
|
@ -97,6 +101,35 @@ describe 'Dashboard Todos', feature: true do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'User has done todos', js: true do
|
||||||
|
before do
|
||||||
|
create(:todo, :mentioned, :done, user: user, project: project, target: issue, author: author)
|
||||||
|
login_as(user)
|
||||||
|
visit dashboard_todos_path(state: :done)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has the done todo present' do
|
||||||
|
expect(page).to have_selector('.todos-list .todo.todo-done', count: 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'restoring the todo' do
|
||||||
|
before do
|
||||||
|
within first('.todo') do
|
||||||
|
click_link 'Add todo'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is removed from the list' do
|
||||||
|
expect(page).not_to have_selector('.todos-list .todo.todo-done')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates todo count' do
|
||||||
|
expect(page).to have_content 'To do 1'
|
||||||
|
expect(page).to have_content 'Done 0'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'User has Todos with labels spanning multiple projects' do
|
context 'User has Todos with labels spanning multiple projects' do
|
||||||
before do
|
before do
|
||||||
label1 = create(:label, project: project)
|
label1 = create(:label, project: project)
|
||||||
|
@ -143,7 +176,7 @@ describe 'Dashboard Todos', feature: true do
|
||||||
describe 'mark all as done', js: true do
|
describe 'mark all as done', js: true do
|
||||||
before do
|
before do
|
||||||
visit dashboard_todos_path
|
visit dashboard_todos_path
|
||||||
click_link('Mark all as done')
|
click_link 'Mark all as done'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'shows "All done" message!' do
|
it 'shows "All done" message!' do
|
||||||
|
|
Loading…
Reference in New Issue