summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Kipling <nkipling@gitlab.com>2019-07-26 19:42:49 +0100
committerNathan Friend <nathan@gitlab.com>2019-07-30 13:49:49 -0300
commit918e7d43dfe614475ee2dd2b6f4c765726db6ef4 (patch)
treee76709006133602ecd904827346e21021d8a37da
parenta37d672ff5c4c102a5b507ad744919748cbdcb34 (diff)
downloadgitlab-ce-918e7d43dfe614475ee2dd2b6f4c765726db6ef4.tar.gz
Reworked how deletion works with multi vs single
Single deletion no longer requires a prop Modal description is now generated on demand Added dedicated functions for deleting Updated tests to match new function naming Updated css class name to be more specific
-rw-r--r--app/assets/javascripts/registry/components/table_registry.vue99
-rw-r--r--app/assets/javascripts/registry/stores/actions.js3
-rw-r--r--app/assets/stylesheets/pages/container_registry.scss2
-rw-r--r--spec/javascripts/registry/components/table_registry_spec.js8
4 files changed, 59 insertions, 53 deletions
diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue
index 90d6a4ce27f..df665b36c4b 100644
--- a/app/assets/javascripts/registry/components/table_registry.vue
+++ b/app/assets/javascripts/registry/components/table_registry.vue
@@ -41,6 +41,7 @@ export default {
itemsToBeDeleted: [],
modalId: `confirm-image-deletion-modal-${this.repo.id}`,
selectAllChecked: false,
+ modalDescription: '',
};
},
computed: {
@@ -54,67 +55,68 @@ export default {
return n__(
'ContainerRegistry|Remove image',
'ContainerRegistry|Remove images',
- this.singleItemSelected ? 1 : this.itemsToBeDeleted.length,
+ this.itemsToBeDeleted.length === 0 ? 1 : this.itemsToBeDeleted.length,
);
},
- modalDescription() {
- const selectedCount = this.itemsToBeDeleted.length;
-
- if (this.singleItemSelected) {
- // Attempt to pull 'single' property if it's an object in this.itemsToBeDeleted
- // Otherwise, simply use the int value of the selected row
- const { single: itemIndex = this.itemsToBeDeleted[0] } = this.itemsToBeDeleted[0];
+ },
+ methods: {
+ ...mapActions(['fetchList', 'deleteItem', 'multiDeleteItems']),
+ setModalDescription(itemsToDeleteLength, itemIndex) {
+ if (itemsToDeleteLength) {
+ this.modalDescription = sprintf(
+ s__(`ContainerRegistry|You are about to delete <b>%{count}</b> images. This will
+ delete the images and all tags pointing to them.`),
+ { count: itemsToDeleteLength },
+ );
+ } else {
const { tag } = this.repo.list[itemIndex];
- return sprintf(
+ this.modalDescription = sprintf(
s__(`ContainerRegistry|You are about to delete the image <b>%{title}</b>. This will
- delete the image and all tags pointing to this image.`),
+ delete the image and all tags pointing to this image.`),
{ title: `${this.repo.name}:${tag}` },
);
}
-
- return sprintf(
- s__(`ContainerRegistry|You are about to delete <b>%{count}</b> images. This will
- delete the images and all tags pointing to them.`),
- { count: selectedCount },
- );
- },
- singleItemSelected() {
- return this.findSingleRowToDelete || this.itemsToBeDeleted.length === 1;
},
- findSingleRowToDelete() {
- return this.itemsToBeDeleted.find(x => x.single !== undefined);
- },
- },
- methods: {
- ...mapActions(['fetchList', 'deleteItems']),
layers(item) {
return item.layers ? n__('%d layer', '%d layers', item.layers) : '';
},
formatSize(size) {
return numberToHumanSize(size);
},
- addSingleItemToBeDeleted(index) {
- this.itemsToBeDeleted.push({ single: index });
+ removeModalEvents() {
+ this.$refs.deleteModal.$refs.modal.$off('ok');
+ this.$refs.deleteModal.$refs.modal.$off('hide');
},
- removeSingleItemToBeDeleted() {
- const singleIndex = this.itemsToBeDeleted.findIndex(x => x.single !== undefined);
+ deleteSingleItem(index) {
+ this.setModalDescription(0, index);
- if (singleIndex > -1) {
- this.itemsToBeDeleted.splice(singleIndex, 1);
- }
- },
- handleDeleteRegistry() {
- let { itemsToBeDeleted } = this;
+ this.$refs.deleteModal.$refs.modal.$once('ok', () => {
+ this.removeModalEvents();
+ this.handleSingleDelete(this.repo.list[index]);
+ });
- if (this.findSingleRowToDelete) {
- itemsToBeDeleted = [this.findSingleRowToDelete.single];
- }
+ this.$refs.deleteModal.$refs.modal.$once('hide', this.removeModalEvents);
+ },
+ deleteMultipleItems() {
+ this.$refs.deleteModal.$refs.modal.$once('ok', () => {
+ this.removeModalEvents();
+ this.handleMultipleDelete();
+ });
+ this.$refs.deleteModal.$refs.modal.$once('hide', this.removeModalEvents);
+ },
+ handleSingleDelete(itemToDelete) {
+ this.deleteItem(itemToDelete)
+ .then(() => this.fetchList({ repo: this.repo }))
+ .catch(() => this.showError(errorMessagesTypes.DELETE_REGISTRY));
+ },
+ handleMultipleDelete() {
+ const { itemsToBeDeleted } = this;
this.itemsToBeDeleted = [];
if (this.bulkDeletePath) {
- this.deleteItems({
+ this.multiDeleteItems({
path: this.bulkDeletePath,
items: itemsToBeDeleted.map(x => this.repo.list[x].tag),
})
@@ -142,6 +144,7 @@ export default {
selectAll() {
this.itemsToBeDeleted = this.repo.list.map((x, index) => index);
this.selectAllChecked = true;
+ this.setModalDescription(this.itemsToBeDeleted.length);
},
deselectAll() {
this.itemsToBeDeleted = [];
@@ -160,6 +163,12 @@ export default {
this.selectAllChecked = true;
}
}
+
+ if (this.itemsToBeDeleted.length === 1) {
+ this.setModalDescription(0, this.itemsToBeDeleted[0]);
+ } else if (this.itemsToBeDeleted.length > 1) {
+ this.setModalDescription(this.itemsToBeDeleted.length);
+ }
},
},
};
@@ -191,13 +200,14 @@ export default {
variant="danger"
:title="s__('ContainerRegistry|Remove selected images')"
:aria-label="s__('ContainerRegistry|Remove selected images')"
+ @click="deleteMultipleItems()"
><icon name="remove"
/></gl-button>
</th>
</tr>
</thead>
<tbody>
- <tr v-for="(item, index) in repo.list" :key="item.tag" class="image-row">
+ <tr v-for="(item, index) in repo.list" :key="item.tag" class="registry-image-row">
<td class="check">
<gl-form-checkbox
v-if="item.canDelete"
@@ -242,7 +252,7 @@ export default {
:aria-label="s__('ContainerRegistry|Remove image')"
variant="danger"
class="js-delete-registry-row float-right btn-inverted btn-border-color btn-icon"
- @click="addSingleItemToBeDeleted(index)"
+ @click="deleteSingleItem(index)"
>
<icon name="remove" />
</gl-button>
@@ -257,12 +267,7 @@ export default {
:page-info="repo.pagination"
/>
- <gl-modal
- :modal-id="modalId"
- ok-variant="danger"
- @ok="handleDeleteRegistry"
- @cancel="removeSingleItemToBeDeleted"
- >
+ <gl-modal ref="deleteModal" :modal-id="modalId" ok-variant="danger">
<template v-slot:modal-title>{{ modalTitle }}</template>
<template v-slot:modal-ok>{{ s__('ContainerRegistry|Remove image(s) and tags') }}</template>
<p v-html="modalDescription"></p>
diff --git a/app/assets/javascripts/registry/stores/actions.js b/app/assets/javascripts/registry/stores/actions.js
index 4c20c003c5a..a2e0130e79e 100644
--- a/app/assets/javascripts/registry/stores/actions.js
+++ b/app/assets/javascripts/registry/stores/actions.js
@@ -36,7 +36,8 @@ export const fetchList = ({ commit }, { repo, page }) => {
};
export const deleteItem = (_, item) => axios.delete(item.destroyPath);
-export const deleteItems = (_, { path, items }) => axios.delete(path, { params: { ids: items } });
+export const multiDeleteItems = (_, { path, items }) =>
+ axios.delete(path, { params: { ids: items } });
export const setMainEndpoint = ({ commit }, data) => commit(types.SET_MAIN_ENDPOINT, data);
export const toggleLoading = ({ commit }) => commit(types.TOGGLE_MAIN_LOADING);
diff --git a/app/assets/stylesheets/pages/container_registry.scss b/app/assets/stylesheets/pages/container_registry.scss
index 6e6a3a1a394..0f4bdb219a3 100644
--- a/app/assets/stylesheets/pages/container_registry.scss
+++ b/app/assets/stylesheets/pages/container_registry.scss
@@ -32,7 +32,7 @@
.table.tags {
margin-bottom: 0;
- .image-row {
+ .registry-image-row {
.check {
padding-right: $gl-padding;
width: 5%;
diff --git a/spec/javascripts/registry/components/table_registry_spec.js b/spec/javascripts/registry/components/table_registry_spec.js
index 2ca7aa30c0e..b6a37abe4f7 100644
--- a/spec/javascripts/registry/components/table_registry_spec.js
+++ b/spec/javascripts/registry/components/table_registry_spec.js
@@ -101,7 +101,7 @@ describe('table registry', () => {
expect(findDeleteBtn().disabled).toBe(false);
findDeleteBtn().click();
- spyOn(vm, 'deleteItems').and.returnValue(Promise.resolve());
+ spyOn(vm, 'multiDeleteItems').and.returnValue(Promise.resolve());
Vue.nextTick(() => {
const modal = confirmationModal();
@@ -111,7 +111,7 @@ describe('table registry', () => {
Vue.nextTick(() => {
expect(vm.itemsToBeDeleted).toEqual([]);
- expect(vm.deleteItems).toHaveBeenCalledWith({
+ expect(vm.multiDeleteItems).toHaveBeenCalledWith({
path: bulkDeletePath,
items: [firstImage.tag, secondImage.tag],
});
@@ -142,13 +142,13 @@ describe('table registry', () => {
expect(vm.itemsToBeDeleted).toEqual([0]);
expect(findDeleteBtn().disabled).toBe(false);
findDeleteBtn().click();
- spyOn(vm, 'deleteItems').and.returnValue(Promise.resolve());
+ spyOn(vm, 'multiDeleteItems').and.returnValue(Promise.resolve());
Vue.nextTick(() => {
confirmationModal('.btn-danger').click();
expect(vm.itemsToBeDeleted).toEqual([]);
- expect(vm.deleteItems).toHaveBeenCalledWith({
+ expect(vm.multiDeleteItems).toHaveBeenCalledWith({
path: bulkDeletePath,
items: [firstImage.tag],
});