Commit 600ca478 authored by Dallas Reedy's avatar Dallas Reedy Committed by Kushal Pandya

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

parent 6f593b85
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';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { debounce } from 'lodash';
import { formatDate } from '~/lib/utils/datetime_utility';
import { s__ } from '~/locale';
import { sprintf } from '~/locale';
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 {
tracking: {
event: 'click_button',
labels: { upgrade: 'upgrade_to_ultimate', compare: 'compare_all_plans' },
property: 'experiment:show_trial_status_in_sidebar',
},
components: {
GlButton,
GlPopover,
GlSprintf,
},
mixins: [Tracking.mixin()],
mixins: [trackingMixin],
props: {
containerId: {
type: [String, null],
......@@ -56,43 +59,47 @@ export default {
disabled: false,
};
},
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}'),
},
i18n,
trackingEvents,
computed: {
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() {
this.debouncedResize = debounce(() => this.onResize(), RESIZE_EVENT_DEBOUNCE_MS);
window.addEventListener('resize', this.debouncedResize);
this.debouncedResize = debounce(() => this.onResize(), resizeEventDebounceMS);
window.addEventListener(RESIZE_EVENT, this.debouncedResize);
},
mounted() {
this.onResize();
},
beforeDestroy() {
window.removeEventListener('resize', this.debouncedResize);
window.removeEventListener(RESIZE_EVENT, this.debouncedResize);
},
methods: {
onResize() {
this.updateDisabledState();
},
onShown() {
this.track('popover_shown', {
label: 'trial_status_popover',
property: 'experiment:show_trial_status_in_sidebar',
});
const { action, ...options } = this.$options.trackingEvents.popoverShown;
this.track(action, options);
},
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() {
this.disabled = ['xs', 'sm'].includes(bp.getBreakpointSize());
this.disabled = disabledBreakpoints.includes(bp.getBreakpointSize());
},
},
};
......@@ -129,17 +136,11 @@ export default {
class="gl-mb-0"
block
data-testid="upgradeBtn"
:data-track-event="$options.tracking.event"
:data-track-label="$options.tracking.labels.upgrade"
:data-track-property="$options.tracking.property"
@click="onUpgradeBtnClick"
>
<span class="gl-font-sm">
<gl-sprintf :message="$options.i18n.upgradeButtonTitle">
<template #groupName>{{ groupName }}</template>
<template #planName>{{ planName }}</template>
</gl-sprintf>
</span>
<span class="gl-font-sm">{{ upgradeButtonTitle }}</span>
</gl-button>
<gl-button
:href="plansHref"
category="secondary"
......@@ -149,9 +150,7 @@ export default {
block
data-testid="compareBtn"
:title="$options.i18n.compareAllButtonTitle"
:data-track-event="$options.tracking.event"
:data-track-label="$options.tracking.labels.compare"
:data-track-property="$options.tracking.property"
@click="onCompareBtnClick"
>
<span class="gl-font-sm">{{ $options.i18n.compareAllButtonTitle }}</span>
</gl-button>
......
<script>
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 {
tracking: {
event: 'click_link',
label: 'trial_status_widget',
property: 'experiment:show_trial_status_in_sidebar',
},
components: {
GlLink,
GlProgressBar,
},
mixins: [trackingMixin],
props: {
containerId: {
type: [String, null],
......@@ -39,11 +40,11 @@ export default {
required: true,
},
},
i18n,
trackingEvents,
computed: {
widgetTitle() {
const i18nWidgetTitle = n__(
'Trials|%{planName} Trial %{enDash} %{num} day left',
'Trials|%{planName} Trial %{enDash} %{num} days left',
const i18nWidgetTitle = this.$options.i18n.widgetTitle.countableTranslator(
this.daysRemaining,
);
......@@ -54,18 +55,17 @@ export default {
});
},
},
methods: {
onWidgetClick() {
const { action, ...options } = this.$options.trackingEvents.widgetClick;
this.track(action, options);
},
},
};
</script>
<template>
<gl-link
:id="containerId"
:title="widgetTitle"
:href="plansHref"
:data-track-event="$options.tracking.event"
:data-track-label="$options.tracking.label"
:data-track-property="$options.tracking.property"
>
<gl-link :id="containerId" :title="widgetTitle" :href="plansHref" @click="onWidgetClick">
<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="nav-icon-container svg-container">
......
......@@ -22,9 +22,6 @@ exports[`TrialStatusPopover component matches the snapshot 1`] = `
category="primary"
class="gl-mb-0"
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"
icon=""
size="small"
......@@ -33,9 +30,7 @@ exports[`TrialStatusPopover component matches the snapshot 1`] = `
<span
class="gl-font-sm"
>
<gl-sprintf-stub
message="Upgrade %{groupName} to %{planName}"
/>
Upgrade Some Test Group to Ultimate
</span>
</gl-button-stub>
......@@ -45,9 +40,6 @@ exports[`TrialStatusPopover component matches the snapshot 1`] = `
category="secondary"
class="gl-mb-0"
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"
icon=""
size="small"
......
......@@ -2,9 +2,6 @@
exports[`TrialStatusWidget component without the optional containerId prop matches the snapshot 1`] = `
<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"
title="Ultimate Trial – 20 days left"
>
......
import { GlPopover } from '@gitlab/ui';
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 { mockTracking } from 'helpers/tracking_helper';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
Vue.config.ignoredElements = ['gl-emoji'];
describe('TrialStatusPopover component', () => {
let wrapper;
let trackingSpy;
const { trackingEvents } = POPOVER;
const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`);
const findGlPopover = () => wrapper.findComponent(GlPopover);
const createComponent = () => {
return shallowMount(TrialStatusPopover, {
const expectTracking = ({ action, ...options } = {}) => {
return expect(trackingSpy).toHaveBeenCalledWith(undefined, action, {
...options,
property: TRACKING_PROPERTY,
});
};
const createComponent = (mountFn = shallowMount) => {
return mountFn(TrialStatusPopover, {
propsData: {
groupName: 'Some Test Group',
planName: 'Ultimate',
......@@ -32,25 +44,31 @@ describe('TrialStatusPopover component', () => {
afterEach(() => {
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', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('renders the upgrade button with correct tracking data attrs', () => {
const attrs = findByTestId('upgradeBtn').attributes();
expect(attrs['data-track-event']).toBe('click_button');
expect(attrs['data-track-label']).toBe('upgrade_to_ultimate');
expect(attrs['data-track-property']).toBe('experiment:show_trial_status_in_sidebar');
it('tracks when the upgrade button is clicked', () => {
findByTestId('upgradeBtn').vm.$emit('click');
expectTracking(trackingEvents.upgradeBtnClick);
});
it('renders the compare plans button with correct tracking data attrs', () => {
const attrs = findByTestId('compareBtn').attributes();
expect(attrs['data-track-event']).toBe('click_button');
expect(attrs['data-track-label']).toBe('compare_all_plans');
expect(attrs['data-track-property']).toBe('experiment:show_trial_status_in_sidebar');
it('tracks when the compare button is clicked', () => {
findByTestId('compareBtn').vm.$emit('click');
expectTracking(trackingEvents.compareBtnClick);
});
describe('methods', () => {
......@@ -76,15 +94,10 @@ describe('TrialStatusPopover component', () => {
});
describe('onShown', () => {
beforeEach(() => {
it('dispatches tracking event', () => {
findGlPopover().vm.$emit('shown');
});
it('dispatches tracking event', () => {
expect(trackingSpy).toHaveBeenCalledWith(undefined, 'popover_shown', {
label: 'trial_status_popover',
property: 'experiment:show_trial_status_in_sidebar',
});
expectTracking(trackingEvents.popoverShown);
});
});
});
......
import { GlLink } from '@gitlab/ui';
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 { mockTracking, unmockTracking } from 'helpers/tracking_helper';
describe('TrialStatusWidget component', () => {
let wrapper;
const { trackingEvents } = WIDGET;
const findGlLink = () => wrapper.findComponent(GlLink);
const createComponent = (props = {}) => {
......@@ -26,6 +29,14 @@ describe('TrialStatusWidget component', () => {
wrapper = null;
});
describe('interpolated strings', () => {
it('correctly interpolates them all', () => {
wrapper = createComponent();
expect(wrapper.text()).not.toMatch(/%{\w+}/);
});
});
describe('without the optional containerId prop', () => {
beforeEach(() => {
wrapper = createComponent();
......@@ -39,11 +50,18 @@ describe('TrialStatusWidget component', () => {
expect(findGlLink().attributes('id')).toBe(undefined);
});
it('renders with the correct tracking data attributes', () => {
const attrs = findGlLink().attributes();
expect(attrs['data-track-event']).toBe('click_link');
expect(attrs['data-track-label']).toBe('trial_status_widget');
expect(attrs['data-track-property']).toBe('experiment:show_trial_status_in_sidebar');
it('tracks when the widget is clicked', () => {
const { action, ...options } = trackingEvents.widgetClick;
const trackingSpy = mockTracking(undefined, undefined, jest.spyOn);
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