Commit 6e1ae968 authored by Martin Wortschack's avatar Martin Wortschack

Merge branch...

Merge branch '37772-add-ui-event-tracking-for-package-details-installation-instructions' into 'master'

Add event tracking to package details installation instructions

Closes #37772

See merge request gitlab-org/gitlab!20967
parents 5d88a5eb c06aab39
---
title: Added event tracking to the package details installation components
merge_request: 20967
author:
type: changed
......@@ -17,7 +17,7 @@ import { numberToHumanSize } from '~/lib/utils/number_utils';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { formatDate } from '~/lib/utils/datetime_utility';
import { __, s__, sprintf } from '~/locale';
import PackageType from '../constants';
import { PackageType } from '../constants';
export default {
name: 'PackagesApp',
......
<script>
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import Tracking from '~/tracking';
import { TrackingLabels } from '../constants';
export default {
name: 'CodeInstruction',
components: {
ClipboardButton,
},
mixins: [
Tracking.mixin({
label: TrackingLabels.CODE_INSTRUCTION,
}),
],
props: {
instruction: {
type: String,
......@@ -20,19 +27,37 @@ export default {
required: false,
default: false,
},
trackingAction: {
type: String,
required: false,
default: '',
},
},
methods: {
trackCopy() {
if (this.trackingAction) {
this.track(this.trackingAction);
}
},
},
};
</script>
<template>
<div v-if="!multiline" class="input-group append-bottom-10">
<input :value="instruction" type="text" class="form-control monospace" readonly />
<span class="input-group-append">
<input
:value="instruction"
type="text"
class="form-control monospace js-instruction-input"
readonly
@copy="trackCopy"
/>
<span class="input-group-append js-instruction-button" @click="trackCopy">
<clipboard-button :text="instruction" :title="copyText" class="input-group-text" />
</span>
</div>
<div v-else>
<pre>{{ instruction }}</pre>
<pre class="js-instruction-pre" @copy="trackCopy">{{ instruction }}</pre>
</div>
</template>
......@@ -2,6 +2,9 @@
import { GlTab, GlTabs } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import CodeInstruction from './code_instruction.vue';
import Tracking from '~/tracking';
import { TrackingActions, TrackingLabels } from '../constants';
import trackInstallationTabChange from '../utils';
export default {
name: 'MavenInstallation',
......@@ -10,6 +13,12 @@ export default {
GlTab,
GlTabs,
},
mixins: [
Tracking.mixin({
label: TrackingLabels.MAVEN_INSTALLATION,
}),
trackInstallationTabChange,
],
props: {
heading: {
type: String,
......@@ -110,13 +119,14 @@ export default {
false,
),
},
trackingActions: { ...TrackingActions },
};
</script>
<template>
<div class="append-bottom-default">
<gl-tabs>
<gl-tab :title="s__('PackageRegistry|Installation')">
<gl-tabs @input="trackInstallationTabChange">
<gl-tab :title="s__('PackageRegistry|Installation')" title-item-class="js-installation-tab">
<div class="prepend-left-default append-right-default">
<p class="prepend-top-8 font-weight-bold">{{ s__('PackageRegistry|Maven XML') }}</p>
<p v-html="$options.i18n.xmlText"></p>
......@@ -125,6 +135,7 @@ export default {
:copy-text="s__('PackageRegistry|Copy Maven XML')"
class="js-maven-xml"
multiline
:tracking-action="$options.trackingActions.COPY_MAVEN_XML"
/>
<p class="prepend-top-default font-weight-bold">
......@@ -134,10 +145,11 @@ export default {
:instruction="mavenCommand"
:copy-text="s__('PackageRegistry|Copy Maven command')"
class="js-maven-command"
:tracking-action="$options.trackingActions.COPY_MAVEN_COMMAND"
/>
</div>
</gl-tab>
<gl-tab :title="s__('PackageRegistry|Registry Setup')">
<gl-tab :title="s__('PackageRegistry|Registry Setup')" title-item-class="js-setup-tab">
<div class="prepend-left-default append-right-default">
<p v-html="$options.i18n.setupText"></p>
<code-instruction
......@@ -145,6 +157,7 @@ export default {
:copy-text="s__('PackageRegistry|Copy Maven registry XML')"
class="js-maven-setup-xml"
multiline
:tracking-action="$options.trackingActions.COPY_MAVEN_SETUP"
/>
<p v-html="helpText"></p>
</div>
......
......@@ -2,6 +2,9 @@
import { GlTab, GlTabs } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import CodeInstruction from './code_instruction.vue';
import Tracking from '~/tracking';
import { TrackingActions, TrackingLabels } from '../constants';
import trackInstallationTabChange from '../utils';
export default {
name: 'NpmInstallation',
......@@ -10,6 +13,12 @@ export default {
GlTab,
GlTabs,
},
mixins: [
Tracking.mixin({
label: TrackingLabels.NPM_INSTALLATION,
}),
trackInstallationTabChange,
],
props: {
name: {
type: String,
......@@ -63,19 +72,21 @@ export default {
);
},
},
trackingActions: { ...TrackingActions },
};
</script>
<template>
<div class="append-bottom-default">
<gl-tabs>
<gl-tab :title="s__('PackageRegistry|Installation')">
<gl-tabs @input="trackInstallationTabChange">
<gl-tab :title="s__('PackageRegistry|Installation')" title-item-class="js-installation-tab">
<div class="prepend-left-default append-right-default">
<p class="prepend-top-8 font-weight-bold">{{ s__('PackageRegistry|npm') }}</p>
<code-instruction
:instruction="npmCommand"
:copy-text="s__('PackageRegistry|Copy npm command')"
class="js-npm-install"
:tracking-action="$options.trackingActions.COPY_NPM_INSTALL_COMMAND"
/>
<p class="prepend-top-default font-weight-bold">{{ s__('PackageRegistry|yarn') }}</p>
......@@ -83,16 +94,18 @@ export default {
:instruction="yarnCommand"
:copy-text="s__('PackageRegistry|Copy yarn command')"
class="js-yarn-install"
:tracking-action="$options.trackingActions.COPY_YARN_INSTALL_COMMAND"
/>
</div>
</gl-tab>
<gl-tab :title="s__('PackageRegistry|Registry Setup')">
<gl-tab :title="s__('PackageRegistry|Registry Setup')" title-item-class="js-setup-tab">
<div class="prepend-left-default append-right-default">
<p class="prepend-top-8 font-weight-bold">{{ s__('PackageRegistry|npm') }}</p>
<code-instruction
:instruction="npmSetupCommand"
:copy-text="s__('PackageRegistry|Copy npm setup command')"
class="js-npm-setup"
:tracking-action="$options.trackingActions.COPY_NPM_SETUP_COMMAND"
/>
<p class="prepend-top-default font-weight-bold">{{ s__('PackageRegistry|yarn') }}</p>
......@@ -100,6 +113,7 @@ export default {
:instruction="yarnSetupCommand"
:copy-text="s__('PackageRegistry|Copy yarn setup command')"
class="js-yarn-setup"
:tracking-action="$options.trackingActions.COPY_YARN_SETUP_COMMAND"
/>
<p v-html="helpText"></p>
......
const PackageType = {
export const PackageType = {
MAVEN: 'maven',
NPM: 'npm',
};
export default PackageType;
export const TrackingLabels = {
CODE_INSTRUCTION: 'code_instruction',
MAVEN_INSTALLATION: 'maven_installation',
NPM_INSTALLATION: 'npm_installation',
};
export const TrackingActions = {
INSTALLATION: 'installation',
REGISTRY_SETUP: 'registry_setup',
COPY_MAVEN_XML: 'copy_maven_xml',
COPY_MAVEN_COMMAND: 'copy_maven_command',
COPY_MAVEN_SETUP: 'copy_maven_setup_xml',
COPY_NPM_INSTALL_COMMAND: 'copy_npm_install_command',
COPY_NPM_SETUP_COMMAND: 'copy_npm_setup_command',
COPY_YARN_INSTALL_COMMAND: 'copy_yarn_install_command',
COPY_YARN_SETUP_COMMAND: 'copy_yarn_setup_command',
};
import { TrackingActions } from './constants';
const trackInstallationTabChange = {
methods: {
trackInstallationTabChange(tabIndex) {
const action = tabIndex === 0 ? TrackingActions.INSTALLATION : TrackingActions.REGISTRY_SETUP;
this.track(action);
},
},
};
export default trackInstallationTabChange;
......@@ -2,7 +2,9 @@
exports[`Package code instruction multiline to match the snapshot 1`] = `
<div>
<pre>
<pre
class="js-instruction-pre"
>
this is some
multiline text
</pre>
......@@ -14,13 +16,13 @@ exports[`Package code instruction single line to match the default snapshot 1`]
class="input-group append-bottom-10"
>
<input
class="form-control monospace"
class="form-control monospace js-instruction-input"
readonly="readonly"
type="text"
/>
<span
class="input-group-append"
class="input-group-append js-instruction-button"
>
<button
class="btn input-group-text btn-secondary btn-default"
......
import { mount } from '@vue/test-utils';
import CodeInstruction from 'ee/packages/details/components/code_instruction.vue';
import { TrackingLabels } from 'ee/packages/details/constants';
import Tracking from '~/tracking';
describe('Package code instruction', () => {
let wrapper;
......@@ -18,6 +20,10 @@ describe('Package code instruction', () => {
});
}
const findInstructionInput = () => wrapper.find('.js-instruction-input');
const findInstructionPre = () => wrapper.find('.js-instruction-pre');
const findInstructionButton = () => wrapper.find('.js-instruction-button');
afterEach(() => {
wrapper.destroy();
});
......@@ -43,4 +49,62 @@ describe('Package code instruction', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
describe('tracking', () => {
let eventSpy;
const trackingAction = 'test_action';
const label = TrackingLabels.CODE_INSTRUCTION;
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
});
it('should not track when no trackingAction is provided', () => {
createComponent();
findInstructionButton().trigger('click');
expect(eventSpy).toHaveBeenCalledTimes(0);
});
describe('when trackingAction is provided for single line', () => {
beforeEach(() =>
createComponent({
trackingAction,
}),
);
it('should track when copying from the input', () => {
findInstructionInput().trigger('copy');
expect(eventSpy).toHaveBeenCalledWith(undefined, trackingAction, {
label,
});
});
it('should track when the copy button is pressed', () => {
findInstructionButton().trigger('click');
expect(eventSpy).toHaveBeenCalledWith(undefined, trackingAction, {
label,
});
});
});
describe('when trackingAction is provided for multiline', () => {
beforeEach(() =>
createComponent({
trackingAction,
multiline: true,
}),
);
it('should track when copying from the multiline pre element', () => {
findInstructionPre().trigger('copy');
expect(eventSpy).toHaveBeenCalledWith(undefined, trackingAction, {
label,
});
});
});
});
});
import { mount } from '@vue/test-utils';
import MavenInstallation from 'ee/packages/details/components/maven_installation.vue';
import { TrackingActions, TrackingLabels } from 'ee/packages/details/constants';
import {
generateMavenCommand,
generateXmlCodeBlock,
......@@ -7,6 +8,7 @@ import {
mavenMetadata,
registryUrl,
} from '../mock_data';
import Tracking from '~/tracking';
describe('MavenInstallation', () => {
let wrapper;
......@@ -21,6 +23,8 @@ describe('MavenInstallation', () => {
const xmlCodeBlock = generateXmlCodeBlock(mavenMetadata);
const mavenSetupXml = generateMavenSetupXml();
const installationTab = () => wrapper.find('.js-installation-tab > a');
const setupTab = () => wrapper.find('.js-setup-tab > a');
const xmlCode = () => wrapper.find('.js-maven-xml > pre');
const mavenCommand = () => wrapper.find('.js-maven-command > input');
const xmlSetup = () => wrapper.find('.js-maven-setup-xml > pre');
......@@ -79,4 +83,30 @@ describe('MavenInstallation', () => {
expect(xmlSetup().text()).toBe(mavenSetupXml);
});
});
describe('tab change tracking', () => {
let eventSpy;
const label = TrackingLabels.MAVEN_INSTALLATION;
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
});
it('should track when the setup tab is clicked', () => {
setupTab().trigger('click');
expect(eventSpy).toHaveBeenCalledWith(undefined, TrackingActions.REGISTRY_SETUP, {
label,
});
});
it('should track when the installation tab is clicked', () => {
setupTab().trigger('click');
installationTab().trigger('click');
expect(eventSpy).toHaveBeenCalledWith(undefined, TrackingActions.INSTALLATION, {
label,
});
});
});
});
import { mount } from '@vue/test-utils';
import NpmInstallation from 'ee/packages/details/components/npm_installation.vue';
import { TrackingActions, TrackingLabels } from 'ee/packages/details/constants';
import Tracking from '~/tracking';
describe('NpmInstallation', () => {
let wrapper;
......@@ -20,6 +22,8 @@ describe('NpmInstallation', () => {
const yarnInstall = `yarn add ${packageScopeName}`;
const yarnSetup = `echo \\"${packageScope}:registry\\" \\"${registryUrl}\\" >> .yarnrc`;
const installationTab = () => wrapper.find('.js-installation-tab > a');
const setupTab = () => wrapper.find('.js-setup-tab > a');
const installCommand = type => wrapper.find(`.js-${type}-install > input`);
const setupCommand = type => wrapper.find(`.js-${type}-setup > input`);
......@@ -73,4 +77,31 @@ describe('NpmInstallation', () => {
expect(setupCommand('yarn').element.value).toBe(yarnSetup);
});
});
describe('tab change tracking', () => {
let eventSpy;
const label = TrackingLabels.NPM_INSTALLATION;
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
createComponent();
});
it('should track when the setup tab is clicked', () => {
setupTab().trigger('click');
expect(eventSpy).toHaveBeenCalledWith(undefined, TrackingActions.REGISTRY_SETUP, {
label,
});
});
it('should track when the installation tab is clicked', () => {
setupTab().trigger('click');
installationTab().trigger('click');
expect(eventSpy).toHaveBeenCalledWith(undefined, TrackingActions.INSTALLATION, {
label,
});
});
});
});
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