diff options
Diffstat (limited to 'app/assets/javascripts')
16 files changed, 82 insertions, 83 deletions
diff --git a/app/assets/javascripts/event_tracking/issue_sidebar.js b/app/assets/javascripts/event_tracking/issue_sidebar.js deleted file mode 100644 index 6909f82c66f..00000000000 --- a/app/assets/javascripts/event_tracking/issue_sidebar.js +++ /dev/null @@ -1,2 +0,0 @@ -export const initSidebarTracking = () => {}; -export const trackEvent = () => {}; diff --git a/app/assets/javascripts/event_tracking/notes.js b/app/assets/javascripts/event_tracking/notes.js deleted file mode 100644 index 1f70290c397..00000000000 --- a/app/assets/javascripts/event_tracking/notes.js +++ /dev/null @@ -1,2 +0,0 @@ -// Noop function which has a EE counter-part -export default () => {}; diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js index 77080691dcb..c21fba06d42 100644 --- a/app/assets/javascripts/filterable_list.js +++ b/app/assets/javascripts/filterable_list.js @@ -22,6 +22,7 @@ export default class FilterableList { getPagePath() { const action = this.filterForm.getAttribute('action'); + // eslint-disable-next-line no-jquery/no-serialize const params = $(this.filterForm).serialize(); return `${action}${action.indexOf('?') > 0 ? '&' : '?'}${params}`; } diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js index a7746bb3a0b..1c9b94ade8a 100644 --- a/app/assets/javascripts/integrations/integration_settings_form.js +++ b/app/assets/javascripts/integrations/integration_settings_form.js @@ -42,6 +42,7 @@ export default class IntegrationSettingsForm { // and test the service using provided configuration. if (this.$form.get(0).checkValidity() && this.canTestService) { e.preventDefault(); + // eslint-disable-next-line no-jquery/no-serialize this.testSettings(this.$form.serialize()); } } diff --git a/app/assets/javascripts/issue_show/index.js b/app/assets/javascripts/issue_show/index.js index 4bcba72bc23..e170d338408 100644 --- a/app/assets/javascripts/issue_show/index.js +++ b/app/assets/javascripts/issue_show/index.js @@ -1,5 +1,4 @@ import Vue from 'vue'; -import { initSidebarTracking } from 'ee_else_ce/event_tracking/issue_sidebar'; import issuableApp from './components/app.vue'; import { parseIssuableData } from './utils/parse_data'; @@ -9,9 +8,6 @@ export default function initIssueableApp() { components: { issuableApp, }, - mounted() { - initSidebarTracking(); - }, render(createElement) { return createElement('issuable-app', { props: parseIssuableData(), diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 49be11d466d..c19a845eb69 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -314,6 +314,7 @@ document.addEventListener('DOMContentLoaded', () => { const action = `${this.action}${link.search === '' ? '?' : '&'}`; event.preventDefault(); + // eslint-disable-next-line no-jquery/no-serialize visitUrl(`${action}${$(this).serialize()}`); }); diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 9cc56b34c75..ed52eec8b18 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -1461,6 +1461,7 @@ export default class Notes { getFormData($form) { const content = $form.find('.js-note-text').val(); return { + // eslint-disable-next-line no-jquery/no-serialize formData: $form.serialize(), formContent: _.escape(content), formAction: $form.attr('action'), diff --git a/app/assets/javascripts/notes/components/note_actions/reply_button.vue b/app/assets/javascripts/notes/components/note_actions/reply_button.vue index 1aeb07d6608..8bdee759a23 100644 --- a/app/assets/javascripts/notes/components/note_actions/reply_button.vue +++ b/app/assets/javascripts/notes/components/note_actions/reply_button.vue @@ -19,7 +19,9 @@ export default { <gl-button ref="button" v-gl-tooltip - class="note-action-button js-note-action-reply" + class="note-action-button" + data-track-event="click_button" + data-track-label="reply_comment_button" variant="transparent" :title="__('Reply to comment')" @click="$emit('startReplying')" diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js index c70c0e4095c..30372103590 100644 --- a/app/assets/javascripts/notes/index.js +++ b/app/assets/javascripts/notes/index.js @@ -1,5 +1,4 @@ import Vue from 'vue'; -import initNoteStats from 'ee_else_ce/event_tracking/notes'; import notesApp from './components/notes_app.vue'; import initDiscussionFilters from './discussion_filters'; import createStore from './stores'; @@ -39,9 +38,6 @@ document.addEventListener('DOMContentLoaded', () => { notesData: JSON.parse(notesDataset.notesData), }; }, - mounted() { - initNoteStats(); - }, render(createElement) { return createElement('notes-app', { props: { diff --git a/app/assets/javascripts/pages/projects/commit/pipelines/index.js b/app/assets/javascripts/pages/projects/commit/pipelines/index.js index 8cc3cb0a57c..9f08260c3d6 100644 --- a/app/assets/javascripts/pages/projects/commit/pipelines/index.js +++ b/app/assets/javascripts/pages/projects/commit/pipelines/index.js @@ -6,6 +6,7 @@ document.addEventListener('DOMContentLoaded', () => { new MiniPipelineGraph({ container: '.js-commit-pipeline-graph', }).bindEvents(); + // eslint-disable-next-line no-jquery/no-load $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); initPipelines(); }); diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js index 6fc982967eb..5aa4734244e 100644 --- a/app/assets/javascripts/pages/projects/commit/show/index.js +++ b/app/assets/javascripts/pages/projects/commit/show/index.js @@ -21,6 +21,7 @@ document.addEventListener('DOMContentLoaded', () => { }).bindEvents(); initNotes(); initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight + performanceHeight); + // eslint-disable-next-line no-jquery/no-load $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); fetchCommitMergeRequests(); initDiffNotes(); diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue index 63b93a80ead..f4dac38b9e1 100644 --- a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue +++ b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue @@ -1,6 +1,5 @@ <script> import { n__ } from '~/locale'; -import { trackEvent } from 'ee_else_ce/event_tracking/issue_sidebar'; export default { name: 'AssigneeTitle', @@ -30,11 +29,6 @@ export default { return n__('Assignee', `%d Assignees`, assignees); }, }, - methods: { - trackEdit() { - trackEvent('click_edit_button', 'assignee'); - }, - }, }; </script> <template> @@ -45,7 +39,9 @@ export default { v-if="editable" class="js-sidebar-dropdown-toggle edit-link float-right" href="#" - @click.prevent="trackEdit" + data-track-event="click_edit_button" + data-track-label="right_sidebar" + data-track-property="assignee" > {{ __('Edit') }} </a> diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue index 1c75b6148e8..e350264de96 100644 --- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue @@ -5,7 +5,6 @@ import tooltip from '~/vue_shared/directives/tooltip'; import Icon from '~/vue_shared/components/icon.vue'; import eventHub from '~/sidebar/event_hub'; import editForm from './edit_form.vue'; -import { trackEvent } from 'ee_else_ce/event_tracking/issue_sidebar'; export default { components: { @@ -52,11 +51,6 @@ export default { toggleForm() { this.edit = !this.edit; }, - onEditClick() { - this.toggleForm(); - - trackEvent('click_edit_button', 'confidentiality'); - }, updateConfidentialAttribute(confidential) { this.service .update('issue', { confidential }) @@ -88,7 +82,10 @@ export default { v-if="isEditable" class="float-right confidential-edit" href="#" - @click.prevent="onEditClick" + data-track-event="click_edit_button" + data-track-label="right_sidebar" + data-track-property="confidentiality" + @click.prevent="toggleForm" > {{ __('Edit') }} </a> diff --git a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue index ec2a7b93a98..c7c5e0e20f1 100644 --- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue @@ -6,7 +6,6 @@ import issuableMixin from '~/vue_shared/mixins/issuable'; import Icon from '~/vue_shared/components/icon.vue'; import eventHub from '~/sidebar/event_hub'; import editForm from './edit_form.vue'; -import { trackEvent } from 'ee_else_ce/event_tracking/issue_sidebar'; export default { components: { @@ -66,11 +65,6 @@ export default { toggleForm() { this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen; }, - onEditClick() { - this.toggleForm(); - - trackEvent('click_edit_button', 'lock_issue'); - }, updateLockedAttribute(locked) { this.mediator.service .update(this.issuableType, { @@ -114,7 +108,10 @@ export default { v-if="isEditable" class="float-right lock-edit" type="button" - @click.prevent="onEditClick" + data-track-event="click_edit_button" + data-track-label="right_sidebar" + data-track-property="lock_issue" + @click.prevent="toggleForm" > {{ __('Edit') }} </button> diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue index 1f5f19d1931..ea5edb3ce3f 100644 --- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue +++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue @@ -1,10 +1,10 @@ <script> import { __ } from '~/locale'; +import Tracking from '~/tracking'; import icon from '~/vue_shared/components/icon.vue'; import toggleButton from '~/vue_shared/components/toggle_button.vue'; import tooltip from '~/vue_shared/directives/tooltip'; import eventHub from '../../event_hub'; -import { trackEvent } from 'ee_else_ce/event_tracking/issue_sidebar'; const ICON_ON = 'notifications'; const ICON_OFF = 'notifications-off'; @@ -19,6 +19,7 @@ export default { icon, toggleButton, }, + mixins: [Tracking.mixin({ label: 'right_sidebar' })], props: { loading: { type: Boolean, @@ -65,7 +66,10 @@ export default { // Component event emission. this.$emit('toggleSubscription', this.id); - trackEvent('toggle_button', 'notifications', this.subscribed ? 0 : 1); + this.track('toggle_button', { + property: 'notifications', + value: this.subscribed ? 0 : 1, + }); }, onClickCollapsedIcon() { this.$emit('toggleSidebar'); diff --git a/app/assets/javascripts/tracking.js b/app/assets/javascripts/tracking.js index 1b4ca1d5741..7c0097fbe37 100644 --- a/app/assets/javascripts/tracking.js +++ b/app/assets/javascripts/tracking.js @@ -1,4 +1,4 @@ -import $ from 'jquery'; +import _ from 'underscore'; const DEFAULT_SNOWPLOW_OPTIONS = { namespace: 'gl', @@ -14,18 +14,31 @@ const DEFAULT_SNOWPLOW_OPTIONS = { linkClickTracking: false, }; -const extractData = (el, opts = {}) => { - const { trackEvent, trackLabel = '', trackProperty = '' } = el.dataset; - let trackValue = el.dataset.trackValue || el.value || ''; - if (el.type === 'checkbox' && !el.checked) trackValue = false; - return [ - trackEvent + (opts.suffix || ''), - { - label: trackLabel, - property: trackProperty, - value: trackValue, - }, - ]; +const eventHandler = (e, func, opts = {}) => { + const el = e.target.closest('[data-track-event]'); + const action = el && el.dataset.trackEvent; + if (!action) return; + + let value = el.dataset.trackValue || el.value || undefined; + if (el.type === 'checkbox' && !el.checked) value = false; + + const data = { + label: el.dataset.trackLabel, + property: el.dataset.trackProperty, + value, + context: el.dataset.trackContext, + }; + + func(opts.category, action + (opts.suffix || ''), _.omit(data, _.isUndefined)); +}; + +const eventHandlers = (category, func) => { + const handler = opts => e => eventHandler(e, func, { ...{ category }, ...opts }); + const handlers = []; + handlers.push({ name: 'click', func: handler() }); + handlers.push({ name: 'show.bs.dropdown', func: handler({ suffix: '_show' }) }); + handlers.push({ name: 'hide.bs.dropdown', func: handler({ suffix: '_hide' }) }); + return handlers; }; export default class Tracking { @@ -39,49 +52,43 @@ export default class Tracking { return typeof window.snowplow === 'function' && this.trackable(); } - static event(category = document.body.dataset.page, event = 'generic', data = {}) { + static event(category = document.body.dataset.page, action = 'generic', data = {}) { if (!this.enabled()) return false; // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings if (!category) throw new Error('Tracking: no category provided for tracking.'); - return window.snowplow( - 'trackStructEvent', - category, - event, - Object.assign({}, { label: '', property: '', value: '' }, data), - ); + const { label, property, value, context } = data; + const contexts = context ? [context] : undefined; + return window.snowplow('trackStructEvent', category, action, label, property, value, contexts); } - constructor(category = document.body.dataset.page) { - this.category = category; - } - - bind(container = document) { - if (!this.constructor.enabled()) return; - container.querySelectorAll(`[data-track-event]`).forEach(el => { - if (this.customHandlingFor(el)) return; - // jquery is required for select2, so we use it always - // see: https://github.com/select2/select2/issues/4686 - $(el).on('click', this.eventHandler(this.category)); - }); - } + static bindDocument(category = document.body.dataset.page, documentOverride = null) { + const el = documentOverride || document; + if (!this.enabled() || el.trackingBound) return []; - customHandlingFor(el) { - const classes = el.classList; + el.trackingBound = true; - // bootstrap dropdowns - if (classes.contains('dropdown')) { - $(el).on('show.bs.dropdown', this.eventHandler(this.category, { suffix: '_show' })); - $(el).on('hide.bs.dropdown', this.eventHandler(this.category, { suffix: '_hide' })); - return true; - } - - return false; + const handlers = eventHandlers(category, (...args) => this.event(...args)); + handlers.forEach(event => el.addEventListener(event.name, event.func)); + return handlers; } - eventHandler(category = null, opts = {}) { - return e => { - this.constructor.event(category || this.category, ...extractData(e.currentTarget, opts)); + static mixin(opts) { + return { + data() { + return { + tracking: { + // eslint-disable-next-line no-underscore-dangle + category: this.$options.name || this.$options._componentTag, + }, + }; + }, + methods: { + track(action, data) { + const category = opts.category || data.category || this.tracking.category; + Tracking.event(category || 'unspecified', action, { ...opts, ...this.tracking, ...data }); + }, + }, }; } } @@ -89,7 +96,7 @@ export default class Tracking { export function initUserTracking() { if (!Tracking.enabled()) return; - const opts = Object.assign({}, DEFAULT_SNOWPLOW_OPTIONS, window.snowplowOptions); + const opts = { ...DEFAULT_SNOWPLOW_OPTIONS, ...window.snowplowOptions }; window.snowplow('newTracker', opts.namespace, opts.hostname, opts); window.snowplow('enableActivityTracking', 30, 30); @@ -97,4 +104,6 @@ export function initUserTracking() { if (opts.formTracking) window.snowplow('enableFormTracking'); if (opts.linkClickTracking) window.snowplow('enableLinkClickTracking'); + + Tracking.bindDocument(); } |