Commit 9ab8f097 authored by Clement Ho's avatar Clement Ho

Merge branch '9703-track-clicks-on-the-issue-sidebars' into 'master'

Resolve "Track clicks on the issue sidebars"

Closes #9703

See merge request gitlab-org/gitlab-ee!14944
parents 4b7da284 0ae4d32b
export const initSidebarTracking = () => {};
export const trackEvent = () => {};
import Vue from 'vue'; import Vue from 'vue';
import { initSidebarTracking } from 'ee_else_ce/event_tracking/issue_sidebar';
import issuableApp from './components/app.vue'; import issuableApp from './components/app.vue';
import { parseIssuableData } from './utils/parse_data'; import { parseIssuableData } from './utils/parse_data';
import '../vue_shared/vue_resource_interceptor'; import '../vue_shared/vue_resource_interceptor';
...@@ -9,6 +10,9 @@ export default function initIssueableApp() { ...@@ -9,6 +10,9 @@ export default function initIssueableApp() {
components: { components: {
issuableApp, issuableApp,
}, },
mounted() {
initSidebarTracking();
},
render(createElement) { render(createElement) {
return createElement('issuable-app', { return createElement('issuable-app', {
props: parseIssuableData(), props: parseIssuableData(),
......
<script> <script>
import { n__ } from '~/locale'; import { n__ } from '~/locale';
import { trackEvent } from 'ee_else_ce/event_tracking/issue_sidebar';
export default { export default {
name: 'AssigneeTitle', name: 'AssigneeTitle',
...@@ -29,13 +30,23 @@ export default { ...@@ -29,13 +30,23 @@ export default {
return n__('Assignee', `%d Assignees`, assignees); return n__('Assignee', `%d Assignees`, assignees);
}, },
}, },
methods: {
trackEdit() {
trackEvent('click_edit_button', 'assignee');
},
},
}; };
</script> </script>
<template> <template>
<div class="title hide-collapsed"> <div class="title hide-collapsed">
{{ assigneeTitle }} {{ assigneeTitle }}
<i v-if="loading" aria-hidden="true" class="fa fa-spinner fa-spin block-loading"></i> <i v-if="loading" aria-hidden="true" class="fa fa-spinner fa-spin block-loading"></i>
<a v-if="editable" class="js-sidebar-dropdown-toggle edit-link float-right" href="#"> <a
v-if="editable"
class="js-sidebar-dropdown-toggle edit-link float-right"
href="#"
@click.prevent="trackEdit"
>
{{ __('Edit') }} {{ __('Edit') }}
</a> </a>
<a <a
......
...@@ -5,6 +5,7 @@ import tooltip from '~/vue_shared/directives/tooltip'; ...@@ -5,6 +5,7 @@ import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '~/sidebar/event_hub'; import eventHub from '~/sidebar/event_hub';
import editForm from './edit_form.vue'; import editForm from './edit_form.vue';
import { trackEvent } from 'ee_else_ce/event_tracking/issue_sidebar';
export default { export default {
components: { components: {
...@@ -51,6 +52,11 @@ export default { ...@@ -51,6 +52,11 @@ export default {
toggleForm() { toggleForm() {
this.edit = !this.edit; this.edit = !this.edit;
}, },
onEditClick() {
this.toggleForm();
trackEvent('click_edit_button', 'confidentiality');
},
updateConfidentialAttribute(confidential) { updateConfidentialAttribute(confidential) {
this.service this.service
.update('issue', { confidential }) .update('issue', { confidential })
...@@ -82,7 +88,7 @@ export default { ...@@ -82,7 +88,7 @@ export default {
v-if="isEditable" v-if="isEditable"
class="float-right confidential-edit" class="float-right confidential-edit"
href="#" href="#"
@click.prevent="toggleForm" @click.prevent="onEditClick"
> >
{{ __('Edit') }} {{ __('Edit') }}
</a> </a>
......
...@@ -6,6 +6,7 @@ import issuableMixin from '~/vue_shared/mixins/issuable'; ...@@ -6,6 +6,7 @@ import issuableMixin from '~/vue_shared/mixins/issuable';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '~/sidebar/event_hub'; import eventHub from '~/sidebar/event_hub';
import editForm from './edit_form.vue'; import editForm from './edit_form.vue';
import { trackEvent } from 'ee_else_ce/event_tracking/issue_sidebar';
export default { export default {
components: { components: {
...@@ -65,7 +66,11 @@ export default { ...@@ -65,7 +66,11 @@ export default {
toggleForm() { toggleForm() {
this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen; this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen;
}, },
onEditClick() {
this.toggleForm();
trackEvent('click_edit_button', 'lock_issue');
},
updateLockedAttribute(locked) { updateLockedAttribute(locked) {
this.mediator.service this.mediator.service
.update(this.issuableType, { .update(this.issuableType, {
...@@ -109,7 +114,7 @@ export default { ...@@ -109,7 +114,7 @@ export default {
v-if="isEditable" v-if="isEditable"
class="float-right lock-edit" class="float-right lock-edit"
type="button" type="button"
@click.prevent="toggleForm" @click.prevent="onEditClick"
> >
{{ __('Edit') }} {{ __('Edit') }}
</button> </button>
......
...@@ -4,6 +4,7 @@ import icon from '~/vue_shared/components/icon.vue'; ...@@ -4,6 +4,7 @@ import icon from '~/vue_shared/components/icon.vue';
import toggleButton from '~/vue_shared/components/toggle_button.vue'; import toggleButton from '~/vue_shared/components/toggle_button.vue';
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
import { trackEvent } from 'ee_else_ce/event_tracking/issue_sidebar';
const ICON_ON = 'notifications'; const ICON_ON = 'notifications';
const ICON_OFF = 'notifications-off'; const ICON_OFF = 'notifications-off';
...@@ -63,6 +64,8 @@ export default { ...@@ -63,6 +64,8 @@ export default {
// Component event emission. // Component event emission.
this.$emit('toggleSubscription', this.id); this.$emit('toggleSubscription', this.id);
trackEvent('toggle_button', 'notifications', this.subscribed ? 0 : 1);
}, },
onClickCollapsedIcon() { onClickCollapsedIcon() {
this.$emit('toggleSidebar'); this.$emit('toggleSidebar');
......
...@@ -405,7 +405,11 @@ module IssuablesHelper ...@@ -405,7 +405,11 @@ module IssuablesHelper
placement: is_collapsed ? 'left' : nil, placement: is_collapsed ? 'left' : nil,
container: is_collapsed ? 'body' : nil, container: is_collapsed ? 'body' : nil,
boundary: 'viewport', boundary: 'viewport',
is_collapsed: is_collapsed is_collapsed: is_collapsed,
track_label: "right_sidebar",
track_property: "update_todo",
track_event: "click_button",
track_value: ""
} }
end end
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
= _('Milestone') = _('Milestone')
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true') = icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable - if can_edit_issuable
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right' = link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right', data: { track_label: "right_sidebar", track_property: "milestone", track_event: "click_edit_button", track_value: "" }
.value.hide-collapsed .value.hide-collapsed
- if milestone.present? - if milestone.present?
= link_to milestone[:title], milestone[:web_url], class: "bold has-tooltip", title: sidebar_milestone_remaining_days(milestone), data: { container: "body", html: 'true', boundary: 'viewport' } = link_to milestone[:title], milestone[:web_url], class: "bold has-tooltip", title: sidebar_milestone_remaining_days(milestone), data: { container: "body", html: 'true', boundary: 'viewport' }
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
= _('Due date') = _('Due date')
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true') = icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable - if can_edit_issuable
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right' = link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right', data: { track_label: "right_sidebar", track_property: "due_date", track_event: "click_edit_button", track_value: "" }
.value.hide-collapsed .value.hide-collapsed
%span.value-content %span.value-content
- if issuable_sidebar[:due_date] - if issuable_sidebar[:due_date]
...@@ -102,7 +102,7 @@ ...@@ -102,7 +102,7 @@
= _('Labels') = _('Labels')
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true') = icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable - if can_edit_issuable
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link qa-edit-link-labels float-right' = link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link qa-edit-link-labels float-right', data: { track_label: "right_sidebar", track_property: "labels", track_event: "click_edit_button", track_value: "" }
.value.issuable-show-labels.dont-hide.hide-collapsed.qa-labels-block{ class: ("has-labels" if selected_labels.any?) } .value.issuable-show-labels.dont-hide.hide-collapsed.qa-labels-block{ class: ("has-labels" if selected_labels.any?) }
- if selected_labels.any? - if selected_labels.any?
- selected_labels.each do |label_hash| - selected_labels.each do |label_hash|
...@@ -160,7 +160,7 @@ ...@@ -160,7 +160,7 @@
= custom_icon('icon_arrow_right') = custom_icon('icon_arrow_right')
.dropdown.sidebar-move-issue-dropdown.hide-collapsed .dropdown.sidebar-move-issue-dropdown.hide-collapsed
%button.btn.btn-default.btn-block.js-sidebar-dropdown-toggle.js-move-issue{ type: 'button', %button.btn.btn-default.btn-block.js-sidebar-dropdown-toggle.js-move-issue{ type: 'button',
data: { toggle: 'dropdown', display: 'static' } } data: { toggle: 'dropdown', display: 'static', track_label: "right_sidebar", track_property: "move_issue", track_event: "click_button", track_value: "" } }
= _('Move issue') = _('Move issue')
.dropdown-menu.dropdown-menu-selectable.dropdown-extended-height .dropdown-menu.dropdown-menu-selectable.dropdown-extended-height
= dropdown_title(_('Move issue')) = dropdown_title(_('Move issue'))
......
import Tracking from '~/tracking';
export const initSidebarTracking = () => {
new Tracking().bind('.js-issuable-sidebar');
};
export const trackEvent = (eventType, property, value = '') => {
Tracking.event(document.body.dataset.page, eventType, {
label: 'right_sidebar',
property,
value,
});
};
...@@ -5,6 +5,7 @@ import eventHub from '~/sidebar/event_hub'; ...@@ -5,6 +5,7 @@ import eventHub from '~/sidebar/event_hub';
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
import icon from '~/vue_shared/components/icon.vue'; import icon from '~/vue_shared/components/icon.vue';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import { trackEvent } from 'ee/event_tracking/issue_sidebar';
export default { export default {
components: { components: {
...@@ -101,6 +102,11 @@ export default { ...@@ -101,6 +102,11 @@ export default {
checkIfNoValue(weight) { checkIfNoValue(weight) {
return weight === undefined || weight === null || weight === this.weightNoneValue; return weight === undefined || weight === null || weight === this.weightNoneValue;
}, },
onEditClick(shouldShowEditField = true) {
this.showEditField(shouldShowEditField);
trackEvent('click_edit_button', 'weight');
},
showEditField(bool = true) { showEditField(bool = true) {
this.shouldShowEditField = bool; this.shouldShowEditField = bool;
...@@ -160,7 +166,7 @@ export default { ...@@ -160,7 +166,7 @@ export default {
v-if="editable" v-if="editable"
class="float-right js-weight-edit-link" class="float-right js-weight-edit-link"
href="#" href="#"
@click="showEditField(!shouldShowEditField)" @click="onEditClick(!shouldShowEditField)"
> >
{{ __('Edit') }} {{ __('Edit') }}
</a> </a>
......
...@@ -11,8 +11,10 @@ const DEFAULT_PROPS = { ...@@ -11,8 +11,10 @@ const DEFAULT_PROPS = {
describe('Weight', function() { describe('Weight', function() {
let vm; let vm;
let Weight; let Weight;
let statsSpy;
beforeEach(() => { beforeEach(() => {
statsSpy = spyOnDependency(weight, 'trackEvent');
Weight = Vue.extend(weight); Weight = Vue.extend(weight);
}); });
...@@ -109,6 +111,22 @@ describe('Weight', function() { ...@@ -109,6 +111,22 @@ describe('Weight', function() {
.catch(done.fail); .catch(done.fail);
}); });
it('calls trackEvent when "Edit" is clicked', done => {
vm = mountComponent(Weight, {
...DEFAULT_PROPS,
editable: true,
});
vm.$el.querySelector('.js-weight-edit-link').click();
vm.$nextTick()
.then(() => {
expect(statsSpy).toHaveBeenCalled();
})
.then(done)
.catch(done.fail);
});
it('emits event on input submission', done => { it('emits event on input submission', done => {
const ID = 123; const ID = 123;
const expectedWeightValue = '3'; const expectedWeightValue = '3';
......
...@@ -4,8 +4,10 @@ import AssigneeTitle from '~/sidebar/components/assignees/assignee_title.vue'; ...@@ -4,8 +4,10 @@ import AssigneeTitle from '~/sidebar/components/assignees/assignee_title.vue';
describe('AssigneeTitle component', () => { describe('AssigneeTitle component', () => {
let component; let component;
let AssigneeTitleComponent; let AssigneeTitleComponent;
let statsSpy;
beforeEach(() => { beforeEach(() => {
statsSpy = spyOnDependency(AssigneeTitle, 'trackEvent');
AssigneeTitleComponent = Vue.extend(AssigneeTitle); AssigneeTitleComponent = Vue.extend(AssigneeTitle);
}); });
...@@ -102,4 +104,16 @@ describe('AssigneeTitle component', () => { ...@@ -102,4 +104,16 @@ describe('AssigneeTitle component', () => {
expect(component.$el.querySelector('.edit-link')).not.toBeNull(); expect(component.$el.querySelector('.edit-link')).not.toBeNull();
}); });
it('calls trackEvent when edit is clicked', () => {
component = new AssigneeTitleComponent({
propsData: {
numberOfAssignees: 0,
editable: true,
},
}).$mount();
component.$el.querySelector('.js-sidebar-dropdown-toggle').click();
expect(statsSpy).toHaveBeenCalled();
});
}); });
...@@ -4,8 +4,10 @@ import confidentialIssueSidebar from '~/sidebar/components/confidential/confiden ...@@ -4,8 +4,10 @@ import confidentialIssueSidebar from '~/sidebar/components/confidential/confiden
describe('Confidential Issue Sidebar Block', () => { describe('Confidential Issue Sidebar Block', () => {
let vm1; let vm1;
let vm2; let vm2;
let statsSpy;
beforeEach(() => { beforeEach(() => {
statsSpy = spyOnDependency(confidentialIssueSidebar, 'trackEvent');
const Component = Vue.extend(confidentialIssueSidebar); const Component = Vue.extend(confidentialIssueSidebar);
const service = { const service = {
update: () => Promise.resolve(true), update: () => Promise.resolve(true),
...@@ -67,4 +69,10 @@ describe('Confidential Issue Sidebar Block', () => { ...@@ -67,4 +69,10 @@ describe('Confidential Issue Sidebar Block', () => {
done(); done();
}); });
}); });
it('calls trackEvent when "Edit" is clicked', () => {
vm1.$el.querySelector('.confidential-edit').click();
expect(statsSpy).toHaveBeenCalled();
});
}); });
...@@ -4,8 +4,10 @@ import lockIssueSidebar from '~/sidebar/components/lock/lock_issue_sidebar.vue'; ...@@ -4,8 +4,10 @@ import lockIssueSidebar from '~/sidebar/components/lock/lock_issue_sidebar.vue';
describe('LockIssueSidebar', () => { describe('LockIssueSidebar', () => {
let vm1; let vm1;
let vm2; let vm2;
let statsSpy;
beforeEach(() => { beforeEach(() => {
statsSpy = spyOnDependency(lockIssueSidebar, 'trackEvent');
const Component = Vue.extend(lockIssueSidebar); const Component = Vue.extend(lockIssueSidebar);
const mediator = { const mediator = {
...@@ -59,6 +61,12 @@ describe('LockIssueSidebar', () => { ...@@ -59,6 +61,12 @@ describe('LockIssueSidebar', () => {
}); });
}); });
it('calls trackEvent when "Edit" is clicked', () => {
vm1.$el.querySelector('.lock-edit').click();
expect(statsSpy).toHaveBeenCalled();
});
it('displays the edit form when opened from collapsed state', done => { it('displays the edit form when opened from collapsed state', done => {
expect(vm1.isLockDialogOpen).toBe(false); expect(vm1.isLockDialogOpen).toBe(false);
......
...@@ -6,8 +6,10 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper'; ...@@ -6,8 +6,10 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Subscriptions', function() { describe('Subscriptions', function() {
let vm; let vm;
let Subscriptions; let Subscriptions;
let statsSpy;
beforeEach(() => { beforeEach(() => {
statsSpy = spyOnDependency(subscriptions, 'trackEvent');
Subscriptions = Vue.extend(subscriptions); Subscriptions = Vue.extend(subscriptions);
}); });
...@@ -58,6 +60,13 @@ describe('Subscriptions', function() { ...@@ -58,6 +60,13 @@ describe('Subscriptions', function() {
expect(vm.$emit).toHaveBeenCalledWith('toggleSubscription', jasmine.any(Object)); expect(vm.$emit).toHaveBeenCalledWith('toggleSubscription', jasmine.any(Object));
}); });
it('calls trackEvent when toggled', () => {
vm = mountComponent(Subscriptions, { subscribed: true });
vm.toggleSubscription();
expect(statsSpy).toHaveBeenCalled();
});
it('onClickCollapsedIcon method emits `toggleSidebar` event on component', () => { it('onClickCollapsedIcon method emits `toggleSidebar` event on component', () => {
vm = mountComponent(Subscriptions, { subscribed: true }); vm = mountComponent(Subscriptions, { subscribed: true });
spyOn(vm, '$emit'); spyOn(vm, '$emit');
......
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