Commit a618af50 authored by Angelo Gulina's avatar Angelo Gulina Committed by Brandon Labuschagne

Add mid-term banner

parent 09d4ec74
<script>
import { GlBanner, GlLink, GlSprintf } from '@gitlab/ui';
import {
activateCloudLicense,
subscriptionBannerText,
subscriptionBannerTitle,
} from '../constants';
export const ACTIVATE_SUBSCRIPTION_EVENT = 'activate-subscription';
export default {
name: 'SubscriptionActivationBanner',
i18n: {
bannerText: subscriptionBannerText,
buttonText: activateCloudLicense,
title: subscriptionBannerTitle,
},
components: {
GlBanner,
GlLink,
GlSprintf,
},
inject: ['congratulationSvgPath', 'customersPortalUrl'],
methods: {
handlePrimary() {
this.$emit(ACTIVATE_SUBSCRIPTION_EVENT);
},
},
};
</script>
<template>
<gl-banner
:button-text="$options.i18n.buttonText"
:title="$options.i18n.title"
variant="promotion"
:svg-path="congratulationSvgPath"
@primary="handlePrimary"
>
<p>
<gl-sprintf :message="$options.i18n.bannerText">
<template #blogPostLink="{ content }">
<gl-link href="#" target="_blank">{{ content }}</gl-link>
</template>
<template #portalLink="{ content }">
<gl-link :href="customersPortalUrl" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
</gl-banner>
</template>
...@@ -22,11 +22,19 @@ export default { ...@@ -22,11 +22,19 @@ export default {
SubscriptionActivationErrors, SubscriptionActivationErrors,
SubscriptionActivationForm, SubscriptionActivationForm,
}, },
model: {
prop: 'visible',
event: 'change',
},
props: { props: {
modalId: { modalId: {
type: String, type: String,
required: true, required: true,
}, },
visible: {
type: Boolean,
required: true,
},
}, },
data() { data() {
return { return {
...@@ -38,7 +46,10 @@ export default { ...@@ -38,7 +46,10 @@ export default {
this.error = error; this.error = error;
}, },
handleActivationSuccess() { handleActivationSuccess() {
this.$refs.modal.hide(); this.$emit('change', false);
},
handleChange(event) {
this.$emit('change', event);
}, },
handlePrimary() { handlePrimary() {
this.$refs.form.submit(); this.$refs.form.submit();
...@@ -52,13 +63,14 @@ export default { ...@@ -52,13 +63,14 @@ export default {
<template> <template>
<gl-modal <gl-modal
ref="modal" :visible="visible"
:modal-id="modalId" :modal-id="modalId"
:title="$options.title" :title="$options.title"
:action-cancel="$options.actionCancel" :action-cancel="$options.actionCancel"
:action-primary="$options.actionPrimary" :action-primary="$options.actionPrimary"
@primary.prevent="handlePrimary" @primary.prevent="handlePrimary"
@hidden="removeError" @hidden="removeError"
@change="handleChange"
> >
<subscription-activation-errors v-if="error" class="mb-4" :error="error" /> <subscription-activation-errors v-if="error" class="mb-4" :error="error" />
<p>{{ $options.bodyText }}</p> <p>{{ $options.bodyText }}</p>
......
...@@ -66,6 +66,7 @@ export default { ...@@ -66,6 +66,7 @@ export default {
shouldShowNotifications: false, shouldShowNotifications: false,
subscriptionSyncStatus: null, subscriptionSyncStatus: null,
subscriptionDetailsFields, subscriptionDetailsFields,
activationModalVisible: false,
}; };
}, },
computed: { computed: {
...@@ -138,7 +139,11 @@ export default { ...@@ -138,7 +139,11 @@ export default {
<template> <template>
<div> <div>
<subscription-activation-modal v-if="hasSubscription" :modal-id="$options.modal.id" /> <subscription-activation-modal
v-if="hasSubscription"
v-model="activationModalVisible"
:modal-id="$options.modal.id"
/>
<subscription-sync-notifications <subscription-sync-notifications
v-if="shouldShowNotifications" v-if="shouldShowNotifications"
class="mb-4" class="mb-4"
......
...@@ -137,3 +137,7 @@ export const connectivityErrorAlert = { ...@@ -137,3 +137,7 @@ export const connectivityErrorAlert = {
), ),
}; };
export const supportLink = 'https://about.gitlab.com/support/#contact-support'; export const supportLink = 'https://about.gitlab.com/support/#contact-support';
export const subscriptionBannerTitle = s__('SuperSonics|Cloud licensing');
export const subscriptionBannerText = s__(
"SuperSonics|Cloud licensing is now available. It's an easier way to activate instances and manage subscriptions. Read more about it in our %{blogPostLinkStart}blog post%{blogPostLinkEnd}. Activation codes are available in the %{portalLinkStart}Customers Portal%{portalLinkEnd}.",
);
...@@ -25,6 +25,7 @@ export default () => { ...@@ -25,6 +25,7 @@ export default () => {
const { const {
buySubscriptionPath, buySubscriptionPath,
congratulationSvgPath,
customersPortalUrl, customersPortalUrl,
freeTrialPath, freeTrialPath,
hasActiveLicense, hasActiveLicense,
...@@ -41,6 +42,7 @@ export default () => { ...@@ -41,6 +42,7 @@ export default () => {
apolloProvider, apolloProvider,
provide: { provide: {
buySubscriptionPath, buySubscriptionPath,
congratulationSvgPath,
connectivityHelpURL, connectivityHelpURL,
customersPortalUrl, customersPortalUrl,
freeTrialPath, freeTrialPath,
......
...@@ -60,7 +60,8 @@ module LicenseHelper ...@@ -60,7 +60,8 @@ module LicenseHelper
has_active_license: (has_active_license? ? 'true' : 'false'), has_active_license: (has_active_license? ? 'true' : 'false'),
license_upload_path: new_admin_license_path, license_upload_path: new_admin_license_path,
license_remove_path: admin_license_path, license_remove_path: admin_license_path,
subscription_sync_path: sync_seat_link_admin_license_path subscription_sync_path: sync_seat_link_admin_license_path,
congratulation_svg_path: image_path('illustrations/illustration-congratulation-purchase.svg')
} }
end end
......
import { GlBanner, GlLink, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import SubscriptionActivationBanner, {
ACTIVATE_SUBSCRIPTION_EVENT,
} from 'ee/admin/subscriptions/show/components/subscription_activation_banner.vue';
import {
activateCloudLicense,
subscriptionBannerText,
subscriptionBannerTitle,
} from 'ee/admin/subscriptions/show/constants';
describe('SubscriptionActivationBanner', () => {
let wrapper;
const findBanner = () => wrapper.findComponent(GlBanner);
const findLink = (at) => wrapper.findAllComponents(GlLink).at(at);
const customersPortalUrl = 'customers.dot';
const congratulationSvgPath = '/path/to/svg';
const createComponent = () => {
wrapper = shallowMount(SubscriptionActivationBanner, {
provide: {
congratulationSvgPath,
customersPortalUrl,
},
stubs: {
GlSprintf,
},
});
};
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('provides the correct props to the banner', () => {
expect(findBanner().props()).toMatchObject({
buttonText: activateCloudLicense,
title: subscriptionBannerTitle,
svgPath: congratulationSvgPath,
});
});
it('contains help text', () => {
expect(findBanner().text()).toMatchInterpolatedText(subscriptionBannerText);
});
it('contains a link to the blog post', () => {
expect(findLink(0).attributes('href')).toBe('#');
});
it('contains a link to the customers portal', () => {
expect(findLink(1).attributes('href')).toBe(customersPortalUrl);
});
it('emits an event when the primary button is clicked', () => {
expect(wrapper.emitted(ACTIVATE_SUBSCRIPTION_EVENT)).toBeUndefined();
findBanner().vm.$emit('primary');
expect(wrapper.emitted(ACTIVATE_SUBSCRIPTION_EVENT)).toEqual([[]]);
});
});
...@@ -23,14 +23,14 @@ describe('SubscriptionActivationModal', () => { ...@@ -23,14 +23,14 @@ describe('SubscriptionActivationModal', () => {
wrapper.findComponent(SubscriptionActivationErrors); wrapper.findComponent(SubscriptionActivationErrors);
const findSubscriptionActivationForm = () => wrapper.findComponent(SubscriptionActivationForm); const findSubscriptionActivationForm = () => wrapper.findComponent(SubscriptionActivationForm);
const createComponent = ({ props = {}, stubs = {} } = {}) => { const createComponent = ({ props = {} } = {}) => {
wrapper = extendedWrapper( wrapper = extendedWrapper(
shallowMount(SubscriptionActivationModal, { shallowMount(SubscriptionActivationModal, {
propsData: { propsData: {
modalId, modalId,
visible: false,
...props, ...props,
}, },
stubs,
}), }),
); );
}; };
...@@ -67,11 +67,18 @@ describe('SubscriptionActivationModal', () => { ...@@ -67,11 +67,18 @@ describe('SubscriptionActivationModal', () => {
it('does not show any error', () => { it('does not show any error', () => {
expect(findSubscriptionActivationErrors().exists()).toBe(false); expect(findSubscriptionActivationErrors().exists()).toBe(false);
}); });
it('emits a change event', () => {
expect(wrapper.emitted('change')).toBeUndefined();
findGlModal().vm.$emit('change', false);
expect(wrapper.emitted('change')).toEqual([[false]]);
});
}); });
describe('subscription activation', () => { describe('subscription activation', () => {
const fakeEvent = 'fake-modal-event'; const fakeEvent = 'fake-modal-event';
const hiddenEven = 'hidden';
describe('when submitting the form', () => { describe('when submitting the form', () => {
beforeEach(() => { beforeEach(() => {
...@@ -89,15 +96,19 @@ describe('SubscriptionActivationModal', () => { ...@@ -89,15 +96,19 @@ describe('SubscriptionActivationModal', () => {
describe('successful activation', () => { describe('successful activation', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ stubs: { GlModal } }); createComponent({ props: { visible: true } });
jest
.spyOn(wrapper.vm.$refs.modal, 'hide')
.mockImplementation(() => wrapper.vm.$emit(hiddenEven));
findSubscriptionActivationForm().vm.$emit(SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT);
}); });
it('it emits a hidden event', () => { it('provides the correct prop to the modal', () => {
expect(wrapper.emitted(hiddenEven)).toEqual([[]]); expect(findGlModal().props('visible')).toBe(true);
});
it('hides the modal', () => {
expect(wrapper.emitted('change')).toBeUndefined();
findSubscriptionActivationForm().vm.$emit(SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT);
expect(wrapper.emitted('change')).toEqual([[false]]);
}); });
}); });
......
...@@ -138,11 +138,18 @@ describe('Subscription Breakdown', () => { ...@@ -138,11 +138,18 @@ describe('Subscription Breakdown', () => {
}); });
it('presents a subscription activation modal', () => { it('presents a subscription activation modal', () => {
expect(findSubscriptionActivationModal().exists()).toBe(true); expect(findSubscriptionActivationModal().props()).toMatchObject({
modalId,
visible: false,
});
}); });
it('passes the correct modal id', () => { it('updates visible of subscription activation modal when change emitted', async () => {
expect(findSubscriptionActivationModal().attributes('modalid')).toBe(modalId); findSubscriptionActivationModal().vm.$emit('change', true);
await wrapper.vm.$nextTick();
expect(findSubscriptionActivationModal().props('visible')).toBe(true);
}); });
describe('footer buttons', () => { describe('footer buttons', () => {
...@@ -279,9 +286,7 @@ describe('Subscription Breakdown', () => { ...@@ -279,9 +286,7 @@ describe('Subscription Breakdown', () => {
expect(findSubscriptionSyncNotifications().exists()).toBe(false); expect(findSubscriptionSyncNotifications().exists()).toBe(false);
}); });
it('shows a modal', () => { it('shows modal when active subscription action clicked', () => {
const props = { subscription: { ...licenseFile } };
createComponent({ props, stubs: { GlCard, SubscriptionDetailsCard } });
findActivateSubscriptionAction().vm.$emit('click'); findActivateSubscriptionAction().vm.$emit('click');
expect(glModalDirective).toHaveBeenCalledWith(modalId); expect(glModalDirective).toHaveBeenCalledWith(modalId);
......
...@@ -99,7 +99,8 @@ RSpec.describe LicenseHelper do ...@@ -99,7 +99,8 @@ RSpec.describe LicenseHelper do
buy_subscription_path: 'subscriptions_plans_url', buy_subscription_path: 'subscriptions_plans_url',
subscription_sync_path: sync_seat_link_admin_license_path, subscription_sync_path: sync_seat_link_admin_license_path,
license_upload_path: new_admin_license_path, license_upload_path: new_admin_license_path,
license_remove_path: admin_license_path }) license_remove_path: admin_license_path,
congratulation_svg_path: helper.image_path('illustrations/illustration-congratulation-purchase.svg') })
end end
end end
...@@ -113,7 +114,8 @@ RSpec.describe LicenseHelper do ...@@ -113,7 +114,8 @@ RSpec.describe LicenseHelper do
buy_subscription_path: 'subscriptions_plans_url', buy_subscription_path: 'subscriptions_plans_url',
subscription_sync_path: sync_seat_link_admin_license_path, subscription_sync_path: sync_seat_link_admin_license_path,
license_upload_path: new_admin_license_path, license_upload_path: new_admin_license_path,
license_remove_path: admin_license_path }) license_remove_path: admin_license_path,
congratulation_svg_path: helper.image_path('illustrations/illustration-congratulation-purchase.svg') })
end end
end end
end end
......
...@@ -31425,6 +31425,12 @@ msgstr "" ...@@ -31425,6 +31425,12 @@ msgstr ""
msgid "SuperSonics|Cloud license" msgid "SuperSonics|Cloud license"
msgstr "" msgstr ""
msgid "SuperSonics|Cloud licensing"
msgstr ""
msgid "SuperSonics|Cloud licensing is now available. It's an easier way to activate instances and manage subscriptions. Read more about it in our %{blogPostLinkStart}blog post%{blogPostLinkEnd}. Activation codes are available in the %{portalLinkStart}Customers Portal%{portalLinkEnd}."
msgstr ""
msgid "SuperSonics|Expires on" msgid "SuperSonics|Expires on"
msgstr "" 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