Commit 6cfa6b8e authored by Florie Guibert's avatar Florie Guibert

Migrate Add To Do button to widget

Use Add To Do widget on Issue and MR sidebars
Remove To Do label for consistency with boards sidebar

Changelog: changed
parent bd7f09e2
...@@ -11,6 +11,7 @@ import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assig ...@@ -11,6 +11,7 @@ import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assig
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue'; import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue'; import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue';
import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue'; import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default { export default {
...@@ -24,6 +25,7 @@ export default { ...@@ -24,6 +25,7 @@ export default {
BoardSidebarLabelsSelect, BoardSidebarLabelsSelect,
SidebarSubscriptionsWidget, SidebarSubscriptionsWidget,
SidebarDropdownWidget, SidebarDropdownWidget,
SidebarTodoWidget,
MountingPortal, MountingPortal,
SidebarWeightWidget: () => SidebarWeightWidget: () =>
import('ee_component/sidebar/components/weight/sidebar_weight_widget.vue'), import('ee_component/sidebar/components/weight/sidebar_weight_widget.vue'),
...@@ -90,6 +92,15 @@ export default { ...@@ -90,6 +92,15 @@ export default {
<template #title> <template #title>
<h2 class="gl-my-0 gl-font-size-h2 gl-line-height-24">{{ __('Issue details') }}</h2> <h2 class="gl-my-0 gl-font-size-h2 gl-line-height-24">{{ __('Issue details') }}</h2>
</template> </template>
<template #header>
<sidebar-todo-widget
class="gl-mt-3"
:issuable-id="activeBoardItem.fullId"
:issuable-iid="activeBoardItem.iid"
:full-path="fullPath"
:issuable-type="issuableType"
/>
</template>
<template #default> <template #default>
<board-sidebar-title /> <board-sidebar-title />
<sidebar-assignees-widget <sidebar-assignees-widget
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import $ from 'jquery'; import $ from 'jquery';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { fixTitle, hide } from '~/tooltips'; import { hide } from '~/tooltips';
import createFlash from './flash'; import createFlash from './flash';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import { sprintf, s__, __ } from './locale'; import { sprintf, s__, __ } from './locale';
...@@ -107,36 +107,6 @@ Sidebar.prototype.toggleTodo = function (e) { ...@@ -107,36 +107,6 @@ Sidebar.prototype.toggleTodo = function (e) {
); );
}; };
Sidebar.prototype.todoUpdateDone = function (data) {
const deletePath = data.delete_path ? data.delete_path : null;
const attrPrefix = deletePath ? 'mark' : 'todo';
const $todoBtns = $('.js-issuable-todo');
$(document).trigger('todo:toggle', data.count);
$todoBtns.each((i, el) => {
const $el = $(el);
const $elText = $el.find('.js-issuable-todo-inner');
$el
.removeClass('is-loading')
.enable()
.attr('aria-label', $el.data(`${attrPrefix}Text`))
.attr('title', $el.data(`${attrPrefix}Text`))
.data('deletePath', deletePath);
if ($el.hasClass('has-tooltip')) {
fixTitle(el);
}
if (typeof $el.data('isCollapsed') !== 'undefined') {
$elText.html($el.data(`${attrPrefix}Icon`));
} else {
$elText.text($el.data(`${attrPrefix}Text`));
}
});
};
Sidebar.prototype.sidebarCollapseClicked = function (e) { Sidebar.prototype.sidebarCollapseClicked = function (e) {
if ($(e.currentTarget).hasClass('dont-change-state')) { if ($(e.currentTarget).hasClass('dont-change-state')) {
return; return;
......
<script> <script>
import { GlTooltipDirective } from '@gitlab/ui'; import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { produce } from 'immer'; import { produce } from 'immer';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
...@@ -8,11 +8,17 @@ import TodoButton from '~/vue_shared/components/sidebar/todo_button.vue'; ...@@ -8,11 +8,17 @@ import TodoButton from '~/vue_shared/components/sidebar/todo_button.vue';
export default { export default {
components: { components: {
GlIcon,
TodoButton, TodoButton,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
}, },
inject: {
isClassicSidebar: {
default: false,
},
},
props: { props: {
issuableId: { issuableId: {
type: String, type: String,
...@@ -86,6 +92,12 @@ export default { ...@@ -86,6 +92,12 @@ export default {
} }
return TodoMutationTypes.Create; return TodoMutationTypes.Create;
}, },
collapsedButtonIcon() {
return this.hasTodo ? 'todo-done' : 'todo-add';
},
tootltipTitle() {
return this.hasTodo ? __('Mark as done') : __('Add a to do');
},
}, },
methods: { methods: {
toggleTodo() { toggleTodo() {
...@@ -158,7 +170,19 @@ export default { ...@@ -158,7 +170,19 @@ export default {
:is-todo="hasTodo" :is-todo="hasTodo"
:loading="isLoading" :loading="isLoading"
size="small" size="small"
class="hide-collapsed"
@click.stop.prevent="toggleTodo" @click.stop.prevent="toggleTodo"
/> />
<div v-if="isClassicSidebar" class="sidebar-collapsed-icon sidebar-collapsed-container">
<gl-icon
v-gl-tooltip
:title="tootltipTitle"
:size="16"
:class="{ 'todo-undone': hasTodo }"
:name="collapsedButtonIcon"
:aria-label="collapsedButtonIcon"
@click.stop.prevent="toggleTodo"
/>
</div>
</div> </div>
</template> </template>
...@@ -13,10 +13,12 @@ import issueDueDateQuery from '~/sidebar/queries/issue_due_date.query.graphql'; ...@@ -13,10 +13,12 @@ import issueDueDateQuery from '~/sidebar/queries/issue_due_date.query.graphql';
import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql'; import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql';
import issueSubscribedQuery from '~/sidebar/queries/issue_subscribed.query.graphql'; import issueSubscribedQuery from '~/sidebar/queries/issue_subscribed.query.graphql';
import issueTimeTrackingQuery from '~/sidebar/queries/issue_time_tracking.query.graphql'; import issueTimeTrackingQuery from '~/sidebar/queries/issue_time_tracking.query.graphql';
import issueTodoQuery from '~/sidebar/queries/issue_todo.query.graphql';
import mergeRequestMilestone from '~/sidebar/queries/merge_request_milestone.query.graphql'; import mergeRequestMilestone from '~/sidebar/queries/merge_request_milestone.query.graphql';
import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql'; import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql';
import mergeRequestSubscribed from '~/sidebar/queries/merge_request_subscribed.query.graphql'; import mergeRequestSubscribed from '~/sidebar/queries/merge_request_subscribed.query.graphql';
import mergeRequestTimeTrackingQuery from '~/sidebar/queries/merge_request_time_tracking.query.graphql'; import mergeRequestTimeTrackingQuery from '~/sidebar/queries/merge_request_time_tracking.query.graphql';
import mergeRequestTodoQuery from '~/sidebar/queries/merge_request_todo.query.graphql';
import todoCreateMutation from '~/sidebar/queries/todo_create.mutation.graphql'; import todoCreateMutation from '~/sidebar/queries/todo_create.mutation.graphql';
import todoMarkDoneMutation from '~/sidebar/queries/todo_mark_done.mutation.graphql'; import todoMarkDoneMutation from '~/sidebar/queries/todo_mark_done.mutation.graphql';
import updateEpicConfidentialMutation from '~/sidebar/queries/update_epic_confidential.mutation.graphql'; import updateEpicConfidentialMutation from '~/sidebar/queries/update_epic_confidential.mutation.graphql';
...@@ -216,6 +218,12 @@ export const todoQueries = { ...@@ -216,6 +218,12 @@ export const todoQueries = {
[IssuableType.Epic]: { [IssuableType.Epic]: {
query: epicTodoQuery, query: epicTodoQuery,
}, },
[IssuableType.Issue]: {
query: issueTodoQuery,
},
[IssuableType.MergeRequest]: {
query: mergeRequestTodoQuery,
},
}; };
export const TodoMutationTypes = { export const TodoMutationTypes = {
......
...@@ -19,8 +19,10 @@ import SidebarDueDateWidget from '~/sidebar/components/date/sidebar_date_widget. ...@@ -19,8 +19,10 @@ import SidebarDueDateWidget from '~/sidebar/components/date/sidebar_date_widget.
import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue'; import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue';
import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue'; import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue';
import SidebarDropdownWidget from '~/sidebar/components/sidebar_dropdown_widget.vue'; import SidebarDropdownWidget from '~/sidebar/components/sidebar_dropdown_widget.vue';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import { apolloProvider } from '~/sidebar/graphql'; import { apolloProvider } from '~/sidebar/graphql';
import trackShowInviteMemberLink from '~/sidebar/track_invite_members'; import trackShowInviteMemberLink from '~/sidebar/track_invite_members';
import { toIssueGid, toMergeRequestGid } from '~/sidebar/utils';
import Translate from '../vue_shared/translate'; import Translate from '../vue_shared/translate';
import SidebarAssignees from './components/assignees/sidebar_assignees.vue'; import SidebarAssignees from './components/assignees/sidebar_assignees.vue';
import CopyEmailToClipboard from './components/copy_email_to_clipboard.vue'; import CopyEmailToClipboard from './components/copy_email_to_clipboard.vue';
...@@ -40,6 +42,37 @@ function getSidebarOptions(sidebarOptEl = document.querySelector('.js-sidebar-op ...@@ -40,6 +42,37 @@ function getSidebarOptions(sidebarOptEl = document.querySelector('.js-sidebar-op
return JSON.parse(sidebarOptEl.innerHTML); return JSON.parse(sidebarOptEl.innerHTML);
} }
function mountSidebarToDoWidget() {
const el = document.querySelector('.js-issuable-todo');
if (!el) {
return false;
}
const { projectPath, iid, id } = el.dataset;
return new Vue({
el,
apolloProvider,
components: {
SidebarTodoWidget,
},
provide: {
isClassicSidebar: true,
},
render: (createElement) =>
createElement('sidebar-todo-widget', {
props: {
fullPath: projectPath,
issuableId: isInIssuePage() || isInDesignPage() ? toIssueGid(id) : toMergeRequestGid(id),
issuableIid: iid,
issuableType:
isInIssuePage() || isInDesignPage() ? IssuableType.Issue : IssuableType.MergeRequest,
},
}),
});
}
function getSidebarAssigneeAvailabilityData() { function getSidebarAssigneeAvailabilityData() {
const sidebarAssigneeEl = document.querySelectorAll('.js-sidebar-assignee-data input'); const sidebarAssigneeEl = document.querySelectorAll('.js-sidebar-assignee-data input');
return Array.from(sidebarAssigneeEl) return Array.from(sidebarAssigneeEl)
...@@ -497,6 +530,7 @@ export function mountSidebar(mediator) { ...@@ -497,6 +530,7 @@ export function mountSidebar(mediator) {
initInviteMembersModal(); initInviteMembersModal();
initInviteMembersTrigger(); initInviteMembersTrigger();
mountSidebarToDoWidget();
if (isAssigneesWidgetShown) { if (isAssigneesWidgetShown) {
mountAssigneesComponent(); mountAssigneesComponent();
} else { } else {
......
query issueTodos($fullPath: ID!, $iid: String!) {
workspace: project(fullPath: $fullPath) {
__typename
issuable: issue(iid: $iid) {
__typename
id
currentUserTodos(state: pending) {
nodes {
id
}
}
}
}
}
query mergeRequestTodos($fullPath: ID!, $iid: String!) {
workspace: project(fullPath: $fullPath) {
__typename
issuable: mergeRequest(iid: $iid) {
__typename
id
currentUserTodos(state: pending) {
nodes {
id
}
}
}
}
}
export const toLabelGid = (id) => `gid://gitlab/Label/${id}`; export const toLabelGid = (id) => `gid://gitlab/Label/${id}`;
export const toIssueGid = (id) => `gid://gitlab/Issue/${id}`;
export const toMergeRequestGid = (id) => `gid://gitlab/MergeRequest/${id}`;
...@@ -175,7 +175,8 @@ ...@@ -175,7 +175,8 @@
} }
} }
.block { .block,
.issuable-sidebar-header {
@include clearfix; @include clearfix;
padding: $gl-padding 0; padding: $gl-padding 0;
border-bottom: 1px solid $border-gray-normal; border-bottom: 1px solid $border-gray-normal;
...@@ -184,11 +185,6 @@ ...@@ -184,11 +185,6 @@
width: $gutter-inner-width; width: $gutter-inner-width;
// -- // --
&.issuable-sidebar-header {
padding-top: 0;
padding-bottom: 10px;
}
&:last-child { &:last-child {
border: 0; border: 0;
} }
...@@ -273,10 +269,6 @@ ...@@ -273,10 +269,6 @@
padding: 0 20px; padding: 0 20px;
} }
.issuable-sidebar-header {
padding-top: 10px;
}
&:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) { &:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) {
.issuable-sidebar-header { .issuable-sidebar-header {
display: none; display: none;
...@@ -302,7 +294,6 @@ ...@@ -302,7 +294,6 @@
} }
.gutter-toggle { .gutter-toggle {
margin-top: 7px;
border-left: 1px solid $border-gray-normal; border-left: 1px solid $border-gray-normal;
text-align: center; text-align: center;
} }
...@@ -331,20 +322,21 @@ ...@@ -331,20 +322,21 @@
width: $gutter-collapsed-width; width: $gutter-collapsed-width;
padding: 0; padding: 0;
.block { .block,
.issuable-sidebar-header {
width: $gutter-collapsed-width - 2px; width: $gutter-collapsed-width - 2px;
padding: 0; padding: 0;
border-bottom: 0; border-bottom: 0;
overflow: hidden; overflow: hidden;
}
.block,
.gutter-toggle,
.sidebar-collapsed-container {
&.with-sub-blocks .sub-block:hover, &.with-sub-blocks .sub-block:hover,
&:not(.with-sub-blocks):hover { &:not(.with-sub-blocks):hover {
background-color: $gray-100; background-color: $gray-100;
} }
&.issuable-sidebar-header {
padding-top: 0;
}
} }
.participants { .participants {
......
...@@ -285,7 +285,7 @@ module ApplicationHelper ...@@ -285,7 +285,7 @@ module ApplicationHelper
def page_class def page_class
class_names = [] class_names = []
class_names << 'issue-boards-page gl-overflow-auto' if current_controller?(:boards) class_names << 'issue-boards-page gl-overflow-auto' if current_controller?(:boards)
class_names << 'epic-boards-page' if current_controller?(:epic_boards) class_names << 'epic-boards-page gl-overflow-auto' if current_controller?(:epic_boards)
class_names << 'environment-logs-page' if current_controller?(:logs) class_names << 'environment-logs-page' if current_controller?(:logs)
class_names << 'with-performance-bar' if performance_bar_enabled? class_names << 'with-performance-bar' if performance_bar_enabled?
class_names << system_message_class class_names << system_message_class
......
...@@ -10,19 +10,13 @@ ...@@ -10,19 +10,13 @@
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite', 'aria-label': issuable_type } %aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite', 'aria-label': issuable_type }
.issuable-sidebar .issuable-sidebar
.block.issuable-sidebar-header .issuable-sidebar-header.gl-py-3
- if signed_in
%span.issuable-header-text.hide-collapsed.float-left
= _('To Do')
%a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", href: "#", "aria-label" => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } } %a.gutter-toggle.float-right.js-sidebar-toggle.has-tooltip{ role: "button", href: "#", "aria-label" => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }
= sidebar_gutter_toggle_icon = sidebar_gutter_toggle_icon
- if signed_in - if signed_in
= render "shared/issuable/sidebar_todo", issuable_sidebar: issuable_sidebar .js-issuable-todo{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } }
= form_for issuable_type, url: issuable_sidebar[:issuable_json_path], remote: true, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f| = form_for issuable_type, url: issuable_sidebar[:issuable_json_path], remote: true, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
- if signed_in
.block.todo.hide-expanded
= render "shared/issuable/sidebar_todo", issuable_sidebar: issuable_sidebar, is_collapsed: true
.block.assignee.qa-assignee-block .block.assignee.qa-assignee-block
= render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in = render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in
......
- is_collapsed = local_assigns.fetch(:is_collapsed, false)
- has_todo = !!issuable_sidebar.dig(:current_user, :todo, :id)
- todo_button_data = issuable_todo_button_data(issuable_sidebar, is_collapsed)
- button_title = has_todo ? todo_button_data[:mark_text] : todo_button_data[:todo_text]
- button_icon = has_todo ? todo_button_data[:mark_icon] : todo_button_data[:todo_icon]
%button.issuable-todo-btn.js-issuable-todo{ type: 'button',
class: (is_collapsed ? 'btn-blank sidebar-collapsed-icon dont-change-state has-tooltip' : 'gl-button btn btn-default issuable-header-btn float-right'),
title: button_title,
'aria-label' => button_title,
data: todo_button_data }
%span.issuable-todo-inner.js-issuable-todo-inner<
= is_collapsed ? button_icon : button_title
= loading_icon(css_class: is_collapsed ? '' : 'gl-ml-3')
...@@ -10,6 +10,14 @@ exports[`ee/BoardContentSidebar matches the snapshot 1`] = ` ...@@ -10,6 +10,14 @@ exports[`ee/BoardContentSidebar matches the snapshot 1`] = `
Issue details Issue details
</h2> </h2>
<sidebartodowidget-stub
class="gl-mt-3"
fullpath="gitlab-org/gitlab-test"
issuableid="gid://gitlab/Issue/436"
issuableiid="27"
issuabletype="issue"
/>
<boardsidebartitle-stub /> <boardsidebartitle-stub />
<sidebarassigneeswidget-stub <sidebarassigneeswidget-stub
......
...@@ -76,6 +76,7 @@ describe('ee/BoardContentSidebar', () => { ...@@ -76,6 +76,7 @@ describe('ee/BoardContentSidebar', () => {
SidebarSubscriptionsWidget: true, SidebarSubscriptionsWidget: true,
SidebarWeightWidget: true, SidebarWeightWidget: true,
SidebarDropdownWidget: true, SidebarDropdownWidget: true,
SidebarTodoWidget: true,
MountingPortal: true, MountingPortal: true,
}, },
}); });
......
...@@ -157,6 +157,7 @@ export const mockIssueProjectPath = `${mockIssueGroupPath}/gitlab-test`; ...@@ -157,6 +157,7 @@ export const mockIssueProjectPath = `${mockIssueGroupPath}/gitlab-test`;
export const mockIssue = { export const mockIssue = {
id: '436', id: '436',
fullId: 'gid://gitlab/Issue/436',
iid: '27', iid: '27',
title: 'Issue 1', title: 'Issue 1',
referencePath: `${mockIssueProjectPath}#27`, referencePath: `${mockIssueProjectPath}#27`,
......
...@@ -10,7 +10,8 @@ import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.v ...@@ -10,7 +10,8 @@ import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.v
import { ISSUABLE } from '~/boards/constants'; import { ISSUABLE } from '~/boards/constants';
import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue'; import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue';
import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue'; import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue';
import { mockIssue, mockIssueGroupPath, mockIssueProjectPath } from '../mock_data'; import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import { mockActiveIssue, mockIssue, mockIssueGroupPath, mockIssueProjectPath } from '../mock_data';
describe('BoardContentSidebar', () => { describe('BoardContentSidebar', () => {
let wrapper; let wrapper;
...@@ -26,7 +27,7 @@ describe('BoardContentSidebar', () => { ...@@ -26,7 +27,7 @@ describe('BoardContentSidebar', () => {
}, },
getters: { getters: {
activeBoardItem: () => { activeBoardItem: () => {
return { ...mockIssue, epic: null }; return { ...mockActiveIssue, epic: null };
}, },
groupPathForActiveIssue: () => mockIssueGroupPath, groupPathForActiveIssue: () => mockIssueGroupPath,
projectPathForActiveIssue: () => mockIssueProjectPath, projectPathForActiveIssue: () => mockIssueProjectPath,
...@@ -110,6 +111,10 @@ describe('BoardContentSidebar', () => { ...@@ -110,6 +111,10 @@ describe('BoardContentSidebar', () => {
expect(wrapper.findComponent(GlDrawer).props('open')).toBe(true); expect(wrapper.findComponent(GlDrawer).props('open')).toBe(true);
}); });
it('renders SidebarTodoWidget', () => {
expect(wrapper.findComponent(SidebarTodoWidget).exists()).toBe(true);
});
it('renders BoardSidebarLabelsSelect', () => { it('renders BoardSidebarLabelsSelect', () => {
expect(wrapper.findComponent(BoardSidebarLabelsSelect).exists()).toBe(true); expect(wrapper.findComponent(BoardSidebarLabelsSelect).exists()).toBe(true);
}); });
...@@ -147,7 +152,7 @@ describe('BoardContentSidebar', () => { ...@@ -147,7 +152,7 @@ describe('BoardContentSidebar', () => {
expect(toggleBoardItem).toHaveBeenCalledTimes(1); expect(toggleBoardItem).toHaveBeenCalledTimes(1);
expect(toggleBoardItem).toHaveBeenCalledWith(expect.any(Object), { expect(toggleBoardItem).toHaveBeenCalledWith(expect.any(Object), {
boardItem: { ...mockIssue, epic: null }, boardItem: { ...mockActiveIssue, epic: null },
sidebarType: ISSUABLE, sidebarType: ISSUABLE,
}); });
}); });
......
import { GlAlert } from '@gitlab/ui'; import { GlAlert } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Draggable from 'vuedraggable'; import Draggable from 'vuedraggable';
import Vuex from 'vuex'; import Vuex from 'vuex';
import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue'; import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue';
...@@ -8,8 +9,7 @@ import BoardColumnDeprecated from '~/boards/components/board_column_deprecated.v ...@@ -8,8 +9,7 @@ import BoardColumnDeprecated from '~/boards/components/board_column_deprecated.v
import BoardContent from '~/boards/components/board_content.vue'; import BoardContent from '~/boards/components/board_content.vue';
import { mockLists, mockListsWithModel } from '../mock_data'; import { mockLists, mockListsWithModel } from '../mock_data';
const localVue = createLocalVue(); Vue.use(Vuex);
localVue.use(Vuex);
const actions = { const actions = {
moveList: jest.fn(), moveList: jest.fn(),
...@@ -44,7 +44,6 @@ describe('BoardContent', () => { ...@@ -44,7 +44,6 @@ describe('BoardContent', () => {
...state, ...state,
}); });
wrapper = shallowMount(BoardContent, { wrapper = shallowMount(BoardContent, {
localVue,
propsData: { propsData: {
lists: mockListsWithModel, lists: mockListsWithModel,
disabled: false, disabled: false,
......
...@@ -182,6 +182,7 @@ export const mockIssue = { ...@@ -182,6 +182,7 @@ export const mockIssue = {
export const mockActiveIssue = { export const mockActiveIssue = {
...mockIssue, ...mockIssue,
fullId: 'gid://gitlab/Issue/436',
id: 436, id: 436,
iid: '27', iid: '27',
subscribed: false, subscribed: false,
......
/* eslint-disable no-new */
import MockAdapter from 'axios-mock-adapter';
import { clone } from 'lodash';
import waitForPromises from 'helpers/wait_for_promises';
import { TEST_HOST } from 'spec/test_constants';
import axios from '~/lib/utils/axios_utils';
import Sidebar from '~/right_sidebar';
import { fixTitle } from '~/tooltips';
jest.mock('~/tooltips');
describe('Issuable right sidebar collapsed todo toggle', () => {
const fixtureName = 'issues/open-issue.html';
const jsonFixtureName = 'todos/todos.json';
let mock;
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');
mock = new MockAdapter(axios);
mock.onPost(`${TEST_HOST}/frontend-fixtures/issues-project/todos`).reply(() => {
const response = clone(todoData);
return [200, response];
});
mock.onDelete(/(.*)\/dashboard\/todos\/\d+$/).reply(() => {
const response = clone(todoData);
delete response.delete_path;
return [200, response];
});
});
afterEach(() => {
mock.restore();
});
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 svg')
.getAttribute('data-testid'),
).toBe('todo-add-icon');
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 a to do');
});
it('toggle todo state', (done) => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setImmediate(() => {
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
).not.toBeNull();
expect(
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon svg.todo-undone')
.getAttribute('data-testid'),
).toBe('todo-done-icon');
done();
});
});
it('toggle todo state of expanded todo toggle', (done) => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setImmediate(() => {
expect(
document.querySelector('.issuable-sidebar-header .js-issuable-todo').textContent.trim(),
).toBe('Mark as done');
done();
});
});
it('toggles todo button tooltip', (done) => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setImmediate(() => {
const el = document.querySelector('.js-issuable-todo.sidebar-collapsed-icon');
expect(el.getAttribute('title')).toBe('Mark as done');
expect(fixTitle).toHaveBeenCalledWith(el);
done();
});
});
it('marks todo as done', (done) => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
waitForPromises()
.then(() => {
expect(
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .todo-undone'),
).not.toBeNull();
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
})
.then(waitForPromises)
.then(() => {
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 a to do');
})
.then(done)
.catch(done.fail);
});
it('updates aria-label to Mark as done', (done) => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
setImmediate(() => {
expect(
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
.getAttribute('aria-label'),
).toBe('Mark as done');
done();
});
});
it('updates aria-label to add todo', (done) => {
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
waitForPromises()
.then(() => {
expect(
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
.getAttribute('aria-label'),
).toBe('Mark as done');
document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click();
})
.then(waitForPromises)
.then(() => {
expect(
document
.querySelector('.js-issuable-todo.sidebar-collapsed-icon')
.getAttribute('aria-label'),
).toBe('Add a to do');
})
.then(done)
.catch(done.fail);
});
});
...@@ -66,22 +66,6 @@ describe('RightSidebar', () => { ...@@ -66,22 +66,6 @@ describe('RightSidebar', () => {
assertSidebarState('collapsed'); assertSidebarState('collapsed');
}); });
it('should broadcast todo:toggle event when add todo clicked', (done) => {
const todos = getJSONFixture('todos/todos.json');
mock.onPost(/(.*)\/todos$/).reply(200, todos);
const todoToggleSpy = jest.fn();
$(document).on('todo:toggle', todoToggleSpy);
$('.issuable-sidebar-header .js-issuable-todo').click();
setImmediate(() => {
expect(todoToggleSpy.mock.calls.length).toEqual(1);
done();
});
});
it('should not hide collapsed icons', () => { it('should not hide collapsed icons', () => {
[].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => { [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => {
expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy(); expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy();
......
import { GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
...@@ -28,6 +29,7 @@ describe('Sidebar Todo Widget', () => { ...@@ -28,6 +29,7 @@ describe('Sidebar Todo Widget', () => {
apolloProvider: fakeApollo, apolloProvider: fakeApollo,
provide: { provide: {
canUpdate: true, canUpdate: true,
isClassicSidebar: true,
}, },
propsData: { propsData: {
fullPath: 'group', fullPath: 'group',
...@@ -83,4 +85,42 @@ describe('Sidebar Todo Widget', () => { ...@@ -83,4 +85,42 @@ describe('Sidebar Todo Widget', () => {
expect(createFlash).toHaveBeenCalled(); expect(createFlash).toHaveBeenCalled();
}); });
describe('collapsed', () => {
const event = { stopPropagation: jest.fn(), preventDefault: jest.fn() };
beforeEach(() => {
createComponent({
todosQueryHandler: jest.fn().mockResolvedValue(noTodosResponse),
});
return waitForPromises();
});
it('shows add todo icon', () => {
expect(wrapper.find(GlIcon).exists()).toBe(true);
expect(wrapper.find(GlIcon).props('name')).toBe('todo-add');
});
it('sets default tooltip title', () => {
expect(wrapper.find(GlIcon).attributes('title')).toBe('Add a to do');
});
it('when user has a to do', async () => {
createComponent({
todosQueryHandler: jest.fn().mockResolvedValue(todosResponse),
});
await waitForPromises();
expect(wrapper.find(GlIcon).props('name')).toBe('todo-done');
expect(wrapper.find(GlIcon).attributes('title')).toBe('Mark as done');
});
it('emits `todoUpdated` event on click on icon', async () => {
wrapper.find(GlIcon).vm.$emit('click', event);
await wrapper.vm.$nextTick();
expect(wrapper.emitted('todoUpdated')).toEqual([[false]]);
});
});
}); });
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