Commit 56ad5d8c authored by Kushal Pandya's avatar Kushal Pandya

Merge branch...

Merge branch '333602-trial-status-use-a-constants-js-file-for-magic-strings-shared-data-between-widget-and-popover' into 'master'

Use a constants.js file for magic strings & shared data

See merge request gitlab-org/gitlab!64049
parents 03c17eaa 600ca478
import { n__, s__ } from '~/locale';
const CLICK_BUTTON_ACTION = 'click_button';
const RESIZE_EVENT_DEBOUNCE_MS = 150;
export const RESIZE_EVENT = 'resize';
export const TRACKING_PROPERTY = 'experiment:show_trial_status_in_sidebar';
export const WIDGET = {
i18n: {
widgetTitle: {
countableTranslator(count) {
return n__(
'Trials|%{planName} Trial %{enDash} %{num} day left',
'Trials|%{planName} Trial %{enDash} %{num} days left',
count,
);
},
},
},
trackingEvents: {
widgetClick: { action: 'click_link', label: 'trial_status_widget' },
},
};
export const POPOVER = {
i18n: {
compareAllButtonTitle: s__('Trials|Compare all plans'),
popoverTitle: s__('Trials|Hey there'),
popoverContent: s__(`Trials|Your trial ends on
%{boldStart}%{trialEndDate}%{boldEnd}. We hope you’re enjoying the
features of GitLab %{planName}. To keep those features after your trial
ends, you’ll need to buy a subscription. (You can also choose GitLab
Premium if it meets your needs.)`),
upgradeButtonTitle: s__('Trials|Upgrade %{groupName} to %{planName}'),
},
trackingEvents: {
popoverShown: { action: 'popover_shown', label: 'trial_status_popover' },
upgradeBtnClick: { action: CLICK_BUTTON_ACTION, label: 'upgrade_to_ultimate' },
compareBtnClick: { action: CLICK_BUTTON_ACTION, label: 'compare_all_plans' },
},
resizeEventDebounceMS: RESIZE_EVENT_DEBOUNCE_MS,
disabledBreakpoints: ['xs', 'sm'],
trialEndDateFormatString: 'mmmm d',
};
...@@ -3,23 +3,26 @@ import { GlButton, GlPopover, GlSprintf } from '@gitlab/ui'; ...@@ -3,23 +3,26 @@ import { GlButton, GlPopover, GlSprintf } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import { s__ } from '~/locale'; import { sprintf } from '~/locale';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import { POPOVER, RESIZE_EVENT, TRACKING_PROPERTY } from './constants';
const RESIZE_EVENT_DEBOUNCE_MS = 150; const {
i18n,
trackingEvents,
trialEndDateFormatString,
resizeEventDebounceMS,
disabledBreakpoints,
} = POPOVER;
const trackingMixin = Tracking.mixin({ property: TRACKING_PROPERTY });
export default { export default {
tracking: {
event: 'click_button',
labels: { upgrade: 'upgrade_to_ultimate', compare: 'compare_all_plans' },
property: 'experiment:show_trial_status_in_sidebar',
},
components: { components: {
GlButton, GlButton,
GlPopover, GlPopover,
GlSprintf, GlSprintf,
}, },
mixins: [Tracking.mixin()], mixins: [trackingMixin],
props: { props: {
containerId: { containerId: {
type: [String, null], type: [String, null],
...@@ -56,43 +59,47 @@ export default { ...@@ -56,43 +59,47 @@ export default {
disabled: false, disabled: false,
}; };
}, },
i18n: { i18n,
compareAllButtonTitle: s__('Trials|Compare all plans'), trackingEvents,
popoverTitle: s__('Trials|Hey there'),
popoverContent: s__(`Trials|Your trial ends on
%{boldStart}%{trialEndDate}%{boldEnd}. We hope you’re enjoying the
features of GitLab %{planName}. To keep those features after your trial
ends, you’ll need to buy a subscription. (You can also choose GitLab
Premium if it meets your needs.)`),
upgradeButtonTitle: s__('Trials|Upgrade %{groupName} to %{planName}'),
},
computed: { computed: {
formattedTrialEndDate() { formattedTrialEndDate() {
return formatDate(this.trialEndDate, 'mmmm d'); return formatDate(this.trialEndDate, trialEndDateFormatString);
},
upgradeButtonTitle() {
return sprintf(this.$options.i18n.upgradeButtonTitle, {
groupName: this.groupName,
planName: this.planName,
});
}, },
}, },
created() { created() {
this.debouncedResize = debounce(() => this.onResize(), RESIZE_EVENT_DEBOUNCE_MS); this.debouncedResize = debounce(() => this.onResize(), resizeEventDebounceMS);
window.addEventListener('resize', this.debouncedResize); window.addEventListener(RESIZE_EVENT, this.debouncedResize);
}, },
mounted() { mounted() {
this.onResize(); this.onResize();
}, },
beforeDestroy() { beforeDestroy() {
window.removeEventListener('resize', this.debouncedResize); window.removeEventListener(RESIZE_EVENT, this.debouncedResize);
}, },
methods: { methods: {
onResize() { onResize() {
this.updateDisabledState(); this.updateDisabledState();
}, },
onShown() { onShown() {
this.track('popover_shown', { const { action, ...options } = this.$options.trackingEvents.popoverShown;
label: 'trial_status_popover', this.track(action, options);
property: 'experiment:show_trial_status_in_sidebar', },
}); onUpgradeBtnClick() {
const { action, ...options } = this.$options.trackingEvents.upgradeBtnClick;
this.track(action, options);
},
onCompareBtnClick() {
const { action, ...options } = this.$options.trackingEvents.compareBtnClick;
this.track(action, options);
}, },
updateDisabledState() { updateDisabledState() {
this.disabled = ['xs', 'sm'].includes(bp.getBreakpointSize()); this.disabled = disabledBreakpoints.includes(bp.getBreakpointSize());
}, },
}, },
}; };
...@@ -129,17 +136,11 @@ export default { ...@@ -129,17 +136,11 @@ export default {
class="gl-mb-0" class="gl-mb-0"
block block
data-testid="upgradeBtn" data-testid="upgradeBtn"
:data-track-event="$options.tracking.event" @click="onUpgradeBtnClick"
:data-track-label="$options.tracking.labels.upgrade"
:data-track-property="$options.tracking.property"
> >
<span class="gl-font-sm"> <span class="gl-font-sm">{{ upgradeButtonTitle }}</span>
<gl-sprintf :message="$options.i18n.upgradeButtonTitle">
<template #groupName>{{ groupName }}</template>
<template #planName>{{ planName }}</template>
</gl-sprintf>
</span>
</gl-button> </gl-button>
<gl-button <gl-button
:href="plansHref" :href="plansHref"
category="secondary" category="secondary"
...@@ -149,9 +150,7 @@ export default { ...@@ -149,9 +150,7 @@ export default {
block block
data-testid="compareBtn" data-testid="compareBtn"
:title="$options.i18n.compareAllButtonTitle" :title="$options.i18n.compareAllButtonTitle"
:data-track-event="$options.tracking.event" @click="onCompareBtnClick"
:data-track-label="$options.tracking.labels.compare"
:data-track-property="$options.tracking.property"
> >
<span class="gl-font-sm">{{ $options.i18n.compareAllButtonTitle }}</span> <span class="gl-font-sm">{{ $options.i18n.compareAllButtonTitle }}</span>
</gl-button> </gl-button>
......
<script> <script>
import { GlLink, GlProgressBar } from '@gitlab/ui'; import { GlLink, GlProgressBar } from '@gitlab/ui';
import { n__, sprintf } from '~/locale'; import { sprintf } from '~/locale';
import Tracking from '~/tracking';
import { TRACKING_PROPERTY, WIDGET } from './constants';
const { i18n, trackingEvents } = WIDGET;
const trackingMixin = Tracking.mixin({ property: TRACKING_PROPERTY });
export default { export default {
tracking: {
event: 'click_link',
label: 'trial_status_widget',
property: 'experiment:show_trial_status_in_sidebar',
},
components: { components: {
GlLink, GlLink,
GlProgressBar, GlProgressBar,
}, },
mixins: [trackingMixin],
props: { props: {
containerId: { containerId: {
type: [String, null], type: [String, null],
...@@ -39,11 +40,11 @@ export default { ...@@ -39,11 +40,11 @@ export default {
required: true, required: true,
}, },
}, },
i18n,
trackingEvents,
computed: { computed: {
widgetTitle() { widgetTitle() {
const i18nWidgetTitle = n__( const i18nWidgetTitle = this.$options.i18n.widgetTitle.countableTranslator(
'Trials|%{planName} Trial %{enDash} %{num} day left',
'Trials|%{planName} Trial %{enDash} %{num} days left',
this.daysRemaining, this.daysRemaining,
); );
...@@ -54,18 +55,17 @@ export default { ...@@ -54,18 +55,17 @@ export default {
}); });
}, },
}, },
methods: {
onWidgetClick() {
const { action, ...options } = this.$options.trackingEvents.widgetClick;
this.track(action, options);
},
},
}; };
</script> </script>
<template> <template>
<gl-link <gl-link :id="containerId" :title="widgetTitle" :href="plansHref" @click="onWidgetClick">
:id="containerId"
:title="widgetTitle"
:href="plansHref"
:data-track-event="$options.tracking.event"
:data-track-label="$options.tracking.label"
:data-track-property="$options.tracking.property"
>
<div class="gl-display-flex gl-flex-direction-column gl-align-items-stretch gl-w-full"> <div class="gl-display-flex gl-flex-direction-column gl-align-items-stretch gl-w-full">
<span class="gl-display-flex gl-align-items-center"> <span class="gl-display-flex gl-align-items-center">
<span class="nav-icon-container svg-container"> <span class="nav-icon-container svg-container">
......
...@@ -22,9 +22,6 @@ exports[`TrialStatusPopover component matches the snapshot 1`] = ` ...@@ -22,9 +22,6 @@ exports[`TrialStatusPopover component matches the snapshot 1`] = `
category="primary" category="primary"
class="gl-mb-0" class="gl-mb-0"
data-testid="upgradeBtn" data-testid="upgradeBtn"
data-track-event="click_button"
data-track-label="upgrade_to_ultimate"
data-track-property="experiment:show_trial_status_in_sidebar"
href="transactions/new" href="transactions/new"
icon="" icon=""
size="small" size="small"
...@@ -33,9 +30,7 @@ exports[`TrialStatusPopover component matches the snapshot 1`] = ` ...@@ -33,9 +30,7 @@ exports[`TrialStatusPopover component matches the snapshot 1`] = `
<span <span
class="gl-font-sm" class="gl-font-sm"
> >
<gl-sprintf-stub Upgrade Some Test Group to Ultimate
message="Upgrade %{groupName} to %{planName}"
/>
</span> </span>
</gl-button-stub> </gl-button-stub>
...@@ -45,9 +40,6 @@ exports[`TrialStatusPopover component matches the snapshot 1`] = ` ...@@ -45,9 +40,6 @@ exports[`TrialStatusPopover component matches the snapshot 1`] = `
category="secondary" category="secondary"
class="gl-mb-0" class="gl-mb-0"
data-testid="compareBtn" data-testid="compareBtn"
data-track-event="click_button"
data-track-label="compare_all_plans"
data-track-property="experiment:show_trial_status_in_sidebar"
href="billing/path-for/group" href="billing/path-for/group"
icon="" icon=""
size="small" size="small"
......
...@@ -2,9 +2,6 @@ ...@@ -2,9 +2,6 @@
exports[`TrialStatusWidget component without the optional containerId prop matches the snapshot 1`] = ` exports[`TrialStatusWidget component without the optional containerId prop matches the snapshot 1`] = `
<gl-link-stub <gl-link-stub
data-track-event="click_link"
data-track-label="trial_status_widget"
data-track-property="experiment:show_trial_status_in_sidebar"
href="billing/path-for/group" href="billing/path-for/group"
title="Ultimate Trial – 20 days left" title="Ultimate Trial – 20 days left"
> >
......
import { GlPopover } from '@gitlab/ui'; import { GlPopover } from '@gitlab/ui';
import { GlBreakpointInstance } from '@gitlab/ui/dist/utils'; import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
import { shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import { POPOVER, TRACKING_PROPERTY } from 'ee/contextual_sidebar/components/constants';
import TrialStatusPopover from 'ee/contextual_sidebar/components/trial_status_popover.vue'; import TrialStatusPopover from 'ee/contextual_sidebar/components/trial_status_popover.vue';
import { mockTracking } from 'helpers/tracking_helper'; import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
Vue.config.ignoredElements = ['gl-emoji'];
describe('TrialStatusPopover component', () => { describe('TrialStatusPopover component', () => {
let wrapper; let wrapper;
let trackingSpy; let trackingSpy;
const { trackingEvents } = POPOVER;
const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`); const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`);
const findGlPopover = () => wrapper.findComponent(GlPopover); const findGlPopover = () => wrapper.findComponent(GlPopover);
const createComponent = () => { const expectTracking = ({ action, ...options } = {}) => {
return shallowMount(TrialStatusPopover, { return expect(trackingSpy).toHaveBeenCalledWith(undefined, action, {
...options,
property: TRACKING_PROPERTY,
});
};
const createComponent = (mountFn = shallowMount) => {
return mountFn(TrialStatusPopover, {
propsData: { propsData: {
groupName: 'Some Test Group', groupName: 'Some Test Group',
planName: 'Ultimate', planName: 'Ultimate',
...@@ -32,25 +44,31 @@ describe('TrialStatusPopover component', () => { ...@@ -32,25 +44,31 @@ describe('TrialStatusPopover component', () => {
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
wrapper = null; unmockTracking();
});
describe('interpolated strings', () => {
it('correctly interpolates them all', () => {
wrapper = createComponent(mount);
expect(wrapper.text()).not.toMatch(/%{\w+}/);
});
}); });
it('matches the snapshot', () => { it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot(); expect(wrapper.element).toMatchSnapshot();
}); });
it('renders the upgrade button with correct tracking data attrs', () => { it('tracks when the upgrade button is clicked', () => {
const attrs = findByTestId('upgradeBtn').attributes(); findByTestId('upgradeBtn').vm.$emit('click');
expect(attrs['data-track-event']).toBe('click_button');
expect(attrs['data-track-label']).toBe('upgrade_to_ultimate'); expectTracking(trackingEvents.upgradeBtnClick);
expect(attrs['data-track-property']).toBe('experiment:show_trial_status_in_sidebar');
}); });
it('renders the compare plans button with correct tracking data attrs', () => { it('tracks when the compare button is clicked', () => {
const attrs = findByTestId('compareBtn').attributes(); findByTestId('compareBtn').vm.$emit('click');
expect(attrs['data-track-event']).toBe('click_button');
expect(attrs['data-track-label']).toBe('compare_all_plans'); expectTracking(trackingEvents.compareBtnClick);
expect(attrs['data-track-property']).toBe('experiment:show_trial_status_in_sidebar');
}); });
describe('methods', () => { describe('methods', () => {
...@@ -76,15 +94,10 @@ describe('TrialStatusPopover component', () => { ...@@ -76,15 +94,10 @@ describe('TrialStatusPopover component', () => {
}); });
describe('onShown', () => { describe('onShown', () => {
beforeEach(() => { it('dispatches tracking event', () => {
findGlPopover().vm.$emit('shown'); findGlPopover().vm.$emit('shown');
});
it('dispatches tracking event', () => { expectTracking(trackingEvents.popoverShown);
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'popover_shown', {
label: 'trial_status_popover',
property: 'experiment:show_trial_status_in_sidebar',
});
}); });
}); });
}); });
......
import { GlLink } from '@gitlab/ui'; import { GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { TRACKING_PROPERTY, WIDGET } from 'ee/contextual_sidebar/components/constants';
import TrialStatusWidget from 'ee/contextual_sidebar/components/trial_status_widget.vue'; import TrialStatusWidget from 'ee/contextual_sidebar/components/trial_status_widget.vue';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
describe('TrialStatusWidget component', () => { describe('TrialStatusWidget component', () => {
let wrapper; let wrapper;
const { trackingEvents } = WIDGET;
const findGlLink = () => wrapper.findComponent(GlLink); const findGlLink = () => wrapper.findComponent(GlLink);
const createComponent = (props = {}) => { const createComponent = (props = {}) => {
...@@ -26,6 +29,14 @@ describe('TrialStatusWidget component', () => { ...@@ -26,6 +29,14 @@ describe('TrialStatusWidget component', () => {
wrapper = null; wrapper = null;
}); });
describe('interpolated strings', () => {
it('correctly interpolates them all', () => {
wrapper = createComponent();
expect(wrapper.text()).not.toMatch(/%{\w+}/);
});
});
describe('without the optional containerId prop', () => { describe('without the optional containerId prop', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); wrapper = createComponent();
...@@ -39,11 +50,18 @@ describe('TrialStatusWidget component', () => { ...@@ -39,11 +50,18 @@ describe('TrialStatusWidget component', () => {
expect(findGlLink().attributes('id')).toBe(undefined); expect(findGlLink().attributes('id')).toBe(undefined);
}); });
it('renders with the correct tracking data attributes', () => { it('tracks when the widget is clicked', () => {
const attrs = findGlLink().attributes(); const { action, ...options } = trackingEvents.widgetClick;
expect(attrs['data-track-event']).toBe('click_link'); const trackingSpy = mockTracking(undefined, undefined, jest.spyOn);
expect(attrs['data-track-label']).toBe('trial_status_widget');
expect(attrs['data-track-property']).toBe('experiment:show_trial_status_in_sidebar'); findGlLink().vm.$emit('click');
expect(trackingSpy).toHaveBeenCalledWith(undefined, action, {
...options,
property: TRACKING_PROPERTY,
});
unmockTracking();
}); });
}); });
......
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