Commit 2391f1e8 authored by Peter Hegman's avatar Peter Hegman Committed by Nicolò Maria Mezzopera

Add confirmation modal to Deploy keys delete button

parent 900de4d9
<script>
import { GlTable, GlButton, GlPagination, GlLoadingIcon, GlEmptyState } from '@gitlab/ui';
import { GlTable, GlButton, GlPagination, GlLoadingIcon, GlEmptyState, GlModal } from '@gitlab/ui';
import { __ } from '~/locale';
import Api, { DEFAULT_PER_PAGE } from '~/api';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { cleanLeadingSeparator } from '~/lib/utils/url_utility';
import createFlash from '~/flash';
import csrf from '~/lib/utils/csrf';
export default {
name: 'DeployKeysTable',
......@@ -16,12 +17,16 @@ export default {
emptyStateDescription: __(
'Deploy keys grant read/write access to all repositories in your instance',
),
remove: __('Remove deploy key'),
delete: __('Delete deploy key'),
edit: __('Edit deploy key'),
pagination: {
next: __('Next'),
prev: __('Prev'),
},
modal: {
title: __('Are you sure?'),
body: __('Are you sure you want to delete this deploy key?'),
},
apiErrorMessage: __('An error occurred fetching the public deploy keys. Please try again.'),
},
fields: [
......@@ -48,6 +53,22 @@ export default {
thClass: 'gl-lg-w-1px gl-white-space-nowrap',
},
],
modal: {
id: 'delete-deploy-key-modal',
actionPrimary: {
text: __('Delete'),
attributes: {
variant: 'danger',
},
},
actionSecondary: {
text: __('Cancel'),
attributes: {
variant: 'default',
},
},
},
csrf,
DEFAULT_PER_PAGE,
components: {
GlTable,
......@@ -56,6 +77,7 @@ export default {
TimeAgoTooltip,
GlLoadingIcon,
GlEmptyState,
GlModal,
},
inject: ['editPath', 'deletePath', 'createPath', 'emptyStateSvgPath'],
data() {
......@@ -64,12 +86,21 @@ export default {
totalItems: 0,
loading: false,
items: [],
deployKeyToDelete: null,
};
},
computed: {
shouldShowTable() {
return this.totalItems !== 0 || this.loading;
},
isModalVisible() {
return this.deployKeyToDelete !== null;
},
deleteAction() {
return this.deployKeyToDelete === null
? null
: this.deletePath.replace(':id', this.deployKeyToDelete);
},
},
watch: {
page(newPage) {
......@@ -120,6 +151,15 @@ export default {
}
this.loading = false;
},
handleDeleteClick(id) {
this.deployKeyToDelete = id;
},
handleModalHide() {
this.deployKeyToDelete = null;
},
handleModalPrimary() {
this.$refs.modalForm.submit();
},
},
};
</script>
......@@ -175,7 +215,12 @@ export default {
:href="editHref(id)"
class="gl-mr-2"
/>
<gl-button variant="danger" icon="remove" :aria-label="$options.i18n.remove" />
<gl-button
variant="danger"
icon="remove"
:aria-label="$options.i18n.delete"
@click="handleDeleteClick(id)"
/>
</template>
</gl-table>
<gl-pagination
......@@ -196,5 +241,21 @@ export default {
:primary-button-text="$options.i18n.newDeployKeyButtonText"
:primary-button-link="createPath"
/>
<gl-modal
:modal-id="$options.modal.id"
:visible="isModalVisible"
:title="$options.i18n.modal.title"
:action-primary="$options.modal.actionPrimary"
:action-secondary="$options.modal.actionSecondary"
size="sm"
@hide="handleModalHide"
@primary="handleModalPrimary"
>
<form ref="modalForm" :action="deleteAction" method="post">
<input type="hidden" name="_method" value="delete" />
<input type="hidden" name="authenticity_token" :value="$options.csrf.token" />
</form>
{{ $options.i18n.modal.body }}
</gl-modal>
</div>
</template>
......@@ -4509,6 +4509,9 @@ msgstr ""
msgid "Are you sure you want to delete this SSH key?"
msgstr ""
msgid "Are you sure you want to delete this deploy key?"
msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
msgstr ""
......@@ -11189,6 +11192,9 @@ msgstr ""
msgid "Delete corpus"
msgstr ""
msgid "Delete deploy key"
msgstr ""
msgid "Delete file"
msgstr ""
......
import { merge } from 'lodash';
import { GlLoadingIcon, GlEmptyState, GlPagination } from '@gitlab/ui';
import { GlLoadingIcon, GlEmptyState, GlPagination, GlModal } from '@gitlab/ui';
import { nextTick } from 'vue';
import responseBody from 'test_fixtures/api/deploy_keys/index.json';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { stubComponent } from 'helpers/stub_component';
import DeployKeysTable from '~/admin/deploy_keys/components/table.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import Api, { DEFAULT_PER_PAGE } from '~/api';
......@@ -12,6 +13,7 @@ import createFlash from '~/flash';
jest.mock('~/api');
jest.mock('~/flash');
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
describe('DeployKeysTable', () => {
let wrapper;
......@@ -29,13 +31,23 @@ describe('DeployKeysTable', () => {
const createComponent = (provide = {}) => {
wrapper = mountExtended(DeployKeysTable, {
provide: merge({}, defaultProvide, provide),
stubs: {
GlModal: stubComponent(GlModal, {
template: `
<div>
<slot name="modal-title"></slot>
<slot></slot>
<slot name="modal-footer"></slot>
</div>`,
}),
},
});
};
const findEditButton = (index) =>
wrapper.findAllByLabelText(DeployKeysTable.i18n.edit, { selector: 'a' }).at(index);
const findRemoveButton = (index) =>
wrapper.findAllByLabelText(DeployKeysTable.i18n.remove, { selector: 'button' }).at(index);
wrapper.findAllByLabelText(DeployKeysTable.i18n.delete, { selector: 'button' }).at(index);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findTimeAgoTooltip = (index) => wrapper.findAllComponents(TimeAgoTooltip).at(index);
const findPagination = () => wrapper.findComponent(GlPagination);
......@@ -118,6 +130,27 @@ describe('DeployKeysTable', () => {
expectDeployKeyIsRendered(deployKey, 0);
expectDeployKeyIsRendered(deployKey2, 1);
});
describe('when delete button is clicked', () => {
it('asks user to confirm', async () => {
await findRemoveButton(0).trigger('click');
const modal = wrapper.findComponent(GlModal);
const form = modal.find('form');
const submitSpy = jest.spyOn(form.element, 'submit');
expect(modal.props('visible')).toBe(true);
expect(form.attributes('action')).toBe(`/admin/deploy_keys/${deployKey.id}`);
expect(form.find('input[name="_method"]').attributes('value')).toBe('delete');
expect(form.find('input[name="authenticity_token"]').attributes('value')).toBe(
'mock-csrf-token',
);
modal.vm.$emit('primary');
expect(submitSpy).toHaveBeenCalled();
});
});
});
describe('pagination', () => {
......
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