summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/right_sidebar.js47
-rw-r--r--app/assets/stylesheets/framework/buttons.scss10
-rw-r--r--app/assets/stylesheets/pages/issuable.scss31
-rw-r--r--app/assets/stylesheets/pages/todos.scss10
-rw-r--r--app/helpers/issuables_helper.rb15
-rw-r--r--app/views/layouts/header/_default.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml11
-rw-r--r--app/views/shared/issuable/_sidebar_todo.html.haml15
-rw-r--r--changelogs/unreleased/create-collapsed-todo-button.yml5
-rw-r--r--spec/javascripts/collapsed_sidebar_todo_spec.js123
-rw-r--r--spec/javascripts/right_sidebar_spec.js2
11 files changed, 237 insertions, 34 deletions
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 64a68d56962..f3cd41fb4db 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -56,14 +56,15 @@ import Cookies from 'js-cookie';
Sidebar.prototype.toggleTodo = function(e) {
var $btnText, $this, $todoLoading, ajaxType, url;
$this = $(e.currentTarget);
- $todoLoading = $('.js-issuable-todo-loading');
- $btnText = $('.js-issuable-todo-text', $this);
ajaxType = $this.attr('data-delete-path') ? 'DELETE' : 'POST';
if ($this.attr('data-delete-path')) {
url = "" + ($this.attr('data-delete-path'));
} else {
url = "" + ($this.data('url'));
}
+
+ $this.tooltip('hide');
+
return $.ajax({
url: url,
type: ajaxType,
@@ -74,34 +75,44 @@ import Cookies from 'js-cookie';
},
beforeSend: (function(_this) {
return function() {
- return _this.beforeTodoSend($this, $todoLoading);
+ $('.js-issuable-todo').disable()
+ .addClass('is-loading');
};
})(this)
}).done((function(_this) {
return function(data) {
- return _this.todoUpdateDone(data, $this, $btnText, $todoLoading);
+ return _this.todoUpdateDone(data);
};
})(this));
};
- Sidebar.prototype.beforeTodoSend = function($btn, $todoLoading) {
- $btn.disable();
- return $todoLoading.removeClass('hidden');
- };
+ Sidebar.prototype.todoUpdateDone = function(data) {
+ const deletePath = data.delete_path ? data.delete_path : null;
+ const attrPrefix = deletePath ? 'mark' : 'todo';
+ const $todoBtns = $('.js-issuable-todo');
- Sidebar.prototype.todoUpdateDone = function(data, $btn, $btnText, $todoLoading) {
$(document).trigger('todo:toggle', data.count);
- $btn.enable();
- $todoLoading.addClass('hidden');
+ $todoBtns.each((i, el) => {
+ const $el = $(el);
+ const $elText = $el.find('.js-issuable-todo-inner');
- 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'));
- } else {
- $btn.attr('aria-label', $btn.data('todo-text')).removeAttr('data-delete-path');
- return $btnText.text($btn.data('todo-text'));
- }
+ $el.removeClass('is-loading')
+ .enable()
+ .attr('aria-label', $el.data(`${attrPrefix}-text`))
+ .attr('data-delete-path', deletePath)
+ .attr('title', $el.data(`${attrPrefix}-text`));
+
+ if ($el.hasClass('has-tooltip')) {
+ $el.tooltip('fixTitle');
+ }
+
+ if ($el.data(`${attrPrefix}-icon`)) {
+ $elText.html($el.data(`${attrPrefix}-icon`));
+ } else {
+ $elText.text($el.data(`${attrPrefix}-text`));
+ }
+ });
};
Sidebar.prototype.sidebarDropdownLoading = function(e) {
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 50849e95541..4369ae78bde 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -362,3 +362,13 @@
width: 100%;
}
}
+
+.btn-blank {
+ padding: 0;
+ background: transparent;
+ border: 0;
+
+ &:focus {
+ outline: 0;
+ }
+}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index c1a9bc4be28..e84a05e3e9e 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -243,6 +243,10 @@
font-size: 13px;
font-weight: normal;
}
+
+ .hide-expanded {
+ display: none;
+ }
}
&.right-sidebar-collapsed {
@@ -282,10 +286,11 @@
display: block;
width: 100%;
text-align: center;
- padding-bottom: 10px;
+ margin-bottom: 10px;
color: $issuable-sidebar-color;
- &:hover {
+ &:hover,
+ &:hover .todo-undone {
color: $gl-text-color;
}
@@ -294,6 +299,10 @@
margin-top: 0;
}
+ .todo-undone {
+ color: $gl-link-color;
+ }
+
.author {
display: none;
}
@@ -582,3 +591,21 @@
opacity: 0;
}
}
+
+.issuable-todo-btn {
+ .fa-spinner {
+ display: none;
+ }
+
+ &.is-loading {
+ .fa-spinner {
+ display: inline-block;
+ }
+
+ &.sidebar-collapsed-icon {
+ .issuable-todo-inner {
+ display: none;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index b071d7f18cd..9c325227fa1 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -7,17 +7,17 @@
li {
.badge.todos-pending-count {
position: inherit;
- top: -6px;
+ top: -10px;
margin-top: -5px;
font-weight: normal;
background: $todo-alert-blue;
- margin-left: -17px;
+ margin-left: -13px;
font-size: 11px;
color: $white-light;
- padding: 3px;
+ padding: 4px;
padding-top: 1px;
- padding-bottom: 1px;
- border-radius: 3px;
+ padding-bottom: 2px;
+ border-radius: 7px;
}
}
}
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index a777db2826b..749b6245edc 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -253,4 +253,19 @@ module IssuablesHelper
def selected_template(issuable)
params[:issuable_template] if issuable_templates(issuable).include?(params[:issuable_template])
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
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 7bf4bc70f7c..afa6eec2fc7 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -40,7 +40,7 @@
= icon('wrench fw')
%li
= 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) }
= todos_count_format(todos_pending_count)
- if current_user.can_create_project?
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index f19f362f514..92d2d93a732 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -13,15 +13,12 @@
%a.gutter-toggle.pull-right.js-sidebar-toggle{ role: "button", href: "#", "aria-label" => "Toggle sidebar" }
= sidebar_gutter_toggle_icon
- 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) } }
- %span.js-issuable-todo-text
- - if todo
- Mark done
- - else
- Add todo
- = icon('spin spinner', class: 'hidden js-issuable-todo-loading', 'aria-hidden': 'true')
+ = render "shared/issuable/sidebar_todo", todo: todo, issuable: issuable
= 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
.sidebar-collapsed-icon.sidebar-collapsed-user{ data: { toggle: "tooltip", placement: "left", container: "body" }, title: (issuable.assignee.name if issuable.assignee) }
- if issuable.assignee
diff --git a/app/views/shared/issuable/_sidebar_todo.html.haml b/app/views/shared/issuable/_sidebar_todo.html.haml
new file mode 100644
index 00000000000..574e2958ae8
--- /dev/null
+++ b/app/views/shared/issuable/_sidebar_todo.html.haml
@@ -0,0 +1,15 @@
+- 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')
diff --git a/changelogs/unreleased/create-collapsed-todo-button.yml b/changelogs/unreleased/create-collapsed-todo-button.yml
new file mode 100644
index 00000000000..6da6c070bf7
--- /dev/null
+++ b/changelogs/unreleased/create-collapsed-todo-button.yml
@@ -0,0 +1,5 @@
+---
+title: adds todo functionality to closed issuable sidebar and changes todo bell icon
+ to check-square
+merge_request:
+author:
diff --git a/spec/javascripts/collapsed_sidebar_todo_spec.js b/spec/javascripts/collapsed_sidebar_todo_spec.js
new file mode 100644
index 00000000000..974815fe939
--- /dev/null
+++ b/spec/javascripts/collapsed_sidebar_todo_spec.js
@@ -0,0 +1,123 @@
+/* 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');
+ });
+});
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index 464b54c62de..f2072a6f350 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -74,7 +74,7 @@ import '~/right_sidebar';
var todoToggleSpy = spyOnEvent(document, 'todo:toggle');
- $('.js-issuable-todo').click();
+ $('.issuable-sidebar-header .js-issuable-todo').click();
expect(todoToggleSpy.calls.count()).toEqual(1);
});