Commit 412c9e8b authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'create-collapsed-todo-button' into 'master'

Added initial todo functionality to collapsed sidebar

Closes #24805

See merge request !7827
parents 03c24c33 89d1a1c0
...@@ -56,14 +56,15 @@ import Cookies from 'js-cookie'; ...@@ -56,14 +56,15 @@ import Cookies from 'js-cookie';
Sidebar.prototype.toggleTodo = function(e) { Sidebar.prototype.toggleTodo = function(e) {
var $btnText, $this, $todoLoading, ajaxType, url; var $btnText, $this, $todoLoading, ajaxType, url;
$this = $(e.currentTarget); $this = $(e.currentTarget);
$todoLoading = $('.js-issuable-todo-loading');
$btnText = $('.js-issuable-todo-text', $this);
ajaxType = $this.attr('data-delete-path') ? 'DELETE' : 'POST'; ajaxType = $this.attr('data-delete-path') ? 'DELETE' : 'POST';
if ($this.attr('data-delete-path')) { if ($this.attr('data-delete-path')) {
url = "" + ($this.attr('data-delete-path')); url = "" + ($this.attr('data-delete-path'));
} else { } else {
url = "" + ($this.data('url')); url = "" + ($this.data('url'));
} }
$this.tooltip('hide');
return $.ajax({ return $.ajax({
url: url, url: url,
type: ajaxType, type: ajaxType,
...@@ -74,34 +75,44 @@ import Cookies from 'js-cookie'; ...@@ -74,34 +75,44 @@ import Cookies from 'js-cookie';
}, },
beforeSend: (function(_this) { beforeSend: (function(_this) {
return function() { return function() {
return _this.beforeTodoSend($this, $todoLoading); $('.js-issuable-todo').disable()
.addClass('is-loading');
}; };
})(this) })(this)
}).done((function(_this) { }).done((function(_this) {
return function(data) { return function(data) {
return _this.todoUpdateDone(data, $this, $btnText, $todoLoading); return _this.todoUpdateDone(data);
}; };
})(this)); })(this));
}; };
Sidebar.prototype.beforeTodoSend = function($btn, $todoLoading) { Sidebar.prototype.todoUpdateDone = function(data) {
$btn.disable(); const deletePath = data.delete_path ? data.delete_path : null;
return $todoLoading.removeClass('hidden'); const attrPrefix = deletePath ? 'mark' : 'todo';
}; const $todoBtns = $('.js-issuable-todo');
Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) {
$(document).trigger('todo:toggle', data.count); $(document).trigger('todo:toggle', data.count);
$btn.enable(); $todoBtns.each((i, el) => {
$todoLoading.addClass('hidden'); const $el = $(el);
const $elText = $el.find('.js-issuable-todo-inner');
$el.removeClass('is-loading')
.enable()
.attr('aria-label', $el.data(`${attrPrefix}-text`))
.attr('data-delete-path', deletePath)
.attr('title', $el.data(`${attrPrefix}-text`));
if (data.delete_path != null) { if ($el.hasClass('has-tooltip')) {
$btn.attr('aria-label', $btn.data('mark-text')).attr('data-delete-path', data.delete_path); $el.tooltip('fixTitle');
return $btnText.text($btn.data('mark-text')); }
if ($el.data(`${attrPrefix}-icon`)) {
$elText.html($el.data(`${attrPrefix}-icon`));
} else { } else {
$btn.attr('aria-label', $btn.data('todo-text')).removeAttr('data-delete-path'); $elText.text($el.data(`${attrPrefix}-text`));
return $btnText.text($btn.data('todo-text'));
} }
});
}; };
Sidebar.prototype.sidebarDropdownLoading = function(e) { Sidebar.prototype.sidebarDropdownLoading = function(e) {
......
...@@ -362,3 +362,13 @@ ...@@ -362,3 +362,13 @@
width: 100%; width: 100%;
} }
} }
.btn-blank {
padding: 0;
background: transparent;
border: 0;
&:focus {
outline: 0;
}
}
...@@ -243,6 +243,10 @@ ...@@ -243,6 +243,10 @@
font-size: 13px; font-size: 13px;
font-weight: normal; font-weight: normal;
} }
.hide-expanded {
display: none;
}
} }
&.right-sidebar-collapsed { &.right-sidebar-collapsed {
...@@ -282,10 +286,11 @@ ...@@ -282,10 +286,11 @@
display: block; display: block;
width: 100%; width: 100%;
text-align: center; text-align: center;
padding-bottom: 10px; margin-bottom: 10px;
color: $issuable-sidebar-color; color: $issuable-sidebar-color;
&:hover { &:hover,
&:hover .todo-undone {
color: $gl-text-color; color: $gl-text-color;
} }
...@@ -294,6 +299,10 @@ ...@@ -294,6 +299,10 @@
margin-top: 0; margin-top: 0;
} }
.todo-undone {
color: $gl-link-color;
}
.author { .author {
display: none; display: none;
} }
...@@ -582,3 +591,21 @@ ...@@ -582,3 +591,21 @@
opacity: 0; opacity: 0;
} }
} }
.issuable-todo-btn {
.fa-spinner {
display: none;
}
&.is-loading {
.fa-spinner {
display: inline-block;
}
&.sidebar-collapsed-icon {
.issuable-todo-inner {
display: none;
}
}
}
}
...@@ -7,17 +7,17 @@ ...@@ -7,17 +7,17 @@
li { li {
.badge.todos-pending-count { .badge.todos-pending-count {
position: inherit; position: inherit;
top: -6px; top: -10px;
margin-top: -5px; margin-top: -5px;
font-weight: normal; font-weight: normal;
background: $todo-alert-blue; background: $todo-alert-blue;
margin-left: -17px; margin-left: -13px;
font-size: 11px; font-size: 11px;
color: $white-light; color: $white-light;
padding: 3px; padding: 4px;
padding-top: 1px; padding-top: 1px;
padding-bottom: 1px; padding-bottom: 2px;
border-radius: 3px; border-radius: 7px;
} }
} }
} }
......
...@@ -253,4 +253,19 @@ module IssuablesHelper ...@@ -253,4 +253,19 @@ module IssuablesHelper
def selected_template(issuable) def selected_template(issuable)
params[:issuable_template] if issuable_templates(issuable).include?(params[:issuable_template]) params[:issuable_template] if issuable_templates(issuable).include?(params[:issuable_template])
end end
def issuable_todo_button_data(issuable, todo, is_collapsed)
{
todo_text: "Add todo",
mark_text: "Mark done",
todo_icon: (is_collapsed ? icon('plus-square') : nil),
mark_icon: (is_collapsed ? icon('check-square', class: 'todo-undone') : nil),
issuable_id: issuable.id,
issuable_type: issuable.class.name.underscore,
url: namespace_project_todos_path(@project.namespace, @project),
delete_path: (dashboard_todo_path(todo) if todo),
placement: (is_collapsed ? 'left' : nil),
container: (is_collapsed ? 'body' : nil)
}
end
end end
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
= icon('wrench fw') = icon('wrench fw')
%li %li
= link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('bell fw') = icon('check-square fw')
%span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) } %span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
= todos_count_format(todos_pending_count) = todos_count_format(todos_pending_count)
- if current_user.can_create_project? - if current_user.can_create_project?
......
...@@ -13,15 +13,12 @@ ...@@ -13,15 +13,12 @@
%a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" } %a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" }
= sidebar_gutter_toggle_icon = sidebar_gutter_toggle_icon
- if current_user - if current_user
%button.btn.btn-default.issuable-header-btn.pull-right.js-issuable-todo{ type: "button", "aria-label" => (todo.nil? ? "Add todo" : "Mark done"), data: { todo_text: "Add todo", mark_text: "Mark done", issuable_id: issuable.id, issuable_type: issuable.class.name.underscore, url: namespace_project_todos_path(@project.namespace, @project), delete_path: (dashboard_todo_path(todo) if todo) } } = render "shared/issuable/sidebar_todo", todo: todo, issuable: issuable
%span.js-issuable-todo-text
- if todo
Mark done
- else
Add todo
= icon('spin spinner', class: 'hidden js-issuable-todo-loading', 'aria-hidden': 'true')
= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f| = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
- if current_user
.block.todo.hide-expanded
= render "shared/issuable/sidebar_todo", todo: todo, issuable: issuable, is_collapsed: true
.block.assignee .block.assignee
.sidebar-collapsed-icon.sidebar-collapsed-user{ data: { toggle: "tooltip", placement: "left", container: "body" }, title: (issuable.assignee.name if issuable.assignee) } .sidebar-collapsed-icon.sidebar-collapsed-user{ data: { toggle: "tooltip", placement: "left", container: "body" }, title: (issuable.assignee.name if issuable.assignee) }
- if issuable.assignee - if issuable.assignee
......
- is_collapsed = local_assigns.fetch(:is_collapsed, false)
- mark_content = is_collapsed ? icon('check-square', class: 'todo-undone') : 'Mark done'
- todo_content = is_collapsed ? icon('plus-square') : 'Add todo'
%button.issuable-todo-btn.js-issuable-todo{ type: 'button',
class: (is_collapsed ? 'btn-blank sidebar-collapsed-icon dont-change-state has-tooltip' : 'btn btn-default issuable-header-btn pull-right'),
title: (todo.nil? ? 'Add todo' : 'Mark done'),
'aria-label' => (todo.nil? ? 'Add todo' : 'Mark done'),
data: issuable_todo_button_data(issuable, todo, is_collapsed) }
%span.issuable-todo-inner.js-issuable-todo-inner<
- if todo
= mark_content
- else
= todo_content
= icon('spin spinner', 'aria-hidden': 'true')
---
title: adds todo functionality to closed issuable sidebar and changes todo bell icon
to check-square
merge_request:
author:
/* global Sidebar */
/* eslint-disable no-new */
import _ from 'underscore';
import '~/right_sidebar';
describe('Issuable right sidebar collapsed todo toggle', () => {
const fixtureName = 'issues/open-issue.html.raw';
const jsonFixtureName = 'todos/todos.json';
preloadFixtures(fixtureName);
preloadFixtures(jsonFixtureName);
beforeEach(() => {
const todoData = getJSONFixture(jsonFixtureName);
new Sidebar();
loadFixtures(fixtureName);
document.querySelector('.js-right-sidebar')
.classList.toggle('right-sidebar-expanded');
document.querySelector('.js-right-sidebar')
.classList.toggle('right-sidebar-collapsed');
spyOn(jQuery, 'ajax').and.callFake((res) => {
const d = $.Deferred();
const response = _.clone(todoData);
if (res.type === 'DELETE') {
delete response.delete_path;
}
d.resolve(response);
return d.promise();
});
});
it('shows add todo button', () => {
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon'),
).not.toBeNull();
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .fa-plus-square'),
).not.toBeNull();
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
).toBeNull();
});
it('sets default tooltip title', () => {
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('title'),
).toBe('Add todo');
});
it('toggle todo state', () => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
).not.toBeNull();
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .fa-check-square'),
).not.toBeNull();
});
it('toggle todo state of expanded todo toggle', () => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
expect(
document.querySelector('.issuable-sidebar-header .js-issuable-todo').textContent.trim(),
).toBe('Mark done');
});
it('toggles todo button tooltip', () => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('data-original-title'),
).toBe('Mark done');
});
it('marks todo as done', () => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
).not.toBeNull();
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
).toBeNull();
expect(
document.querySelector('.issuable-sidebar-header .js-issuable-todo').textContent.trim(),
).toBe('Add todo');
});
it('updates aria-label to mark done', () => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'),
).toBe('Mark done');
});
it('updates aria-label to add todo', () => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'),
).toBe('Mark done');
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'),
).toBe('Add todo');
});
});
...@@ -74,7 +74,7 @@ import '~/right_sidebar'; ...@@ -74,7 +74,7 @@ import '~/right_sidebar';
var todoToggleSpy = spyOnEvent(document, 'todo:toggle'); var todoToggleSpy = spyOnEvent(document, 'todo:toggle');
$('.js-issuable-todo').click(); $('.issuable-sidebar-header .js-issuable-todo').click();
expect(todoToggleSpy.calls.count()).toEqual(1); expect(todoToggleSpy.calls.count()).toEqual(1);
}); });
......
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