Commit 82a8940b authored by Peter Hegman's avatar Peter Hegman

Merge branch '323071-mlunoe-add-loading-to-the-cloud-activation-modal-button' into 'master'

Feat(Cloud Activation Form Modal): loading button

See merge request gitlab-org/gitlab!71385
parents fec45f18 67403800
...@@ -64,9 +64,6 @@ export default { ...@@ -64,9 +64,6 @@ export default {
return { return {
currentSubscription: {}, currentSubscription: {},
activationNotification: null, activationNotification: null,
activationListeners: {
[SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT]: this.displayActivationNotification,
},
subscriptionHistory: [], subscriptionHistory: [],
}; };
}, },
...@@ -78,6 +75,11 @@ export default { ...@@ -78,6 +75,11 @@ export default {
return this.hasActiveLicense || this.hasValidSubscriptionData; return this.hasActiveLicense || this.hasValidSubscriptionData;
}, },
}, },
created() {
this.$options.activationListeners = {
[SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT]: this.displayActivationNotification,
};
},
methods: { methods: {
displayActivationNotification(license) { displayActivationNotification(license) {
if (isInFuture(new Date(license.startsAt))) { if (isInFuture(new Date(license.startsAt))) {
...@@ -123,14 +125,14 @@ export default { ...@@ -123,14 +125,14 @@ export default {
v-if="canShowSubscriptionDetails" v-if="canShowSubscriptionDetails"
:subscription="currentSubscription" :subscription="currentSubscription"
:subscription-list="subscriptionHistory" :subscription-list="subscriptionHistory"
v-on="activationListeners" v-on="$options.activationListeners"
/> />
<div v-else class="row"> <div v-else class="row">
<div class="col-12 col-lg-8 offset-lg-2"> <div class="col-12 col-lg-8 offset-lg-2">
<h3 class="gl-mb-7 gl-mt-6 gl-text-center" data-testid="subscription-activation-title"> <h3 class="gl-mb-7 gl-mt-6 gl-text-center" data-testid="subscription-activation-title">
{{ $options.i18n.noActiveSubscription }} {{ $options.i18n.noActiveSubscription }}
</h3> </h3>
<subscription-activation-card v-on="activationListeners" /> <subscription-activation-card v-on="$options.activationListeners" />
<div class="row gl-mt-7"> <div class="row gl-mt-7">
<div class="col-lg-6"> <div class="col-lg-6">
<subscription-trial-card /> <subscription-trial-card />
......
...@@ -36,10 +36,12 @@ export default { ...@@ -36,10 +36,12 @@ export default {
data() { data() {
return { return {
error: null, error: null,
activationListeners: { };
[SUBSCRIPTION_ACTIVATION_FAILURE_EVENT]: this.handleActivationFailure, },
[SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT]: this.handleActivationSuccess, created() {
}, this.$options.activationListeners = {
[SUBSCRIPTION_ACTIVATION_FAILURE_EVENT]: this.handleActivationFailure,
[SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT]: this.handleActivationSuccess,
}; };
}, },
methods: { methods: {
...@@ -76,7 +78,7 @@ export default { ...@@ -76,7 +78,7 @@ export default {
</template> </template>
</gl-sprintf> </gl-sprintf>
</p> </p>
<subscription-activation-form class="gl-p-5" v-on="activationListeners" /> <subscription-activation-form class="gl-p-5" v-on="$options.activationListeners" />
<template #footer> <template #footer>
<gl-link <gl-link
v-if="licenseUploadPath" v-if="licenseUploadPath"
......
...@@ -15,6 +15,7 @@ import { ...@@ -15,6 +15,7 @@ import {
INVALID_CODE_ERROR_MESSAGE, INVALID_CODE_ERROR_MESSAGE,
SUBSCRIPTION_ACTIVATION_FAILURE_EVENT, SUBSCRIPTION_ACTIVATION_FAILURE_EVENT,
SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT, SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT,
SUBSCRIPTION_ACTIVATION_FINALIZED_EVENT,
subscriptionActivationForm, subscriptionActivationForm,
subscriptionQueries, subscriptionQueries,
} from '../constants'; } from '../constants';
...@@ -93,6 +94,7 @@ export default { ...@@ -93,6 +94,7 @@ export default {
submit() { submit() {
if (!this.form.state) { if (!this.form.state) {
this.form.showValidation = true; this.form.showValidation = true;
this.$emit(SUBSCRIPTION_ACTIVATION_FINALIZED_EVENT);
return; return;
} }
this.form.showValidation = false; this.form.showValidation = false;
...@@ -127,6 +129,7 @@ export default { ...@@ -127,6 +129,7 @@ export default {
this.handleError(error); this.handleError(error);
}) })
.finally(() => { .finally(() => {
this.$emit(SUBSCRIPTION_ACTIVATION_FINALIZED_EVENT);
this.isLoading = false; this.isLoading = false;
}); });
}, },
......
<script> <script>
import { GlModal } from '@gitlab/ui'; import { GlModal } from '@gitlab/ui';
import { __ } from '~/locale';
import { import {
activateLabel, activateLabel,
cancelLabel,
activateSubscription, activateSubscription,
subscriptionActivationInsertCode, subscriptionActivationInsertCode,
SUBSCRIPTION_ACTIVATION_FAILURE_EVENT, SUBSCRIPTION_ACTIVATION_FAILURE_EVENT,
SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT, SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT,
SUBSCRIPTION_ACTIVATION_FINALIZED_EVENT,
} from '../constants'; } from '../constants';
import SubscriptionActivationErrors from './subscription_activation_errors.vue'; import SubscriptionActivationErrors from './subscription_activation_errors.vue';
import SubscriptionActivationForm from './subscription_activation_form.vue'; import SubscriptionActivationForm from './subscription_activation_form.vue';
export default { export default {
actionCancel: { text: __('Cancel') },
actionPrimary: {
text: activateLabel,
},
bodyText: subscriptionActivationInsertCode, bodyText: subscriptionActivationInsertCode,
title: activateSubscription, title: activateSubscription,
name: 'SubscriptionActivationModal', name: 'SubscriptionActivationModal',
...@@ -41,13 +38,37 @@ export default { ...@@ -41,13 +38,37 @@ export default {
data() { data() {
return { return {
error: null, error: null,
activationListeners: { isLoading: false,
[SUBSCRIPTION_ACTIVATION_FAILURE_EVENT]: this.handleActivationFailure, };
[SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT]: this.handleActivationSuccess, },
}, computed: {
actionCancel() {
return { text: cancelLabel };
},
actionPrimary() {
return {
text: activateLabel,
attributes: [
{
variant: 'confirm',
category: 'primary',
loading: this.isLoading,
},
],
};
},
},
created() {
this.$options.activationListeners = {
[SUBSCRIPTION_ACTIVATION_FAILURE_EVENT]: this.handleActivationFailure,
[SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT]: this.handleActivationSuccess,
[SUBSCRIPTION_ACTIVATION_FINALIZED_EVENT]: this.handleActivationFinalized,
}; };
}, },
methods: { methods: {
handleActivationFinalized() {
this.isLoading = false;
},
handleActivationFailure(error) { handleActivationFailure(error) {
this.error = error; this.error = error;
}, },
...@@ -60,6 +81,7 @@ export default { ...@@ -60,6 +81,7 @@ export default {
this.$emit('change', event); this.$emit('change', event);
}, },
handlePrimary() { handlePrimary() {
this.isLoading = true;
this.$refs.form.submit(); this.$refs.form.submit();
}, },
removeError() { removeError() {
...@@ -74,8 +96,8 @@ export default { ...@@ -74,8 +96,8 @@ export default {
:visible="visible" :visible="visible"
:modal-id="modalId" :modal-id="modalId"
:title="$options.title" :title="$options.title"
:action-cancel="$options.actionCancel" :action-cancel="actionCancel"
:action-primary="$options.actionPrimary" :action-primary="actionPrimary"
@primary.prevent="handlePrimary" @primary.prevent="handlePrimary"
@hidden="removeError" @hidden="removeError"
@change="handleChange" @change="handleChange"
...@@ -85,7 +107,7 @@ export default { ...@@ -85,7 +107,7 @@ export default {
<subscription-activation-form <subscription-activation-form
ref="form" ref="form"
:hide-submit-button="true" :hide-submit-button="true"
v-on="activationListeners" v-on="$options.activationListeners"
/> />
</gl-modal> </gl-modal>
</template> </template>
...@@ -19,6 +19,7 @@ export const subscriptionActivationInsertCode = __( ...@@ -19,6 +19,7 @@ export const subscriptionActivationInsertCode = __(
export const howToActivateSubscription = s__( export const howToActivateSubscription = s__(
'SuperSonics|Learn how to %{linkStart}activate your subscription%{linkEnd}.', 'SuperSonics|Learn how to %{linkStart}activate your subscription%{linkEnd}.',
); );
export const cancelLabel = __('Cancel');
export const activateLabel = s__('SuperSonics|Activate'); export const activateLabel = s__('SuperSonics|Activate');
export const activateSubscription = s__('SuperSonics|Activate subscription'); export const activateSubscription = s__('SuperSonics|Activate subscription');
export const activateCloudLicense = s__('SuperSonics|Activate cloud license'); export const activateCloudLicense = s__('SuperSonics|Activate cloud license');
...@@ -129,6 +130,7 @@ export const buySubscriptionCard = { ...@@ -129,6 +130,7 @@ export const buySubscriptionCard = {
export const SUBSCRIPTION_ACTIVATION_FAILURE_EVENT = 'subscription-activation-failure'; export const SUBSCRIPTION_ACTIVATION_FAILURE_EVENT = 'subscription-activation-failure';
export const SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT = 'subscription-activation-success'; export const SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT = 'subscription-activation-success';
export const SUBSCRIPTION_ACTIVATION_FINALIZED_EVENT = 'subscription-activation-finalized';
export const INVALID_CODE_ERROR_MESSAGE = 'invalid activation code'; export const INVALID_CODE_ERROR_MESSAGE = 'invalid activation code';
export const CONNECTIVITY_ERROR = 'CONNECTIVITY_ERROR'; export const CONNECTIVITY_ERROR = 'CONNECTIVITY_ERROR';
......
...@@ -7,12 +7,14 @@ import { ...@@ -7,12 +7,14 @@ import {
INVALID_CODE_ERROR, INVALID_CODE_ERROR,
SUBSCRIPTION_ACTIVATION_FAILURE_EVENT, SUBSCRIPTION_ACTIVATION_FAILURE_EVENT,
SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT, SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT,
SUBSCRIPTION_ACTIVATION_FINALIZED_EVENT,
subscriptionQueries, subscriptionQueries,
subscriptionActivationForm, subscriptionActivationForm,
} from 'ee/admin/subscriptions/show/constants'; } from 'ee/admin/subscriptions/show/constants';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import { stubComponent } from 'helpers/stub_component'; import { stubComponent } from 'helpers/stub_component';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { preventDefault, stopPropagation } from '../../test_helpers'; import { preventDefault, stopPropagation } from '../../test_helpers';
import { import {
activateLicenseMutationResponse, activateLicenseMutationResponse,
...@@ -121,6 +123,10 @@ describe('SubscriptionActivationForm', () => { ...@@ -121,6 +123,10 @@ describe('SubscriptionActivationForm', () => {
expect(mutationMock).toHaveBeenCalledTimes(0); expect(mutationMock).toHaveBeenCalledTimes(0);
}); });
it(`emits the ${SUBSCRIPTION_ACTIVATION_FINALIZED_EVENT} event`, () => {
expect(wrapper.emitted(SUBSCRIPTION_ACTIVATION_FINALIZED_EVENT).length).toBe(1);
});
describe('adds text that does not match the pattern', () => { describe('adds text that does not match the pattern', () => {
beforeEach(async () => { beforeEach(async () => {
await findActivationCodeInput().vm.$emit('input', `${fakeActivationCode}2021-asdf`); await findActivationCodeInput().vm.$emit('input', `${fakeActivationCode}2021-asdf`);
...@@ -152,6 +158,12 @@ describe('SubscriptionActivationForm', () => { ...@@ -152,6 +158,12 @@ describe('SubscriptionActivationForm', () => {
subscriptionActivationForm.acceptTermsFeedback, subscriptionActivationForm.acceptTermsFeedback,
); );
}); });
it(`emits the ${SUBSCRIPTION_ACTIVATION_FINALIZED_EVENT} event`, async () => {
findActivateSubscriptionForm().vm.$emit('submit', createFakeEvent());
await waitForPromises();
expect(wrapper.emitted(SUBSCRIPTION_ACTIVATION_FINALIZED_EVENT).length).toBe(2);
});
}); });
}); });
}); });
......
import { GlModal } from '@gitlab/ui'; import { GlModal } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import SubscriptionActivationErrors from 'ee/admin/subscriptions/show/components/subscription_activation_errors.vue'; import SubscriptionActivationErrors from 'ee/admin/subscriptions/show/components/subscription_activation_errors.vue';
import SubscriptionActivationForm from 'ee/admin/subscriptions/show/components/subscription_activation_form.vue'; import SubscriptionActivationForm from 'ee/admin/subscriptions/show/components/subscription_activation_form.vue';
import SubscriptionActivationModal from 'ee/admin/subscriptions/show/components/subscription_activation_modal.vue'; import SubscriptionActivationModal from 'ee/admin/subscriptions/show/components/subscription_activation_modal.vue';
...@@ -10,29 +11,29 @@ import { ...@@ -10,29 +11,29 @@ import {
SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT, SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT,
subscriptionActivationInsertCode, subscriptionActivationInsertCode,
} from 'ee/admin/subscriptions/show/constants'; } from 'ee/admin/subscriptions/show/constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { preventDefault } from '../../test_helpers'; import { preventDefault } from '../../test_helpers';
import { activateLicenseMutationResponse } from '../mock_data'; import { activateLicenseMutationResponse } from '../mock_data';
const modalId = 'fake-modal-id';
describe('SubscriptionActivationModal', () => { describe('SubscriptionActivationModal', () => {
let wrapper; let wrapper;
const modalId = 'fake-modal-id';
const findGlModal = () => wrapper.findComponent(GlModal); const findGlModal = () => wrapper.findComponent(GlModal);
const firePrimaryEvent = () => findGlModal().vm.$emit('primary', { preventDefault });
const findSubscriptionActivationErrors = () => const findSubscriptionActivationErrors = () =>
wrapper.findComponent(SubscriptionActivationErrors); wrapper.findComponent(SubscriptionActivationErrors);
const findSubscriptionActivationForm = () => wrapper.findComponent(SubscriptionActivationForm); const findSubscriptionActivationForm = () => wrapper.findComponent(SubscriptionActivationForm);
const createComponent = ({ props = {} } = {}) => { const createComponent = (options = {}) => {
wrapper = extendedWrapper( const { props = {}, mountFn = shallowMount } = options;
shallowMount(SubscriptionActivationModal, { wrapper = mountFn(SubscriptionActivationModal, {
propsData: { propsData: {
modalId, modalId,
visible: false, visible: false,
...props, ...props,
}, },
}), });
);
}; };
afterEach(() => { afterEach(() => {
...@@ -78,19 +79,34 @@ describe('SubscriptionActivationModal', () => { ...@@ -78,19 +79,34 @@ describe('SubscriptionActivationModal', () => {
}); });
describe('subscription activation', () => { describe('subscription activation', () => {
const fakeEvent = 'fake-modal-event'; let submitSpy;
describe('when the "primary" button is clicked', () => {
beforeEach(async () => {
createComponent({ mountFn: mount, props: { visible: true } });
// Wait for $refs.form to be present
await nextTick();
submitSpy = jest.spyOn(wrapper.vm.$refs.form, 'submit');
});
describe('when submitting the form', () => { it('submits the form', () => {
beforeEach(() => { firePrimaryEvent();
createComponent(); expect(submitSpy).toHaveBeenCalled();
jest });
.spyOn(wrapper.vm, 'handlePrimary')
.mockImplementation(() => wrapper.vm.$emit(fakeEvent)); it('shows loading in the button', async () => {
findGlModal().vm.$emit('primary', { preventDefault }); submitSpy.mockImplementation(() => {});
firePrimaryEvent();
// Wait for submit to emit event
await nextTick();
expect(findGlModal().props('actionPrimary').attributes[0].loading).toEqual(true);
}); });
it('emits the correct event', () => { it('stops loading in the button', async () => {
expect(wrapper.emitted(fakeEvent)).toEqual([[]]); firePrimaryEvent();
// Wait for submit to emit event
await nextTick();
expect(findGlModal().props('actionPrimary').attributes[0].loading).toEqual(false);
}); });
}); });
......
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