Commit 09160f78 authored by Marcin Sedlak-Jakubowski's avatar Marcin Sedlak-Jakubowski Committed by Kushal Pandya

Migrate licenses confirmation modal to use Pajamas

This refactor the license delete confirmation modal
to migrate to GlModal and GlSprintf
parent ec218553
......@@ -7,6 +7,7 @@ import {
GlLoadingIcon,
GlIcon,
GlButton,
GlModalDirective,
} from '@gitlab/ui';
import { getIssueStatusFromLicenseStatus } from 'ee/vue_shared/license_compliance/store/utils';
import { LICENSE_MANAGEMENT } from 'ee/vue_shared/license_compliance/store/constants';
......@@ -30,6 +31,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
GlModal: GlModalDirective,
},
props: {
......@@ -99,6 +101,7 @@ export default {
</gl-dropdown>
<gl-button
v-gl-tooltip
v-gl-modal.modal-license-delete-confirmation
:title="__('Remove license')"
:aria-label="__('Remove license')"
:disabled="loading"
......@@ -106,7 +109,6 @@ export default {
class="js-remove-button gl-ml-3"
category="tertiary"
data-toggle="modal"
data-target="#modal-license-delete-confirmation"
@click="setLicenseInModal(license)"
/>
</div>
......
<script>
/* eslint-disable vue/no-v-html */
import { escape } from 'lodash';
import { mapActions, mapState } from 'vuex';
import { LICENSE_MANAGEMENT } from 'ee/vue_shared/license_compliance/store/constants';
import { s__, sprintf } from '~/locale';
import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
import { GlModal, GlSprintf } from '@gitlab/ui';
import { s__, __ } from '~/locale';
export default {
name: 'LicenseDeleteConfirmationModal',
components: { GlModal: DeprecatedModal2 },
components: { GlModal, GlSprintf },
computed: {
...mapState(LICENSE_MANAGEMENT, ['currentLicenseInModal']),
confirmationText() {
const name = `<strong>${escape(this.currentLicenseInModal.name)}</strong>`;
return sprintf(
s__('LicenseCompliance|You are about to remove the license, %{name}, from this project.'),
{ name },
false,
);
},
},
methods: {
...mapActions(LICENSE_MANAGEMENT, ['resetLicenseInModal', 'deleteLicense']),
},
modal: {
title: s__('LicenseCompliance|Remove license?'),
actionPrimary: {
text: s__('LicenseCompliance|Remove license'),
attributes: [{ variant: 'danger' }],
},
actionCancel: {
text: __('Cancel'),
},
},
};
</script>
<template>
<gl-modal
id="modal-license-delete-confirmation"
:header-title-text="s__('LicenseCompliance|Remove license?')"
:footer-primary-button-text="s__('LicenseCompliance|Remove license')"
footer-primary-button-variant="danger"
modal-id="modal-license-delete-confirmation"
:title="$options.modal.title"
:action-primary="$options.modal.actionPrimary"
:action-cancel="$options.modal.actionCancel"
@primary="deleteLicense(currentLicenseInModal)"
@cancel="resetLicenseInModal"
@submit="deleteLicense(currentLicenseInModal)"
>
<span v-if="currentLicenseInModal" v-html="confirmationText"></span>
<gl-sprintf
v-if="currentLicenseInModal"
:message="
s__('LicenseCompliance|You are about to remove the license, %{name}, from this project.')
"
>
<template #name>
<strong>{{ currentLicenseInModal.name }}</strong>
</template>
</gl-sprintf>
</gl-modal>
</template>
---
title: Refactor license delete confirmation modal to use Pajamas
merge_request: 46149
author:
type: other
import Vue from 'vue';
import Vuex from 'vuex';
import DeleteConfirmationModal from 'ee/vue_shared/license_compliance/components/delete_confirmation_modal.vue';
import { trimText } from 'helpers/text_helper';
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlModal, GlSprintf } from '@gitlab/ui';
import Component from 'ee/vue_shared/license_compliance/components/delete_confirmation_modal.vue';
import { approvedLicense } from '../mock_data';
Vue.use(Vuex);
const localVue = createLocalVue();
localVue.use(Vuex);
describe('DeleteConfirmationModal', () => {
const Component = Vue.extend(DeleteConfirmationModal);
let vm;
let store;
let actions;
let wrapper;
beforeEach(() => {
actions = {
resetLicenseInModal: jest.fn(),
deleteLicense: jest.fn(),
};
const mockEvent = { preventDefault: jest.fn() };
const actions = {
resetLicenseInModal: jest.fn(),
deleteLicense: jest.fn(),
};
store = new Vuex.Store({
const createStore = (initialState = {}) => {
return new Vuex.Store({
modules: {
licenseManagement: {
namespaced: true,
state: {
currentLicenseInModal: approvedLicense,
...initialState,
},
actions,
},
},
});
};
const createComponent = initialState => {
store = createStore(initialState);
wrapper = shallowMount(Component, {
localVue,
store,
stubs: {
GlModal,
GlSprintf,
},
});
};
vm = mountComponentWithStore(Component, { store });
const findModal = () => wrapper.find(GlModal);
beforeEach(() => {
createComponent();
});
afterEach(() => {
vm.$destroy();
wrapper.destroy();
});
describe('computed', () => {
describe('confirmationText', () => {
it('returns information text with current license name in bold', () => {
expect(vm.confirmationText).toBe(
`You are about to remove the license, <strong>${approvedLicense.name}</strong>, from this project.`,
);
});
describe('modal', () => {
it('should be loaded', () => {
expect(findModal().exists()).toBe(true);
});
it('escapes the license name', done => {
const name = '<a href="#">BAD</a>';
const nameEscaped = '&lt;a href=&quot;#&quot;&gt;BAD&lt;/a&gt;';
store.replaceState({
...store.state,
licenseManagement: {
currentLicenseInModal: {
...approvedLicense,
name,
},
},
});
Vue.nextTick()
.then(() => {
expect(vm.confirmationText).toBe(
`You are about to remove the license, <strong>${nameEscaped}</strong>, from this project.`,
);
})
.then(done)
.catch(done.fail);
it('should have Primary and Cancel actions', () => {
expect(findModal().props()).toMatchObject({
actionPrimary: {
text: 'Remove license',
},
actionCancel: {
text: 'Cancel',
},
});
});
});
describe('interaction', () => {
describe('triggering resetLicenseInModal on canceling', () => {
it('by clicking the cancel button', () => {
const linkEl = vm.$el.querySelector('.js-modal-cancel-action');
linkEl.click();
it('should have the confirmation text', () => {
expect(findModal().html()).toContain(
`You are about to remove the license, <strong>${approvedLicense.name}</strong>, from this project.`,
);
});
expect(actions.resetLicenseInModal).toHaveBeenCalled();
});
it('should escape the confirmation text', () => {
const name = '<a href="#">BAD</a>';
const nameEscaped = '&lt;a href="#"&gt;BAD&lt;/a&gt;';
it('by clicking the X button', () => {
const linkEl = vm.$el.querySelector('.js-modal-close-action');
linkEl.click();
const currentLicenseInModal = {
...approvedLicense,
name,
};
expect(actions.resetLicenseInModal).toHaveBeenCalled();
createComponent({
currentLicenseInModal,
});
});
describe('triggering deleteLicense on canceling', () => {
it('by clicking the confirmation button', () => {
const linkEl = vm.$el.querySelector('.js-modal-primary-action');
linkEl.click();
expect(actions.deleteLicense).toHaveBeenCalledWith(
expect.any(Object),
store.state.licenseManagement.currentLicenseInModal,
);
});
expect(findModal().html()).toContain(
`You are about to remove the license, <strong>${nameEscaped}</strong>, from this project`,
);
});
});
describe('template', () => {
it('renders modal title', () => {
const headerEl = vm.$el.querySelector('.modal-title');
expect(headerEl).not.toBeNull();
expect(headerEl.innerText.trim()).toBe('Remove license?');
});
it('renders button in modal footer', () => {
const footerButton = vm.$el.querySelector('.js-modal-primary-action');
expect(footerButton).not.toBeNull();
expect(footerButton.innerText.trim()).toBe('Remove license');
describe('interaction', () => {
it('triggering resetLicenseInModal on cancel', async () => {
findModal().vm.$emit('cancel', mockEvent);
await wrapper.vm.$nextTick();
expect(actions.resetLicenseInModal).toHaveBeenCalled();
});
it('renders modal body', () => {
const modalBody = vm.$el.querySelector('.modal-body');
expect(modalBody).not.toBeNull();
expect(trimText(modalBody.innerText)).toBe(
`You are about to remove the license, ${approvedLicense.name}, from this project.`,
);
it('triggering deleteLicense on cancel', async () => {
findModal().vm.$emit('primary', mockEvent);
await wrapper.vm.$nextTick();
expect(actions.deleteLicense).toHaveBeenCalled();
});
});
});
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