Commit a3254abf authored by Frédéric Caplette's avatar Frédéric Caplette

Merge branch '336934-improved-ux-for-bulk-deleting-container-image-tags' into 'master'

Enable bulk delete in container registry tags list

See merge request gitlab-org/gitlab!75655
parents 7eb2f2d7 edc3beb1
<script> <script>
import { GlButton, GlKeysetPagination } from '@gitlab/ui';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { n__ } from '~/locale';
import { joinPaths } from '~/lib/utils/url_utility'; import { joinPaths } from '~/lib/utils/url_utility';
import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
import { import {
REMOVE_TAGS_BUTTON_TITLE, REMOVE_TAGS_BUTTON_TITLE,
TAGS_LIST_TITLE, TAGS_LIST_TITLE,
...@@ -16,11 +17,10 @@ import TagsLoader from './tags_loader.vue'; ...@@ -16,11 +17,10 @@ import TagsLoader from './tags_loader.vue';
export default { export default {
name: 'TagsList', name: 'TagsList',
components: { components: {
GlButton,
GlKeysetPagination,
TagsListRow, TagsListRow,
EmptyState, EmptyState,
TagsLoader, TagsLoader,
RegistryList,
}, },
inject: ['config'], inject: ['config'],
props: { props: {
...@@ -61,11 +61,13 @@ export default { ...@@ -61,11 +61,13 @@ export default {
}, },
data() { data() {
return { return {
selectedItems: {},
containerRepository: {}, containerRepository: {},
}; };
}, },
computed: { computed: {
listTitle() {
return n__('%d tag', '%d tags', this.tags.length);
},
tags() { tags() {
return this.containerRepository?.tags?.nodes || []; return this.containerRepository?.tags?.nodes || [];
}, },
...@@ -78,18 +80,9 @@ export default { ...@@ -78,18 +80,9 @@ export default {
first: GRAPHQL_PAGE_SIZE, first: GRAPHQL_PAGE_SIZE,
}; };
}, },
hasSelectedItems() {
return this.tags.some((tag) => this.selectedItems[tag.name]);
},
showMultiDeleteButton() { showMultiDeleteButton() {
return this.tags.some((tag) => tag.canDelete) && !this.isMobile; return this.tags.some((tag) => tag.canDelete) && !this.isMobile;
}, },
multiDeleteButtonIsDisabled() {
return !this.hasSelectedItems || this.disabled;
},
showPagination() {
return this.tagsPageInfo.hasPreviousPage || this.tagsPageInfo.hasNextPage;
},
hasNoTags() { hasNoTags() {
return this.tags.length === 0; return this.tags.length === 0;
}, },
...@@ -98,19 +91,13 @@ export default { ...@@ -98,19 +91,13 @@ export default {
}, },
}, },
methods: { methods: {
updateSelectedItems(name) {
this.$set(this.selectedItems, name, !this.selectedItems[name]);
},
mapTagsToBeDleeted(items) {
return this.tags.filter((tag) => items[tag.name]);
},
fetchNextPage() { fetchNextPage() {
this.$apollo.queries.containerRepository.fetchMore({ this.$apollo.queries.containerRepository.fetchMore({
variables: { variables: {
after: this.tagsPageInfo?.endCursor, after: this.tagsPageInfo?.endCursor,
first: GRAPHQL_PAGE_SIZE, first: GRAPHQL_PAGE_SIZE,
}, },
updateQuery(previousResult, { fetchMoreResult }) { updateQuery(_, { fetchMoreResult }) {
return fetchMoreResult; return fetchMoreResult;
}, },
}); });
...@@ -122,7 +109,7 @@ export default { ...@@ -122,7 +109,7 @@ export default {
before: this.tagsPageInfo?.startCursor, before: this.tagsPageInfo?.startCursor,
last: GRAPHQL_PAGE_SIZE, last: GRAPHQL_PAGE_SIZE,
}, },
updateQuery(previousResult, { fetchMoreResult }) { updateQuery(_, { fetchMoreResult }) {
return fetchMoreResult; return fetchMoreResult;
}, },
}); });
...@@ -137,42 +124,27 @@ export default { ...@@ -137,42 +124,27 @@ export default {
<template v-else> <template v-else>
<empty-state v-if="hasNoTags" :no-containers-image="config.noContainersImage" /> <empty-state v-if="hasNoTags" :no-containers-image="config.noContainersImage" />
<template v-else> <template v-else>
<div class="gl-display-flex gl-justify-content-space-between gl-mb-3"> <registry-list
<h5 data-testid="list-title"> :title="listTitle"
{{ $options.i18n.TAGS_LIST_TITLE }} :pagination="tagsPageInfo"
</h5> :items="tags"
id-property="name"
<gl-button @prev-page="fetchPreviousPage"
v-if="showMultiDeleteButton" @next-page="fetchNextPage"
:disabled="multiDeleteButtonIsDisabled" @delete="$emit('delete', $event)"
category="secondary" >
variant="danger" <template #default="{ selectItem, isSelected, item, first }">
@click="$emit('delete', mapTagsToBeDleeted(selectedItems))" <tags-list-row
> :tag="item"
{{ $options.i18n.REMOVE_TAGS_BUTTON_TITLE }} :first="first"
</gl-button> :selected="isSelected(item)"
</div> :is-mobile="isMobile"
<tags-list-row :disabled="disabled"
v-for="(tag, index) in tags" @select="selectItem(item)"
:key="tag.path" @delete="$emit('delete', [item])"
:tag="tag" />
:first="index === 0" </template>
:selected="selectedItems[tag.name]" </registry-list>
:is-mobile="isMobile"
:disabled="disabled"
@select="updateSelectedItems(tag.name)"
@delete="$emit('delete', mapTagsToBeDleeted({ [tag.name]: true }))"
/>
<div class="gl-display-flex gl-justify-content-center">
<gl-keyset-pagination
v-if="showPagination"
:has-next-page="tagsPageInfo.hasNextPage"
:has-previous-page="tagsPageInfo.hasPreviousPage"
class="gl-mt-3"
@prev="fetchPreviousPage"
@next="fetchNextPage"
/>
</div>
</template> </template>
</template> </template>
</div> </div>
......
...@@ -82,7 +82,7 @@ RSpec.describe 'Container Registry', :js do ...@@ -82,7 +82,7 @@ RSpec.describe 'Container Registry', :js do
end end
it 'shows the image tags' do it 'shows the image tags' do
expect(page).to have_content 'Image tags' expect(page).to have_content '1 tag'
first_tag = first('[data-testid="name"]') first_tag = first('[data-testid="name"]')
expect(first_tag).to have_content 'latest' expect(first_tag).to have_content 'latest'
end end
......
...@@ -87,7 +87,7 @@ RSpec.describe 'Container Registry', :js do ...@@ -87,7 +87,7 @@ RSpec.describe 'Container Registry', :js do
end end
it 'shows the image tags' do it 'shows the image tags' do
expect(page).to have_content 'Image tags' expect(page).to have_content '20 tags'
first_tag = first('[data-testid="name"]') first_tag = first('[data-testid="name"]')
expect(first_tag).to have_content '1' expect(first_tag).to have_content '1'
end end
......
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