Commit 6b4cd07d authored by Brandon Labuschagne's avatar Brandon Labuschagne

Replace applications destroy alert with GlModal

This commit refactors the applications destroy alert
to make use of the GlModal instead of the browser
built in alert.

Changelog: fixed
parent bccd75c8
<script>
import { uniqueId } from 'lodash';
import { GlButton, GlModal, GlModalDirective, GlSprintf } from '@gitlab/ui';
import { __ } from '~/locale';
import csrf from '~/lib/utils/csrf';
export default {
components: {
GlButton,
GlModal,
GlSprintf,
},
directives: {
GlModal: GlModalDirective,
},
inject: {
name: {
default: '',
},
path: {
default: '',
},
},
data() {
return {
modalId: uniqueId('application-delete-button-'),
};
},
methods: {
deleteApplication() {
this.$refs.deleteForm.submit();
},
},
i18n: {
destroy: __('Destroy'),
title: __('Confirm destroy application'),
body: __('Are you sure that you want to destroy %{application}'),
},
modal: {
actionPrimary: {
text: __('Destroy'),
attributes: {
variant: 'danger',
},
},
actionSecondary: {
text: __('Cancel'),
attributes: {
variant: 'default',
},
},
},
csrf,
};
</script>
<template>
<div>
<gl-button v-gl-modal="modalId" variant="danger">{{ $options.i18n.destroy }}</gl-button>
<gl-modal
:title="$options.i18n.title"
:action-primary="$options.modal.actionPrimary"
:action-secondary="$options.modal.actionSecondary"
:modal-id="modalId"
size="sm"
@primary="deleteApplication"
><gl-sprintf :message="$options.i18n.body">
<template #application>
<strong>{{ name }}</strong>
</template></gl-sprintf
>
<form ref="deleteForm" method="post" :action="path">
<input type="hidden" name="_method" value="delete" />
<input type="hidden" name="authenticity_token" :value="$options.csrf.token" />
</form>
</gl-modal>
</div>
</template>
import Vue from 'vue';
import DeleteApplication from './components/delete_application.vue';
export default () => {
const elements = document.querySelectorAll('.js-application-delete-button');
if (!elements) {
return false;
}
return elements.forEach((el) => {
const { path, name } = el.dataset;
return new Vue({
el,
provide: {
path,
name,
},
render(h) {
return h(DeleteApplication);
},
});
});
};
import initApplicationDeleteButtons from '~/admin/applications';
initApplicationDeleteButtons();
- submit_btn_css ||= 'gl-button btn btn-danger btn-sm'
= form_tag admin_application_path(application) do
%input{ :name => "_method", :type => "hidden", :value => "delete" }/
= submit_tag 'Destroy', class: submit_btn_css, data: { confirm: _('Are you sure?') }
.js-application-delete-button{ data: { path: admin_application_path(application), name: application.name } }
......@@ -4696,6 +4696,9 @@ msgstr ""
msgid "Are you sure that you want to archive this project?"
msgstr ""
msgid "Are you sure that you want to destroy %{application}"
msgstr ""
msgid "Are you sure that you want to unarchive this project?"
msgstr ""
......@@ -9281,6 +9284,9 @@ msgstr ""
msgid "Confirm approval"
msgstr ""
msgid "Confirm destroy application"
msgstr ""
msgid "Confirm new password"
msgstr ""
......
import { GlButton, GlModal, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import DeleteApplication from '~/admin/applications/components/delete_application.vue';
const modalID = 'fake-id';
const path = 'application/path/1';
const name = 'Application name';
jest.mock('lodash/uniqueId', () => () => 'fake-id');
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
describe('DeleteApplication', () => {
let wrapper;
const createComponent = () => {
wrapper = shallowMount(DeleteApplication, {
provide: {
path,
name,
},
directives: {
GlModal: createMockDirective(),
},
stubs: {
GlSprintf,
},
});
};
const findButton = () => wrapper.findComponent(GlButton);
const findModal = () => wrapper.findComponent(GlModal);
const findForm = () => wrapper.find('form');
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
describe('the button component', () => {
it('displays the destroy button', () => {
const button = findButton();
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', () => {
const modal = findModal();
expect(modal.exists()).toBe(true);
expect(modal.props('title')).toBe('Confirm destroy application');
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', () => {
it('renders with action and method', () => {
expect(findForm().attributes()).toEqual({
action: path,
method: 'post',
});
});
it('contains the correct form data', () => {
const formData = wrapper.findAll('input').wrappers.reduce(
(acc, input) => ({
...acc,
[input.element.name]: input.element.value,
}),
{},
);
expect(formData).toEqual({
_method: 'delete',
authenticity_token: 'mock-csrf-token',
});
});
describe('form submission', () => {
let formSubmitSpy;
beforeEach(() => {
formSubmitSpy = jest.spyOn(wrapper.vm.$refs.deleteForm, 'submit');
findModal().vm.$emit('primary');
});
it('submits the form on the modal primary action', () => {
expect(formSubmitSpy).toHaveBeenCalled();
});
});
});
});
});
......@@ -5,7 +5,7 @@ RSpec.shared_examples 'manage applications' do
let_it_be(:application_name_changed) { "#{application_name} changed" }
let_it_be(:application_redirect_uri) { 'https://foo.bar' }
it 'allows user to manage applications' do
it 'allows user to manage applications', :js do
visit new_application_path
expect(page).to have_content 'Add new application'
......
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