Commit 70681eab authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '331875-migrate-add-to-do-sidebar-button-to-widget' into 'master'

Migrate Add To Do button to widget [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!65709
parents bb88d2b5 9b4589cf
......@@ -11,6 +11,7 @@ import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assig
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_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';
export default {
......@@ -24,6 +25,7 @@ export default {
BoardSidebarLabelsSelect,
SidebarSubscriptionsWidget,
SidebarDropdownWidget,
SidebarTodoWidget,
MountingPortal,
SidebarWeightWidget: () =>
import('ee_component/sidebar/components/weight/sidebar_weight_widget.vue'),
......@@ -90,6 +92,15 @@ export default {
<template #title>
<h2 class="gl-my-0 gl-font-size-h2 gl-line-height-24">{{ __('Issue details') }}</h2>
</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>
<board-sidebar-title />
<sidebar-assignees-widget
......
<script>
import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql';
import TodoButton from '~/vue_shared/components/sidebar/todo_button.vue';
import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue';
import createDesignTodoMutation from '../graphql/mutations/create_design_todo.mutation.graphql';
import getDesignQuery from '../graphql/queries/get_design.query.graphql';
import allVersionsMixin from '../mixins/all_versions';
......
......@@ -2,7 +2,7 @@
import $ from 'jquery';
import Cookies from 'js-cookie';
import { fixTitle, hide } from '~/tooltips';
import { hide } from '~/tooltips';
import createFlash from './flash';
import axios from './lib/utils/axios_utils';
import { sprintf, s__, __ } from './locale';
......@@ -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) {
if ($(e.currentTarget).hasClass('dont-change-state')) {
return;
......
<script>
import { GlTooltipDirective } from '@gitlab/ui';
import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { produce } from 'immer';
import createFlash from '~/flash';
import { __, sprintf } from '~/locale';
import { todoQueries, TodoMutationTypes, todoMutations } from '~/sidebar/constants';
import TodoButton from '~/vue_shared/components/sidebar/todo_button.vue';
import { todoLabel } from '~/vue_shared/components/sidebar/todo_toggle//utils';
import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue';
export default {
components: {
GlButton,
GlIcon,
TodoButton,
},
directives: {
GlTooltip: GlTooltipDirective,
},
inject: {
isClassicSidebar: {
default: false,
},
},
props: {
issuableId: {
type: String,
......@@ -86,6 +94,12 @@ export default {
}
return TodoMutationTypes.Create;
},
collapsedButtonIcon() {
return this.hasTodo ? 'todo-done' : 'todo-add';
},
tootltipTitle() {
return todoLabel(this.hasTodo);
},
},
methods: {
toggleTodo() {
......@@ -158,7 +172,24 @@ export default {
:is-todo="hasTodo"
:loading="isLoading"
size="small"
class="hide-collapsed"
@click.stop.prevent="toggleTodo"
/>
<gl-button
v-if="isClassicSidebar"
category="tertiary"
type="reset"
class="sidebar-collapsed-icon sidebar-collapsed-container gl-rounded-0! gl-shadow-none!"
@click.stop.prevent="toggleTodo"
>
<gl-icon
v-gl-tooltip.left.viewport
:title="tootltipTitle"
:size="16"
:class="{ 'todo-undone': hasTodo }"
:name="collapsedButtonIcon"
:aria-label="collapsedButtonIcon"
/>
</gl-button>
</div>
</template>
......@@ -13,10 +13,12 @@ import issueDueDateQuery from '~/sidebar/queries/issue_due_date.query.graphql';
import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql';
import issueSubscribedQuery from '~/sidebar/queries/issue_subscribed.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 mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql';
import mergeRequestSubscribed from '~/sidebar/queries/merge_request_subscribed.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 todoMarkDoneMutation from '~/sidebar/queries/todo_mark_done.mutation.graphql';
import updateEpicConfidentialMutation from '~/sidebar/queries/update_epic_confidential.mutation.graphql';
......@@ -216,6 +218,12 @@ export const todoQueries = {
[IssuableType.Epic]: {
query: epicTodoQuery,
},
[IssuableType.Issue]: {
query: issueTodoQuery,
},
[IssuableType.MergeRequest]: {
query: mergeRequestTodoQuery,
},
};
export const TodoMutationTypes = {
......
......@@ -2,6 +2,8 @@ import $ from 'jquery';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createFlash from '~/flash';
import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger';
import { IssuableType } from '~/issue_show/constants';
......@@ -19,6 +21,7 @@ import SidebarDueDateWidget from '~/sidebar/components/date/sidebar_date_widget.
import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue';
import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_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 trackShowInviteMemberLink from '~/sidebar/track_invite_members';
import Translate from '../vue_shared/translate';
......@@ -40,6 +43,40 @@ function getSidebarOptions(sidebarOptEl = document.querySelector('.js-sidebar-op
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()
? convertToGraphQLId(TYPE_ISSUE, id)
: convertToGraphQLId(TYPE_MERGE_REQUEST, id),
issuableIid: iid,
issuableType:
isInIssuePage() || isInDesignPage() ? IssuableType.Issue : IssuableType.MergeRequest,
},
}),
});
}
function getSidebarAssigneeAvailabilityData() {
const sidebarAssigneeEl = document.querySelectorAll('.js-sidebar-assignee-data input');
return Array.from(sidebarAssigneeEl)
......@@ -497,6 +534,7 @@ export function mountSidebar(mediator) {
initInviteMembersModal();
initInviteMembersTrigger();
mountSidebarToDoWidget();
if (isAssigneesWidgetShown) {
mountAssigneesComponent();
} 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
}
}
}
}
}
......@@ -4,7 +4,7 @@ import TodoButton from './todo_button.vue';
export default {
component: TodoButton,
title: 'vue_shared/components/todo_button',
title: 'vue_shared/components/todo_toggle/todo_button',
};
const Template = (args, { argTypes }) => ({
......
<script>
import { GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import { todoLabel } from './utils';
export default {
components: {
......@@ -15,7 +15,7 @@ export default {
},
computed: {
buttonLabel() {
return this.isTodo ? __('Mark as done') : __('Add a to do');
return todoLabel(this.isTodo);
},
},
methods: {
......
import { __ } from '~/locale';
export const todoLabel = (hasTodo) => {
return hasTodo ? __('Mark as done') : __('Add a to do');
};
......@@ -175,7 +175,8 @@
}
}
.block {
.block,
.issuable-sidebar-header {
@include clearfix;
padding: $gl-padding 0;
border-bottom: 1px solid $border-gray-normal;
......@@ -184,11 +185,6 @@
width: $gutter-inner-width;
// --
&.issuable-sidebar-header {
padding-top: 0;
padding-bottom: 10px;
}
&:last-child {
border: 0;
}
......@@ -273,10 +269,6 @@
padding: 0 20px;
}
.issuable-sidebar-header {
padding-top: 10px;
}
&:not(.boards-sidebar):not([data-signed-in]):not([data-always-show-toggle]) {
.issuable-sidebar-header {
display: none;
......@@ -302,7 +294,6 @@
}
.gutter-toggle {
margin-top: 7px;
border-left: 1px solid $border-gray-normal;
text-align: center;
}
......@@ -331,20 +322,21 @@
width: $gutter-collapsed-width;
padding: 0;
.block {
.block,
.issuable-sidebar-header {
width: $gutter-collapsed-width - 2px;
padding: 0;
border-bottom: 0;
overflow: hidden;
}
.block,
.gutter-toggle,
.sidebar-collapsed-container {
&.with-sub-blocks .sub-block:hover,
&:not(.with-sub-blocks):hover {
background-color: $gray-100;
}
&.issuable-sidebar-header {
padding-top: 0;
}
}
.participants {
......
......@@ -285,7 +285,7 @@ module ApplicationHelper
def page_class
class_names = []
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 << 'with-performance-bar' if performance_bar_enabled?
class_names << system_message_class
......
......@@ -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 }
.issuable-sidebar
.block.issuable-sidebar-header
- if signed_in
%span.issuable-header-text.hide-collapsed.float-left
= _('To Do')
.issuable-sidebar-header.gl-py-3
%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
- 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|
- if signed_in
.block.todo.hide-expanded
= render "shared/issuable/sidebar_todo", issuable_sidebar: issuable_sidebar, is_collapsed: true
.block.assignee.qa-assignee-block
= 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')
......@@ -47,5 +47,5 @@ To add a story:
Notes:
- Specify the `title` field of the story as the component's file path from the `javascripts/` directory,
e.g. if the component is located at `app/assets/javascripts/vue_shared/components/sidebar/todo_button.vue`, specify the `title` as
e.g. if the component is located at `app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue`, specify the `title` as
`vue_shared/components/To-do Button`. This will ensure the Storybook navigation maps closely to our internal directory structure.
......@@ -10,6 +10,14 @@ exports[`ee/BoardContentSidebar matches the snapshot 1`] = `
Issue details
</h2>
<sidebartodowidget-stub
class="gl-mt-3"
fullpath="gitlab-org/gitlab-test"
issuableid="gid://gitlab/Issue/436"
issuableiid="27"
issuabletype="issue"
/>
<boardsidebartitle-stub />
<sidebarassigneeswidget-stub
......
......@@ -76,6 +76,7 @@ describe('ee/BoardContentSidebar', () => {
SidebarSubscriptionsWidget: true,
SidebarWeightWidget: true,
SidebarDropdownWidget: true,
SidebarTodoWidget: true,
MountingPortal: true,
},
});
......
......@@ -157,6 +157,7 @@ export const mockIssueProjectPath = `${mockIssueGroupPath}/gitlab-test`;
export const mockIssue = {
id: '436',
fullId: 'gid://gitlab/Issue/436',
iid: '27',
title: 'Issue 1',
referencePath: `${mockIssueProjectPath}#27`,
......
......@@ -10,7 +10,8 @@ import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.v
import { ISSUABLE } from '~/boards/constants';
import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_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', () => {
let wrapper;
......@@ -26,7 +27,7 @@ describe('BoardContentSidebar', () => {
},
getters: {
activeBoardItem: () => {
return { ...mockIssue, epic: null };
return { ...mockActiveIssue, epic: null };
},
groupPathForActiveIssue: () => mockIssueGroupPath,
projectPathForActiveIssue: () => mockIssueProjectPath,
......@@ -110,6 +111,10 @@ describe('BoardContentSidebar', () => {
expect(wrapper.findComponent(GlDrawer).props('open')).toBe(true);
});
it('renders SidebarTodoWidget', () => {
expect(wrapper.findComponent(SidebarTodoWidget).exists()).toBe(true);
});
it('renders BoardSidebarLabelsSelect', () => {
expect(wrapper.findComponent(BoardSidebarLabelsSelect).exists()).toBe(true);
});
......@@ -147,7 +152,7 @@ describe('BoardContentSidebar', () => {
expect(toggleBoardItem).toHaveBeenCalledTimes(1);
expect(toggleBoardItem).toHaveBeenCalledWith(expect.any(Object), {
boardItem: { ...mockIssue, epic: null },
boardItem: { ...mockActiveIssue, epic: null },
sidebarType: ISSUABLE,
});
});
......
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 Vuex from 'vuex';
import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue';
......@@ -8,8 +9,7 @@ import BoardColumnDeprecated from '~/boards/components/board_column_deprecated.v
import BoardContent from '~/boards/components/board_content.vue';
import { mockLists, mockListsWithModel } from '../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
Vue.use(Vuex);
const actions = {
moveList: jest.fn(),
......@@ -44,7 +44,6 @@ describe('BoardContent', () => {
...state,
});
wrapper = shallowMount(BoardContent, {
localVue,
propsData: {
lists: mockListsWithModel,
disabled: false,
......
......@@ -182,6 +182,7 @@ export const mockIssue = {
export const mockActiveIssue = {
...mockIssue,
fullId: 'gid://gitlab/Issue/436',
id: 436,
iid: '27',
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);
});
});
......@@ -2,7 +2,7 @@ import { shallowMount, mount } from '@vue/test-utils';
import DesignTodoButton from '~/design_management/components/design_todo_button.vue';
import createDesignTodoMutation from '~/design_management/graphql/mutations/create_design_todo.mutation.graphql';
import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql';
import TodoButton from '~/vue_shared/components/sidebar/todo_button.vue';
import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue';
import mockDesign from '../mock_data/design';
const mockDesignWithPendingTodos = {
......
......@@ -66,22 +66,6 @@ describe('RightSidebar', () => {
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', () => {
[].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => {
expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy();
......
import { GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
......@@ -6,7 +7,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
import epicTodoQuery from '~/sidebar/queries/epic_todo.query.graphql';
import TodoButton from '~/vue_shared/components/sidebar/todo_button.vue';
import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue';
import { todosResponse, noTodosResponse } from '../../mock_data';
jest.mock('~/flash');
......@@ -28,6 +29,7 @@ describe('Sidebar Todo Widget', () => {
apolloProvider: fakeApollo,
provide: {
canUpdate: true,
isClassicSidebar: true,
},
propsData: {
fullPath: 'group',
......@@ -83,4 +85,42 @@ describe('Sidebar Todo Widget', () => {
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]]);
});
});
});
import { GlButton } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils';
import TodoButton from '~/vue_shared/components/sidebar/todo_button.vue';
import TodoButton from '~/vue_shared/components/sidebar/todo_toggle/todo_button.vue';
describe('Todo Button', () => {
let wrapper;
......
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