Commit a7d94cf2 authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch '229688-delete-account-modal' into 'master'

Update delete account modal

Closes #229688

See merge request gitlab-org/gitlab!37816
parents 9ff8e0be 241d76fd
<script> <script>
/* eslint-disable vue/no-v-html */ /* eslint-disable vue/no-v-html */
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; import { GlModal } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
export default { export default {
components: { components: {
DeprecatedModal, GlModal,
}, },
props: { props: {
actionUrl: { actionUrl: {
...@@ -55,21 +55,34 @@ You are about to permanently delete %{yourAccount}, and all of the issues, merge ...@@ -55,21 +55,34 @@ You are about to permanently delete %{yourAccount}, and all of the issues, merge
Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
{ {
yourAccount: `<strong>${s__('Profiles|your account')}</strong>`, yourAccount: `<strong>${s__('Profiles|your account')}</strong>`,
deleteAccount: `<strong>${s__('Profiles|Delete Account')}</strong>`, deleteAccount: `<strong>${s__('Profiles|Delete account')}</strong>`,
}, },
false, false,
); );
}, },
}, primaryProps() {
methods: { return {
text: s__('Delete account'),
attributes: [{ variant: 'danger' }, { category: 'primary' }, { disabled: !this.canSubmit }],
};
},
cancelProps() {
return {
text: s__('Cancel'),
};
},
canSubmit() { canSubmit() {
if (this.confirmWithPassword) { if (this.confirmWithPassword) {
return this.enteredPassword !== ''; return this.enteredPassword !== '';
} }
return this.enteredUsername === this.username; return this.enteredUsername === this.username;
}, },
},
methods: {
onSubmit() { onSubmit() {
if (!this.canSubmit) {
return;
}
this.$refs.form.submit(); this.$refs.form.submit();
}, },
}, },
...@@ -77,42 +90,39 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), ...@@ -77,42 +90,39 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
</script> </script>
<template> <template>
<deprecated-modal <gl-modal
id="delete-account-modal" modal-id="delete-account-modal"
:title="s__('Profiles|Delete your account?')" title="Profiles"
:text="text" :action-primary="primaryProps"
:primary-button-label="s__('Profiles|Delete account')" :action-cancel="cancelProps"
:submit-disabled="!canSubmit()" :ok-disabled="!canSubmit"
kind="danger" @primary="onSubmit"
@submit="onSubmit"
> >
<template #body="props"> <p v-html="text"></p>
<p v-html="props.text"></p>
<form ref="form" :action="actionUrl" method="post"> <form ref="form" :action="actionUrl" method="post">
<input type="hidden" name="_method" value="delete" /> <input type="hidden" name="_method" value="delete" />
<input :value="csrfToken" type="hidden" name="authenticity_token" /> <input :value="csrfToken" type="hidden" name="authenticity_token" />
<p id="input-label" v-html="inputLabel"></p> <p id="input-label" v-html="inputLabel"></p>
<input <input
v-if="confirmWithPassword" v-if="confirmWithPassword"
v-model="enteredPassword" v-model="enteredPassword"
name="password" name="password"
class="form-control" class="form-control"
type="password" type="password"
data-qa-selector="password_confirmation_field" data-qa-selector="password_confirmation_field"
aria-labelledby="input-label" aria-labelledby="input-label"
/> />
<input <input
v-else v-else
v-model="enteredUsername" v-model="enteredUsername"
name="username" name="username"
class="form-control" class="form-control"
type="text" type="text"
aria-labelledby="input-label" aria-labelledby="input-label"
/> />
</form> </form>
</template> </gl-modal>
</deprecated-modal>
</template> </template>
...@@ -30,6 +30,9 @@ export default () => { ...@@ -30,6 +30,9 @@ export default () => {
}, },
mounted() { mounted() {
deleteAccountButton.classList.remove('disabled'); deleteAccountButton.classList.remove('disabled');
deleteAccountButton.addEventListener('click', () => {
this.$root.$emit('bv::show::modal', 'delete-account-modal', '#delete-account-button');
});
}, },
render(createElement) { render(createElement) {
return createElement('delete-account-modal', { return createElement('delete-account-modal', {
......
...@@ -55,8 +55,8 @@ ...@@ -55,8 +55,8 @@
= s_('Profiles|Deleting an account has the following effects:') = s_('Profiles|Deleting an account has the following effects:')
= render 'users/deletion_guidance', user: current_user = render 'users/deletion_guidance', user: current_user
%button#delete-account-button.btn.btn-danger.disabled{ data: { toggle: 'modal', -# Delete button here
target: '#delete-account-modal', qa_selector: 'delete_account_button' } } %button#delete-account-button.btn.btn-danger.disabled{ data: { qa_selector: 'delete_account_button' } }
= s_('Profiles|Delete account') = s_('Profiles|Delete account')
#delete-account-modal{ data: { action_url: user_registration_path, #delete-account-modal{ data: { action_url: user_registration_path,
......
...@@ -7947,6 +7947,9 @@ msgstr "" ...@@ -7947,6 +7947,9 @@ msgstr ""
msgid "Delete Snippet" msgid "Delete Snippet"
msgstr "" msgstr ""
msgid "Delete account"
msgstr ""
msgid "Delete artifacts" msgid "Delete artifacts"
msgstr "" msgstr ""
...@@ -18724,15 +18727,9 @@ msgstr "" ...@@ -18724,15 +18727,9 @@ msgstr ""
msgid "Profiles|Default notification email" msgid "Profiles|Default notification email"
msgstr "" msgstr ""
msgid "Profiles|Delete Account"
msgstr ""
msgid "Profiles|Delete account" msgid "Profiles|Delete account"
msgstr "" msgstr ""
msgid "Profiles|Delete your account?"
msgstr ""
msgid "Profiles|Deleting an account has the following effects:" msgid "Profiles|Deleting an account has the following effects:"
msgstr "" msgstr ""
......
import Vue from 'vue'; import Vue from 'vue';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import mountComponent from 'helpers/vue_mount_component_helper'; import { merge } from 'lodash';
import { mount } from '@vue/test-utils';
import deleteAccountModal from '~/profile/account/components/delete_account_modal.vue'; import deleteAccountModal from '~/profile/account/components/delete_account_modal.vue';
const GlModalStub = {
name: 'gl-modal-stub',
template: `
<div>
<slot></slot>
</div>
`,
};
describe('DeleteAccountModal component', () => { describe('DeleteAccountModal component', () => {
const actionUrl = `${TEST_HOST}/delete/user`; const actionUrl = `${TEST_HOST}/delete/user`;
const username = 'hasnoname'; const username = 'hasnoname';
let Component; let wrapper;
let vm; let vm;
beforeEach(() => { const createWrapper = (options = {}) => {
Component = Vue.extend(deleteAccountModal); wrapper = mount(
}); deleteAccountModal,
merge(
{},
{
propsData: {
actionUrl,
username,
},
stubs: {
GlModal: GlModalStub,
},
},
options,
),
);
vm = wrapper.vm;
};
afterEach(() => { afterEach(() => {
vm.$destroy(); wrapper.destroy();
wrapper = null;
vm = null;
}); });
const findElements = () => { const findElements = () => {
...@@ -23,16 +51,16 @@ describe('DeleteAccountModal component', () => { ...@@ -23,16 +51,16 @@ describe('DeleteAccountModal component', () => {
return { return {
form: vm.$refs.form, form: vm.$refs.form,
input: vm.$el.querySelector(`[name="${confirmation}"]`), input: vm.$el.querySelector(`[name="${confirmation}"]`),
submitButton: vm.$el.querySelector('.btn-danger'),
}; };
}; };
const findModal = () => wrapper.find(GlModalStub);
describe('with password confirmation', () => { describe('with password confirmation', () => {
beforeEach(done => { beforeEach(done => {
vm = mountComponent(Component, { createWrapper({
actionUrl, propsData: {
confirmWithPassword: true, confirmWithPassword: true,
username, },
}); });
vm.isOpen = true; vm.isOpen = true;
...@@ -43,7 +71,7 @@ describe('DeleteAccountModal component', () => { ...@@ -43,7 +71,7 @@ describe('DeleteAccountModal component', () => {
}); });
it('does not accept empty password', done => { it('does not accept empty password', done => {
const { form, input, submitButton } = findElements(); const { form, input } = findElements();
jest.spyOn(form, 'submit').mockImplementation(() => {}); jest.spyOn(form, 'submit').mockImplementation(() => {});
input.value = ''; input.value = '';
input.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
...@@ -51,8 +79,8 @@ describe('DeleteAccountModal component', () => { ...@@ -51,8 +79,8 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick() Vue.nextTick()
.then(() => { .then(() => {
expect(vm.enteredPassword).toBe(input.value); expect(vm.enteredPassword).toBe(input.value);
expect(submitButton).toHaveAttr('disabled', 'disabled'); expect(findModal().attributes('ok-disabled')).toBe('true');
submitButton.click(); findModal().vm.$emit('primary');
expect(form.submit).not.toHaveBeenCalled(); expect(form.submit).not.toHaveBeenCalled();
}) })
...@@ -61,7 +89,7 @@ describe('DeleteAccountModal component', () => { ...@@ -61,7 +89,7 @@ describe('DeleteAccountModal component', () => {
}); });
it('submits form with password', done => { it('submits form with password', done => {
const { form, input, submitButton } = findElements(); const { form, input } = findElements();
jest.spyOn(form, 'submit').mockImplementation(() => {}); jest.spyOn(form, 'submit').mockImplementation(() => {});
input.value = 'anything'; input.value = 'anything';
input.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
...@@ -69,8 +97,8 @@ describe('DeleteAccountModal component', () => { ...@@ -69,8 +97,8 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick() Vue.nextTick()
.then(() => { .then(() => {
expect(vm.enteredPassword).toBe(input.value); expect(vm.enteredPassword).toBe(input.value);
expect(submitButton).not.toHaveAttr('disabled', 'disabled'); expect(findModal().attributes('ok-disabled')).toBeUndefined();
submitButton.click(); findModal().vm.$emit('primary');
expect(form.submit).toHaveBeenCalled(); expect(form.submit).toHaveBeenCalled();
}) })
...@@ -81,10 +109,10 @@ describe('DeleteAccountModal component', () => { ...@@ -81,10 +109,10 @@ describe('DeleteAccountModal component', () => {
describe('with username confirmation', () => { describe('with username confirmation', () => {
beforeEach(done => { beforeEach(done => {
vm = mountComponent(Component, { createWrapper({
actionUrl, propsData: {
confirmWithPassword: false, confirmWithPassword: false,
username, },
}); });
vm.isOpen = true; vm.isOpen = true;
...@@ -95,7 +123,7 @@ describe('DeleteAccountModal component', () => { ...@@ -95,7 +123,7 @@ describe('DeleteAccountModal component', () => {
}); });
it('does not accept wrong username', done => { it('does not accept wrong username', done => {
const { form, input, submitButton } = findElements(); const { form, input } = findElements();
jest.spyOn(form, 'submit').mockImplementation(() => {}); jest.spyOn(form, 'submit').mockImplementation(() => {});
input.value = 'this is wrong'; input.value = 'this is wrong';
input.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
...@@ -103,8 +131,8 @@ describe('DeleteAccountModal component', () => { ...@@ -103,8 +131,8 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick() Vue.nextTick()
.then(() => { .then(() => {
expect(vm.enteredUsername).toBe(input.value); expect(vm.enteredUsername).toBe(input.value);
expect(submitButton).toHaveAttr('disabled', 'disabled'); expect(findModal().attributes('ok-disabled')).toBe('true');
submitButton.click(); findModal().vm.$emit('primary');
expect(form.submit).not.toHaveBeenCalled(); expect(form.submit).not.toHaveBeenCalled();
}) })
...@@ -113,7 +141,7 @@ describe('DeleteAccountModal component', () => { ...@@ -113,7 +141,7 @@ describe('DeleteAccountModal component', () => {
}); });
it('submits form with correct username', done => { it('submits form with correct username', done => {
const { form, input, submitButton } = findElements(); const { form, input } = findElements();
jest.spyOn(form, 'submit').mockImplementation(() => {}); jest.spyOn(form, 'submit').mockImplementation(() => {});
input.value = username; input.value = username;
input.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input'));
...@@ -121,8 +149,8 @@ describe('DeleteAccountModal component', () => { ...@@ -121,8 +149,8 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick() Vue.nextTick()
.then(() => { .then(() => {
expect(vm.enteredUsername).toBe(input.value); expect(vm.enteredUsername).toBe(input.value);
expect(submitButton).not.toHaveAttr('disabled', 'disabled'); expect(findModal().attributes('ok-disabled')).toBeUndefined();
submitButton.click(); findModal().vm.$emit('primary');
expect(form.submit).toHaveBeenCalled(); expect(form.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