Commit 5d696a75 authored by Brandon Labuschagne's avatar Brandon Labuschagne

Refactor to use a single modal

Prevent the modal from being rendered
for each button, instead render a single
modal and add an event handler to the buttons
which transfer data via attributes.
parent d53b89b4
<script> <script>
import { uniqueId } from 'lodash'; import { GlModal, GlSprintf } from '@gitlab/ui';
import { GlButton, GlModal, GlModalDirective, GlSprintf } from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
export default { export default {
components: { components: {
GlButton,
GlModal, GlModal,
GlSprintf, GlSprintf,
}, },
directives: {
GlModal: GlModalDirective,
},
inject: {
name: {
default: '',
},
path: {
default: '',
},
},
data() { data() {
return { return {
modalId: uniqueId('application-delete-button-'), name: '',
path: '',
}; };
}, },
mounted() {
document.querySelectorAll('.js-application-delete-button').forEach((button) => {
button.addEventListener('click', (e) => {
e.preventDefault();
this.show(button.dataset);
});
});
},
methods: { methods: {
show(dataset) {
const { name, path } = dataset;
this.name = name;
this.path = path;
this.$refs.deleteModal.show();
},
deleteApplication() { deleteApplication() {
this.$refs.deleteForm.submit(); this.$refs.deleteForm.submit();
}, },
...@@ -54,24 +58,22 @@ export default { ...@@ -54,24 +58,22 @@ export default {
}; };
</script> </script>
<template> <template>
<div> <gl-modal
<gl-button v-gl-modal="modalId" variant="danger">{{ $options.i18n.destroy }}</gl-button> ref="deleteModal"
<gl-modal :title="$options.i18n.title"
:title="$options.i18n.title" :action-primary="$options.modal.actionPrimary"
:action-primary="$options.modal.actionPrimary" :action-secondary="$options.modal.actionSecondary"
:action-secondary="$options.modal.actionSecondary" modal-id="delete-application-modal"
:modal-id="modalId" size="sm"
size="sm" @primary="deleteApplication"
@primary="deleteApplication" ><gl-sprintf :message="$options.i18n.body">
><gl-sprintf :message="$options.i18n.body"> <template #application>
<template #application> <strong>{{ name }}</strong>
<strong>{{ name }}</strong> </template></gl-sprintf
</template></gl-sprintf >
> <form ref="deleteForm" method="post" :action="path">
<form ref="deleteForm" method="post" :action="path"> <input type="hidden" name="_method" value="delete" />
<input type="hidden" name="_method" value="delete" /> <input type="hidden" name="authenticity_token" :value="$options.csrf.token" />
<input type="hidden" name="authenticity_token" :value="$options.csrf.token" /> </form>
</form> </gl-modal>
</gl-modal>
</div>
</template> </template>
...@@ -2,24 +2,14 @@ import Vue from 'vue'; ...@@ -2,24 +2,14 @@ import Vue from 'vue';
import DeleteApplication from './components/delete_application.vue'; import DeleteApplication from './components/delete_application.vue';
export default () => { export default () => {
const elements = document.querySelectorAll('.js-application-delete-button'); const el = document.querySelector('.js-application-delete-modal');
if (!elements) { if (!el) return false;
return false;
}
return elements.forEach((el) => { return new Vue({
const { path, name } = el.dataset; el,
render(h) {
return new Vue({ return h(DeleteApplication);
el, },
provide: {
path,
name,
},
render(h) {
return h(DeleteApplication);
},
});
}); });
}; };
.js-application-delete-button{ data: { path: admin_application_path(application), name: application.name } }
- submit_btn_css ||= 'gl-button btn btn-danger btn-sm js-application-delete-button'
%button{ class: submit_btn_css, data: { path: admin_application_path(application), name: application.name } }
= _('Destroy')
...@@ -33,3 +33,5 @@ ...@@ -33,3 +33,5 @@
%td= render 'delete_form', application: application %td= render 'delete_form', application: application
= paginate @applications, theme: 'gitlab' = paginate @applications, theme: 'gitlab'
.js-application-delete-modal
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DeleteApplication the modal component form renders with action and method 1`] = ` exports[`DeleteApplication the modal component form matches the snapshot 1`] = `
<form <form
action="application/path/1" action="application/path/1"
method="post" method="post"
......
import { GlButton, GlModal, GlSprintf } from '@gitlab/ui'; import { GlModal, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import DeleteApplication from '~/admin/applications/components/delete_application.vue'; import DeleteApplication from '~/admin/applications/components/delete_application.vue';
const modalID = 'fake-id';
const path = 'application/path/1'; const path = 'application/path/1';
const name = 'Application name'; const name = 'Application name';
jest.mock('lodash/uniqueId', () => () => 'fake-id');
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' })); jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
describe('DeleteApplication', () => { describe('DeleteApplication', () => {
...@@ -15,24 +12,20 @@ describe('DeleteApplication', () => { ...@@ -15,24 +12,20 @@ describe('DeleteApplication', () => {
const createComponent = () => { const createComponent = () => {
wrapper = shallowMount(DeleteApplication, { wrapper = shallowMount(DeleteApplication, {
provide: {
path,
name,
},
directives: {
GlModal: createMockDirective(),
},
stubs: { stubs: {
GlSprintf, GlSprintf,
}, },
}); });
}; };
const findButton = () => wrapper.findComponent(GlButton);
const findModal = () => wrapper.findComponent(GlModal); const findModal = () => wrapper.findComponent(GlModal);
const findForm = () => wrapper.find('form'); const findForm = () => wrapper.find('form');
beforeEach(() => { beforeEach(() => {
setFixtures(`
<button class="js-application-delete-button" data-path="${path}" data-name="${name}">Destroy</button>
`);
createComponent(); createComponent();
}); });
...@@ -40,22 +33,12 @@ describe('DeleteApplication', () => { ...@@ -40,22 +33,12 @@ describe('DeleteApplication', () => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('the button component', () => { describe('the modal component', () => {
it('displays the destroy button', () => { beforeEach(() => {
const button = findButton(); wrapper.vm.$refs.deleteModal.show = jest.fn();
document.querySelector('.js-application-delete-button').click();
expect(button.exists()).toBe(true);
expect(button.text()).toBe('Destroy');
});
it('contains the correct modal ID', () => {
const buttonModalId = getBinding(findButton().element, 'gl-modal').value;
expect(buttonModalId).toBe(modalID);
}); });
});
describe('the modal component', () => {
it('displays the modal component', () => { it('displays the modal component', () => {
const modal = findModal(); const modal = findModal();
...@@ -64,10 +47,6 @@ describe('DeleteApplication', () => { ...@@ -64,10 +47,6 @@ describe('DeleteApplication', () => {
expect(modal.text()).toBe(`Are you sure that you want to destroy ${name}`); expect(modal.text()).toBe(`Are you sure that you want to destroy ${name}`);
}); });
it('contains the correct modal ID', () => {
expect(findModal().props('modalId')).toBe(modalID);
});
describe('form', () => { describe('form', () => {
it('matches the snapshot', () => { it('matches the snapshot', () => {
expect(findForm().element).toMatchSnapshot(); expect(findForm().element).toMatchSnapshot();
......
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