Commit 17d37a24 authored by Dallas Reedy's avatar Dallas Reedy

Add CTAs and click-tracking to the paid feature callout popover

- Add the two CTAs buttons: "Upgrade to GitLab Premium" & "Compare all
  plans"
- Track when those buttons are clicked
- Take the user to the appropriate place in the app within a new tab:
  - Upgrade: go to in-app purchase flow for the group which has the
    active trial and pre-select the "Premium" plan
  - Compare: go to the billing page for the group which has the active
    trial
parent aaeced81
<script>
import { GlPopover } from '@gitlab/ui';
import { GlButton, GlPopover } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { debounce } from 'lodash';
import { __, n__, s__, sprintf } from '~/locale';
......@@ -8,7 +8,13 @@ import Tracking from '~/tracking';
const RESIZE_EVENT_DEBOUNCE_MS = 150;
export default {
tracking: {
event: 'click_button',
labels: { upgrade: 'upgrade_to_ultimate', compare: 'compare_all_plans' },
property: 'experiment:highlight_paid_features_during_active_trial',
},
components: {
GlButton,
GlPopover,
},
mixins: [Tracking.mixin()],
......@@ -26,6 +32,14 @@ export default {
type: String,
required: true,
},
hrefComparePlans: {
type: String,
required: true,
},
hrefUpgradeToPaid: {
type: String,
required: true,
},
planNameForTrial: {
type: String,
required: true,
......@@ -54,6 +68,9 @@ export default {
disabled: false,
};
},
i18n: {
compareAllButtonTitle: s__('BillingPlans|Compare all plans'),
},
computed: {
popoverTitle() {
const i18nPopoverTitle = n__(
......@@ -77,6 +94,13 @@ export default {
planNameForUpgrade: this.planNameForUpgrade,
});
},
upgradeButtonTitle() {
const i18nUpgradeButtonTitle = s__('BillingPlans|Upgrade to GitLab %{planNameForUpgrade}');
return sprintf(i18nUpgradeButtonTitle, {
planNameForUpgrade: this.planNameForUpgrade,
});
},
},
created() {
this.debouncedResize = debounce(() => this.onResize(), RESIZE_EVENT_DEBOUNCE_MS);
......@@ -128,5 +152,38 @@ export default {
</div>
{{ popoverContent }}
<div class="gl-mt-5">
<gl-button
:href="hrefUpgradeToPaid"
target="_blank"
category="primary"
variant="confirm"
size="small"
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"
>
<span class="gl-font-sm">{{ upgradeButtonTitle }}</span>
</gl-button>
<gl-button
:href="hrefComparePlans"
target="_blank"
category="secondary"
variant="confirm"
size="small"
class="gl-mb-0"
block
data-testid="compareBtn"
:data-track-event="$options.tracking.event"
:data-track-label="$options.tracking.labels.compare"
:data-track-property="$options.tracking.property"
>
<span class="gl-font-sm">{{ $options.i18n.compareAllButtonTitle }}</span>
</gl-button>
</div>
</gl-popover>
</template>
......@@ -25,6 +25,8 @@ export const initPaidFeatureCalloutPopover = () => {
containerId,
daysRemaining,
featureName,
hrefComparePlans,
hrefUpgradeToPaid,
planNameForTrial,
planNameForUpgrade,
promoImageAltText,
......@@ -40,6 +42,8 @@ export const initPaidFeatureCalloutPopover = () => {
containerId,
daysRemaining: Number(daysRemaining),
featureName,
hrefComparePlans,
hrefUpgradeToPaid,
planNameForTrial,
planNameForUpgrade,
promoImageAltText,
......
......@@ -28,6 +28,8 @@ module PaidFeatureCalloutHelper
base_attrs.merge({
container_id: container_id,
days_remaining: group.trial_days_remaining,
href_compare_plans: group_billings_path(group),
href_upgrade_to_paid: premium_subscription_path_for_group(group),
plan_name_for_trial: group.gitlab_subscription&.plan_title,
plan_name_for_upgrade: 'Premium',
target_id: container_id
......@@ -43,4 +45,9 @@ module PaidFeatureCalloutHelper
def base_paid_feature_data_attrs(feature_name)
{ feature_name: feature_name }
end
def premium_subscription_path_for_group(group)
# Hard-coding the plan_id to the Premium plan on production & staging
new_subscriptions_path(namespace_id: group.id, plan_id: '2c92c0f876e0f4cc0176e176a08f1b70')
end
end
......@@ -15,8 +15,10 @@ describe('PaidFeatureCalloutPopover', () => {
const defaultProps = {
daysRemaining: 12,
featureName: 'some feature',
planNameForTrial: 'Ultimate',
planNameForUpgrade: 'Premium',
hrefComparePlans: '/group/test-group/-/billings',
hrefUpgradeToPaid: '/-/subscriptions/new?namespace_id=123&plan_id=abc456',
planNameForTrial: 'Awesomesauce',
planNameForUpgrade: 'Amazing',
targetId: 'some-feature-callout-target',
};
......@@ -32,38 +34,59 @@ describe('PaidFeatureCalloutPopover', () => {
wrapper.destroy();
});
describe('with some default props', () => {
beforeEach(() => {
wrapper = createComponent();
describe('GlPopover attributes', () => {
describe('with some default props', () => {
beforeEach(() => {
wrapper = createComponent();
});
it('sets attributes on the GlPopover component', () => {
const attributes = findGlPopover().attributes();
expect(attributes).toMatchObject({
boundary: 'viewport',
placement: 'top',
target: 'some-feature-callout-target',
});
expect(attributes.containerId).toBeUndefined();
});
});
it('sets attributes on the GlPopover component', () => {
const attributes = findGlPopover().attributes();
describe('with additional, optional props', () => {
beforeEach(() => {
wrapper = createComponent({
...defaultProps,
containerId: 'some-container-id',
});
});
expect(attributes).toMatchObject({
boundary: 'viewport',
placement: 'top',
target: 'some-feature-callout-target',
it('sets more attributes on the GlPopover component', () => {
expect(findGlPopover().attributes()).toMatchObject({
boundary: 'viewport',
container: 'some-container-id',
placement: 'top',
target: 'some-feature-callout-target',
});
});
expect(attributes.containerId).toBeUndefined();
});
});
describe('with additional, optional props', () => {
beforeEach(() => {
wrapper = createComponent({
...defaultProps,
containerId: 'some-container-id',
});
describe('popoverTitle', () => {
it('renders the title text', () => {
wrapper = createComponent();
expect(wrapper.vm.popoverTitle).toEqual('12 days remaining to enjoy some feature');
});
});
it('sets more attributes on the GlPopover component', () => {
expect(findGlPopover().attributes()).toMatchObject({
boundary: 'viewport',
container: 'some-container-id',
placement: 'top',
target: 'some-feature-callout-target',
});
describe('popoverContent', () => {
it('renders the content text', () => {
wrapper = createComponent();
expect(wrapper.vm.popoverContent).toEqual(
'Enjoying your GitLab Awesomesauce trial? To continue using some feature after your trial ends, upgrade to ' +
'GitLab Amazing.',
);
});
});
......@@ -121,6 +144,49 @@ describe('PaidFeatureCalloutPopover', () => {
});
});
describe('call-to-action buttons', () => {
const findUpgradeBtn = () => wrapper.findByTestId('upgradeBtn');
const findCompareBtn = () => wrapper.findByTestId('compareBtn');
beforeEach(() => {
wrapper = createComponent();
});
it('correctly renders an Upgrade button', () => {
const upgradeBtn = findUpgradeBtn();
expect(upgradeBtn.text()).toEqual('Upgrade to GitLab Amazing');
expect(upgradeBtn.attributes()).toMatchObject({
href: '/-/subscriptions/new?namespace_id=123&plan_id=abc456',
target: '_blank',
category: 'primary',
variant: 'confirm',
size: 'small',
block: '',
'data-track-event': 'click_button',
'data-track-label': 'upgrade_to_ultimate',
'data-track-property': 'experiment:highlight_paid_features_during_active_trial',
});
});
it('correctly renders a Compare button', () => {
const compareBtn = findCompareBtn();
expect(compareBtn.text()).toEqual('Compare all plans');
expect(compareBtn.attributes()).toMatchObject({
href: '/group/test-group/-/billings',
target: '_blank',
category: 'secondary',
variant: 'confirm',
size: 'small',
block: '',
'data-track-event': 'click_button',
'data-track-label': 'compare_all_plans',
'data-track-property': 'experiment:highlight_paid_features_during_active_trial',
});
});
});
describe('onShown', () => {
beforeEach(() => {
trackingSpy = mockTracking(undefined, undefined, jest.spyOn);
......
......@@ -71,15 +71,17 @@ RSpec.describe PaidFeatureCalloutHelper do
describe '#paid_feature_popover_data_attrs' do
let(:subscription) { instance_double(GitlabSubscription, plan_title: 'Ultimate') }
let(:group) { instance_double(Group, trial_days_remaining: 12, gitlab_subscription: subscription) }
let(:group) { instance_double(Group, id: 123, to_param: 'test-group', trial_days_remaining: 12, gitlab_subscription: subscription) }
subject { helper.paid_feature_popover_data_attrs(group: group, feature_name: 'first feature') }
it 'returns the set of data attributes needed to bootstrap the PaidFeatureCalloutPopover component' do
expected_attrs = {
container_id: 'first-feature-callout',
feature_name: 'first feature',
days_remaining: 12,
feature_name: 'first feature',
href_compare_plans: '/groups/test-group/-/billings',
href_upgrade_to_paid: '/-/subscriptions/new?namespace_id=123&plan_id=2c92c0f876e0f4cc0176e176a08f1b70',
plan_name_for_trial: 'Ultimate',
plan_name_for_upgrade: 'Premium',
target_id: 'first-feature-callout'
......
......@@ -4988,6 +4988,9 @@ msgstr ""
msgid "BillingPlans|@%{user_name} you are currently using the %{plan_name}."
msgstr ""
msgid "BillingPlans|Compare all plans"
msgstr ""
msgid "BillingPlans|Congratulations, your free trial is activated."
msgstr ""
......@@ -5021,6 +5024,9 @@ msgstr ""
msgid "BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}."
msgstr ""
msgid "BillingPlans|Upgrade to GitLab %{planNameForUpgrade}"
msgstr ""
msgid "BillingPlans|While GitLab is ending availability of the Bronze plan, you can still renew your Bronze subscription one additional time before %{eoa_bronze_plan_end_date}. We are also offering a limited time free upgrade to our Premium Plan (up to 25 users)! Learn more about the changes and offers in our %{announcement_link}."
msgstr ""
......
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