Commit c6517f5b authored by Jiaan Louw's avatar Jiaan Louw Committed by Andrew Fontaine

Refator project delete modal to use Vue

This replaces the project delete modal and button
with a Vue component in preperation for future
changes to the modal.
parent 9e145ff9
...@@ -7,11 +7,13 @@ import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory'; ...@@ -7,11 +7,13 @@ import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory';
import initFilePickers from '~/file_pickers'; import initFilePickers from '~/file_pickers';
import initProjectLoadingSpinner from '../shared/save_project_loader'; import initProjectLoadingSpinner from '../shared/save_project_loader';
import initProjectPermissionsSettings from '../shared/permissions'; import initProjectPermissionsSettings from '../shared/permissions';
import initProjectRemoveModal from '~/projects/project_remove_modal';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initFilePickers(); initFilePickers();
initConfirmDangerModal(); initConfirmDangerModal();
initSettingsPanels(); initSettingsPanels();
initProjectRemoveModal();
mountBadgeSettings(PROJECT_BADGE); mountBadgeSettings(PROJECT_BADGE);
initProjectLoadingSpinner(); initProjectLoadingSpinner();
......
<script>
import { GlModal, GlModalDirective, GlSprintf, GlFormInput, GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import { rstrip } from '~/lib/utils/common_utils';
import csrf from '~/lib/utils/csrf';
export default {
components: {
GlModal,
GlSprintf,
GlFormInput,
GlButton,
},
directives: {
GlModal: GlModalDirective,
},
props: {
confirmPhrase: {
type: String,
required: true,
},
warningMessage: {
type: String,
required: true,
},
formPath: {
type: String,
required: true,
},
},
data() {
return {
userInput: null,
};
},
computed: {
buttonDisabled() {
return rstrip(this.userInput) !== this.confirmPhrase;
},
csrfToken() {
return csrf.token;
},
},
methods: {
submitForm() {
this.$refs.form.submit();
},
},
strings: {
removeProject: __('Remove project'),
title: __('Confirmation required'),
confirm: __('Confirm'),
dataLoss: __(
'This action can lead to data loss. To prevent accidental actions we ask you to confirm your intention.',
),
confirmText: __('Please type %{phrase_code} to proceed or close this modal to cancel.'),
},
modalId: 'remove-project-modal',
};
</script>
<template>
<form ref="form" :action="formPath" method="post">
<input type="hidden" name="_method" value="delete" />
<input :value="csrfToken" type="hidden" name="authenticity_token" />
<gl-button v-gl-modal="$options.modalId" category="primary" variant="danger">{{
$options.strings.removeProject
}}</gl-button>
<gl-modal
ref="removeModal"
:modal-id="$options.modalId"
size="sm"
ok-variant="danger"
footer-class="bg-gray-light gl-p-5"
>
<template #modal-title>{{ $options.strings.title }}</template>
<template #modal-footer>
<div class="gl-w-full gl-display-flex gl-just-content-start gl-m-0">
<gl-button
:disabled="buttonDisabled"
category="primary"
variant="danger"
@click="submitForm"
>
{{ $options.strings.confirm }}
</gl-button>
</div>
</template>
<div>
<p class="gl-text-red-500 gl-font-weight-bold">{{ warningMessage }}</p>
<p class="gl-mb-0">{{ $options.strings.dataLoss }}</p>
<p>
<gl-sprintf :message="$options.strings.confirmText">
<template #phrase_code>
<code>{{ confirmPhrase }}</code>
</template>
</gl-sprintf>
</p>
<gl-form-input
id="confirm_name_input"
v-model="userInput"
name="confirm_name_input"
type="text"
/>
</div>
</gl-modal>
</form>
</template>
import Vue from 'vue';
import RemoveProjectModal from './components/remove_modal.vue';
export default (selector = '#js-confirm-project-remove') => {
const el = document.querySelector(selector);
if (!el) return;
const { formPath, confirmPhrase, warningMessage } = el.dataset;
// eslint-disable-next-line no-new
new Vue({
el,
render(createElement) {
return createElement(RemoveProjectModal, {
props: {
confirmPhrase,
warningMessage,
formPath,
},
});
},
});
};
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
%h4.danger-title= _('Remove project') %h4.danger-title= _('Remove project')
%p %p
%strong= _('Removing the project will delete its repository and all related resources including issues, merge requests etc.') %strong= _('Removing the project will delete its repository and all related resources including issues, merge requests etc.')
= form_tag(project_path(project), method: :delete) do %p
%p %strong= _('Removed projects cannot be restored!')
%strong= _('Removed projects cannot be restored!') #js-confirm-project-remove{ data: { form_path: project_path(project), confirm_phrase: project.path, warning_message: remove_project_message(project) } }
= button_to _('Remove project'), '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(project) }
...@@ -6,9 +6,9 @@ ...@@ -6,9 +6,9 @@
= render 'projects/settings/marked_for_removal' = render 'projects/settings/marked_for_removal'
%p %p
%strong= _('Removing the project will delete its repository and all related resources including issues, merge requests etc.') %strong= _('Removing the project will delete its repository and all related resources including issues, merge requests etc.')
= form_tag(project_path(project), method: :delete) do %p
%p %strong= _('Removed projects cannot be restored!')
%strong= _('Removed projects cannot be restored!') #js-confirm-project-remove{ data: { form_path: project_path(project), confirm_phrase: project.path, warning_message: remove_project_message(project) } }
= button_to _('Remove project'), '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(project) }
- else - else
= render 'projects/settings/restore', project: project = render 'projects/settings/restore', project: project
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Project remove modal initialized matches the snapshot 1`] = `
<form
action="some/path"
method="post"
>
<input
name="_method"
type="hidden"
value="delete"
/>
<input
name="authenticity_token"
type="hidden"
/>
<b-button-stub
class="[object Object]"
event="click"
role="button"
routertag="a"
size="md"
tabindex="0"
tag="button"
type="button"
variant="danger"
>
<!---->
<!---->
<span
class="gl-button-text"
>
Remove project
</span>
</b-button-stub>
<b-modal-stub
canceltitle="Cancel"
cancelvariant="secondary"
footerclass="bg-gray-light gl-p-5"
headerclosecontent="&times;"
headercloselabel="Close"
id="remove-project-modal"
ignoreenforcefocusselector=""
lazy="true"
modalclass="gl-modal,"
oktitle="OK"
okvariant="danger"
size="sm"
title=""
titletag="h4"
>
<div>
<p
class="gl-text-red-500 gl-font-weight-bold"
>
This can lead to data loss.
</p>
<p
class="gl-mb-0"
>
This action can lead to data loss. To prevent accidental actions we ask you to confirm your intention.
</p>
<p>
<gl-sprintf-stub
message="Please type %{phrase_code} to proceed or close this modal to cancel."
/>
</p>
<gl-form-input-stub
id="confirm_name_input"
name="confirm_name_input"
type="text"
/>
</div>
<template />
<template>
Confirmation required
</template>
<template />
<template />
<template />
<template>
<div
class="gl-w-full gl-display-flex gl-just-content-start gl-m-0"
>
<b-button-stub
class="[object Object]"
disabled="true"
event="click"
routertag="a"
size="md"
tag="button"
type="button"
variant="danger"
>
<!---->
<!---->
<span
class="gl-button-text"
>
Confirm
</span>
</b-button-stub>
</div>
</template>
</b-modal-stub>
</form>
`;
import { shallowMount } from '@vue/test-utils';
import { GlButton, GlModal } from '@gitlab/ui';
import ProjectRemoveModal from '~/projects/components/remove_modal.vue';
describe('Project remove modal', () => {
let wrapper;
const findFormElement = () => wrapper.find('form').element;
const findConfirmButton = () => wrapper.find(GlModal).find(GlButton);
const defaultProps = {
formPath: 'some/path',
confirmPhrase: 'foo',
warningMessage: 'This can lead to data loss.',
};
const createComponent = (data = {}) => {
wrapper = shallowMount(ProjectRemoveModal, {
propsData: defaultProps,
data: () => data,
stubs: {
GlButton,
GlModal,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('initialized', () => {
beforeEach(() => {
createComponent();
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
describe('user input matches the confirmPhrase', () => {
beforeEach(() => {
createComponent({ userInput: defaultProps.confirmPhrase });
});
it('the confirm button is not dislabled', () => {
expect(findConfirmButton().attributes('disabled')).toBe(undefined);
});
describe('and when the confirmation button is clicked', () => {
beforeEach(() => {
findConfirmButton().vm.$emit('click');
});
it('submits the form element', () => {
expect(findFormElement().submit).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