Commit 4a25e415 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '346158-add-work-item-modal-url-query' into 'master'

Add url query when work item modal is open

See merge request gitlab-org/gitlab!83988
parents cb2f3ae5 1715eda1
...@@ -4,6 +4,8 @@ import $ from 'jquery'; ...@@ -4,6 +4,8 @@ import $ from 'jquery';
import { convertToGraphQLId } from '~/graphql_shared/utils'; import { convertToGraphQLId } from '~/graphql_shared/utils';
import { TYPE_WORK_ITEM } from '~/graphql_shared/constants'; import { TYPE_WORK_ITEM } from '~/graphql_shared/constants';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { isPositiveInteger } from '~/lib/utils/number_utils';
import { getParameterByName, setUrlParams, updateHistory } from '~/lib/utils/url_utility';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import TaskList from '~/task_list'; import TaskList from '~/task_list';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
...@@ -65,13 +67,17 @@ export default { ...@@ -65,13 +67,17 @@ export default {
}, },
}, },
data() { data() {
const workItemId = getParameterByName('work_item_id');
return { return {
preAnimation: false, preAnimation: false,
pulseAnimation: false, pulseAnimation: false,
initialUpdate: true, initialUpdate: true,
taskButtons: [], taskButtons: [],
activeTask: {}, activeTask: {},
workItemId: null, workItemId: isPositiveInteger(workItemId)
? convertToGraphQLId(TYPE_WORK_ITEM, workItemId)
: undefined,
}; };
}, },
computed: { computed: {
...@@ -184,6 +190,7 @@ export default { ...@@ -184,6 +190,7 @@ export default {
taskLink.addEventListener('click', (e) => { taskLink.addEventListener('click', (e) => {
e.preventDefault(); e.preventDefault();
this.workItemId = convertToGraphQLId(TYPE_WORK_ITEM, issue); this.workItemId = convertToGraphQLId(TYPE_WORK_ITEM, issue);
this.updateWorkItemIdUrlQuery(issue);
this.track('viewed_work_item_from_modal', { this.track('viewed_work_item_from_modal', {
category: 'workItems:show', category: 'workItems:show',
label: 'work_item_view', label: 'work_item_view',
...@@ -232,12 +239,19 @@ export default { ...@@ -232,12 +239,19 @@ export default {
this.$refs.modal.hide(); this.$refs.modal.hide();
}, },
closeWorkItemDetailModal() { closeWorkItemDetailModal() {
this.workItemId = null; this.workItemId = undefined;
this.updateWorkItemIdUrlQuery(undefined);
}, },
handleCreateTask(description) { handleCreateTask(description) {
this.$emit('updateDescription', description); this.$emit('updateDescription', description);
this.closeCreateTaskModal(); this.closeCreateTaskModal();
}, },
updateWorkItemIdUrlQuery(workItemId) {
updateHistory({
url: setUrlParams({ work_item_id: workItemId }),
replace: true,
});
},
}, },
safeHtmlConfig: { ADD_TAGS: ['gl-emoji', 'copy-code'] }, safeHtmlConfig: { ADD_TAGS: ['gl-emoji', 'copy-code'] },
}; };
...@@ -281,7 +295,7 @@ export default { ...@@ -281,7 +295,7 @@ export default {
body-class="gl-p-0!" body-class="gl-p-0!"
> >
<create-work-item <create-work-item
:is-modal="true" is-modal
:initial-title="activeTask.title" :initial-title="activeTask.title"
:issue-gid="issueGid" :issue-gid="issueGid"
:lock-version="lockVersion" :lock-version="lockVersion"
......
...@@ -2,11 +2,13 @@ import $ from 'jquery'; ...@@ -2,11 +2,13 @@ import $ from 'jquery';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import '~/behaviors/markdown/render_gfm'; import '~/behaviors/markdown/render_gfm';
import { GlTooltip, GlModal } from '@gitlab/ui'; import { GlTooltip, GlModal } from '@gitlab/ui';
import setWindowLocation from 'helpers/set_window_location_helper';
import { stubComponent } from 'helpers/stub_component'; import { stubComponent } from 'helpers/stub_component';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { mockTracking } from 'helpers/tracking_helper'; import { mockTracking } from 'helpers/tracking_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import Description from '~/issues/show/components/description.vue'; import Description from '~/issues/show/components/description.vue';
import { updateHistory } from '~/lib/utils/url_utility';
import TaskList from '~/task_list'; import TaskList from '~/task_list';
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue'; import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
import CreateWorkItem from '~/work_items/pages/create_work_item.vue'; import CreateWorkItem from '~/work_items/pages/create_work_item.vue';
...@@ -17,6 +19,10 @@ import { ...@@ -17,6 +19,10 @@ import {
} from '../mock_data/mock_data'; } from '../mock_data/mock_data';
jest.mock('~/flash'); jest.mock('~/flash');
jest.mock('~/lib/utils/url_utility', () => ({
...jest.requireActual('~/lib/utils/url_utility'),
updateHistory: jest.fn(),
}));
jest.mock('~/task_list'); jest.mock('~/task_list');
const showModal = jest.fn(); const showModal = jest.fn();
...@@ -55,6 +61,8 @@ describe('Description component', () => { ...@@ -55,6 +61,8 @@ describe('Description component', () => {
} }
beforeEach(() => { beforeEach(() => {
setWindowLocation(TEST_HOST);
if (!document.querySelector('.issuable-meta')) { if (!document.querySelector('.issuable-meta')) {
const metaData = document.createElement('div'); const metaData = document.createElement('div');
metaData.classList.add('issuable-meta'); metaData.classList.add('issuable-meta');
...@@ -285,48 +293,86 @@ describe('Description component', () => { ...@@ -285,48 +293,86 @@ describe('Description component', () => {
describe('work items detail', () => { describe('work items detail', () => {
const findTaskLink = () => wrapper.find('a.gfm-issue'); const findTaskLink = () => wrapper.find('a.gfm-issue');
beforeEach(() => { describe('when opening and closing', () => {
createComponent({ beforeEach(() => {
props: { createComponent({
descriptionHtml: descriptionHtmlWithTask, props: {
}, descriptionHtml: descriptionHtmlWithTask,
provide: { },
glFeatures: { workItems: true }, provide: {
}, glFeatures: { workItems: true },
},
});
return nextTick();
}); });
return nextTick();
});
it('opens when task button is clicked', async () => { it('opens when task button is clicked', async () => {
expect(findWorkItemDetailModal().props('visible')).toBe(false); expect(findWorkItemDetailModal().props('visible')).toBe(false);
await findTaskLink().trigger('click'); await findTaskLink().trigger('click');
expect(findWorkItemDetailModal().props('visible')).toBe(true); expect(findWorkItemDetailModal().props('visible')).toBe(true);
}); expect(updateHistory).toHaveBeenCalledWith({
url: `${TEST_HOST}/?work_item_id=2`,
replace: true,
});
});
it('closes from an open state', async () => { it('closes from an open state', async () => {
await findTaskLink().trigger('click'); await findTaskLink().trigger('click');
expect(findWorkItemDetailModal().props('visible')).toBe(true); expect(findWorkItemDetailModal().props('visible')).toBe(true);
findWorkItemDetailModal().vm.$emit('close'); findWorkItemDetailModal().vm.$emit('close');
await nextTick(); await nextTick();
expect(findWorkItemDetailModal().props('visible')).toBe(false); expect(findWorkItemDetailModal().props('visible')).toBe(false);
}); expect(updateHistory).toHaveBeenLastCalledWith({
url: `${TEST_HOST}/`,
replace: true,
});
});
it('tracks when opened', async () => { it('tracks when opened', async () => {
const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
await findTaskLink().trigger('click'); await findTaskLink().trigger('click');
expect(trackingSpy).toHaveBeenCalledWith('workItems:show', 'viewed_work_item_from_modal', { expect(trackingSpy).toHaveBeenCalledWith(
category: 'workItems:show', 'workItems:show',
label: 'work_item_view', 'viewed_work_item_from_modal',
property: 'type_task', {
category: 'workItems:show',
label: 'work_item_view',
property: 'type_task',
},
);
}); });
}); });
describe('when url query `work_item_id` exists', () => {
it.each`
behavior | workItemId | visible
${'opens'} | ${'123'} | ${true}
${'does not open'} | ${'123e'} | ${false}
${'does not open'} | ${'12e3'} | ${false}
${'does not open'} | ${'1e23'} | ${false}
${'does not open'} | ${'x'} | ${false}
${'does not open'} | ${'undefined'} | ${false}
`(
'$behavior when url contains `work_item_id=$workItemId`',
async ({ workItemId, visible }) => {
setWindowLocation(`?work_item_id=${workItemId}`);
createComponent({
props: { descriptionHtml: descriptionHtmlWithTask },
provide: { glFeatures: { workItems: true } },
});
expect(findWorkItemDetailModal().props('visible')).toBe(visible);
},
);
});
}); });
}); });
}); });
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