diff options
Diffstat (limited to 'app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue')
-rw-r--r-- | app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue | 277 |
1 files changed, 174 insertions, 103 deletions
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue index 3157653648b..96d097eff38 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue +++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue @@ -3,16 +3,20 @@ import { GlAlert, GlLink, GlTable, - GlDropdownItem, - GlDropdown, + GlDisclosureDropdown, + GlDisclosureDropdownItem, GlButton, GlFormCheckbox, GlLoadingIcon, GlModal, GlSprintf, + GlKeysetPagination, } from '@gitlab/ui'; +import * as Sentry from '@sentry/browser'; import { createAlert, VARIANT_SUCCESS, VARIANT_WARNING } from '~/alert'; +import { NEXT, PREV } from '~/vue_shared/components/pagination/constants'; import { numberToHumanSize } from '~/lib/utils/number_utils'; +import { scrollToElement } from '~/lib/utils/common_utils'; import { __, s__ } from '~/locale'; import FileSha from '~/packages_and_registries/package_registry/components/details/file_sha.vue'; import Tracking from '~/tracking'; @@ -47,12 +51,13 @@ export default { GlAlert, GlLink, GlTable, - GlDropdown, - GlDropdownItem, + GlDisclosureDropdown, + GlDisclosureDropdownItem, GlFormCheckbox, GlButton, GlLoadingIcon, GlModal, + GlKeysetPagination, GlSprintf, FileIcon, TimeAgoTooltip, @@ -94,10 +99,17 @@ export default { return this.queryVariables; }, update(data) { - return data.package?.packageFiles ?? {}; + return data.package?.packageFiles?.nodes ?? []; }, - error() { + result({ data }) { + const { packageFiles } = data?.package ?? {}; + if (packageFiles?.pageInfo) { + this.pageInfo = packageFiles.pageInfo; + } + }, + error(error) { this.fetchPackageFilesError = true; + Sentry.captureException(error); }, }, }, @@ -105,23 +117,21 @@ export default { return { fetchPackageFilesError: false, filesToDelete: [], - packageFiles: {}, + packageFiles: [], mutationLoading: false, selectedReferences: [], + pageInfo: {}, }; }, computed: { - files() { - return this.packageFiles?.nodes ?? []; - }, areFilesSelected() { return this.selectedReferences.length > 0; }, areAllFilesSelected() { - return this.files.length > 0 && this.files.every(this.isSelected); + return this.packageFiles.length > 0 && this.packageFiles.every(this.isSelected); }, filesTableRows() { - return this.files.map((pf) => ({ + return this.packageFiles.map((pf) => ({ ...pf, size: this.formatSize(pf.size), })); @@ -168,6 +178,10 @@ export default { first: GRAPHQL_PACKAGE_FILES_PAGE_SIZE, }; }, + showPagination() { + const { hasPreviousPage, hasNextPage } = this.pageInfo; + return hasPreviousPage || hasNextPage; + }, tracking() { return { category: packageTypeToTrackCategory(this.packageType), @@ -258,7 +272,7 @@ export default { }, handleFileDelete(files) { this.track(REQUEST_DELETE_PACKAGE_FILE_TRACKING_ACTION); - if (files.length === this.files.length && !this.packageFiles?.pageInfo?.hasNextPage) { + if (files.length === this.packageFiles.length && !this.pageInfo.hasNextPage) { this.$emit( 'delete-all-files', this.hasOneItem(files) @@ -281,6 +295,41 @@ export default { } this.deletePackageFiles(this.filesToDelete.map((file) => file.id)); }, + fetchPreviousFilesPage() { + return this.$apollo.queries.packageFiles + .fetchMore({ + variables: { + first: null, + last: GRAPHQL_PACKAGE_FILES_PAGE_SIZE, + before: this.pageInfo.startCursor, + }, + }) + .then(() => { + this.scrollAndFocus(); + }); + }, + fetchNextFilesPage() { + return this.$apollo.queries.packageFiles + .fetchMore({ + variables: { + first: GRAPHQL_PACKAGE_FILES_PAGE_SIZE, + last: null, + after: this.pageInfo.endCursor, + }, + }) + .then(() => { + this.scrollAndFocus(); + }); + }, + scrollAndFocus() { + scrollToElement(this.$el); + + // get first focusable row + const focusable = this.$el.querySelector('tbody tr'); + if (focusable) { + focusable.focus(); + } + }, }, i18n: { deleteFile: s__('PackageRegistry|Delete asset'), @@ -295,6 +344,8 @@ export default { deleteSelected: s__('PackageRegistry|Delete selected'), moreActionsText: __('More actions'), fetchPackageFilesErrorMessage: FETCH_PACKAGE_FILES_ERROR_MESSAGE, + prev: PREV, + next: NEXT, }, modal: { fileDeletePrimaryAction: { @@ -334,102 +385,122 @@ export default { > {{ $options.i18n.fetchPackageFilesErrorMessage }} </gl-alert> - <gl-table - v-else - :busy="isLoading" - :fields="filesTableHeaderFields" - :items="filesTableRows" - show-empty - selectable - select-mode="multi" - selected-variant="primary" - :tbody-tr-attr="{ 'data-testid': 'file-row' }" - @row-selected="updateSelectedReferences" - > - <template #table-busy> - <gl-loading-icon size="lg" class="gl-my-5" /> - </template> - <template #head(checkbox)="{ selectAllRows, clearSelected }"> - <gl-form-checkbox - v-if="canDelete" - data-testid="package-files-checkbox-all" - :checked="areAllFilesSelected" - :indeterminate="hasSelectedSomeFiles" - @change="areAllFilesSelected ? clearSelected() : selectAllRows()" - /> - </template> + <template v-else> + <gl-table + ref="table" + :busy="isLoading" + :fields="filesTableHeaderFields" + :items="filesTableRows" + show-empty + selectable + select-mode="multi" + selected-variant="primary" + :tbody-tr-attr="{ 'data-testid': 'file-row' }" + @row-selected="updateSelectedReferences" + > + <template #table-busy> + <gl-loading-icon size="lg" class="gl-my-5" /> + </template> + <template #head(checkbox)="{ selectAllRows, clearSelected }"> + <gl-form-checkbox + v-if="canDelete" + data-testid="package-files-checkbox-all" + :checked="areAllFilesSelected" + :indeterminate="hasSelectedSomeFiles" + @change="areAllFilesSelected ? clearSelected() : selectAllRows()" + /> + </template> - <template #cell(checkbox)="{ rowSelected, selectRow, unselectRow }"> - <gl-form-checkbox - v-if="canDelete" - class="gl-mt-1" - :checked="rowSelected" - data-testid="package-files-checkbox" - @change="rowSelected ? unselectRow() : selectRow()" - /> - </template> + <template #cell(checkbox)="{ rowSelected, selectRow, unselectRow }"> + <gl-form-checkbox + v-if="canDelete" + class="gl-mt-1" + :checked="rowSelected" + data-testid="package-files-checkbox" + @change="rowSelected ? unselectRow() : selectRow()" + /> + </template> - <template #cell(name)="{ item, toggleDetails, detailsShowing }"> - <gl-button - v-if="hasDetails(item)" - :icon="detailsShowing ? 'chevron-up' : 'chevron-down'" - :aria-label="detailsShowing ? __('Collapse') : __('Expand')" - category="tertiary" - size="small" - @click=" - toggleDetails(); - trackToggleDetails(detailsShowing); - " - /> - <gl-link - :href="item.downloadPath" - class="gl-text-gray-500" - data-testid="download-link" - @click="track($options.trackingActions.DOWNLOAD_PACKAGE_ASSET_TRACKING_ACTION)" - > - <file-icon - :file-name="item.fileName" - css-classes="gl-relative file-icon" - class="gl-mr-1 gl-relative" + <template #cell(name)="{ item, toggleDetails, detailsShowing }"> + <gl-button + v-if="hasDetails(item)" + :icon="detailsShowing ? 'chevron-up' : 'chevron-down'" + :aria-label="detailsShowing ? __('Collapse') : __('Expand')" + data-testid="toggle-details-button" + category="tertiary" + size="small" + @click=" + toggleDetails(); + trackToggleDetails(detailsShowing); + " /> - <span>{{ item.fileName }}</span> - </gl-link> - </template> + <gl-link + :href="item.downloadPath" + class="gl-text-gray-500" + data-testid="download-link" + @click="track($options.trackingActions.DOWNLOAD_PACKAGE_ASSET_TRACKING_ACTION)" + > + <file-icon + :file-name="item.fileName" + css-classes="gl-relative file-icon" + class="gl-mr-1 gl-relative" + /> + <span>{{ item.fileName }}</span> + </gl-link> + </template> - <template #cell(created)="{ item }"> - <time-ago-tooltip :time="item.createdAt" /> - </template> + <template #cell(created)="{ item }"> + <time-ago-tooltip :time="item.createdAt" /> + </template> - <template #cell(actions)="{ item }"> - <gl-dropdown - category="tertiary" - icon="ellipsis_v" - :text-sr-only="true" - :text="$options.i18n.moreActionsText" - no-caret - right - > - <gl-dropdown-item data-testid="delete-file" @click="handleFileDelete([item])"> - {{ $options.i18n.deleteFile }} - </gl-dropdown-item> - </gl-dropdown> - </template> + <template #cell(actions)="{ item }"> + <gl-disclosure-dropdown + category="tertiary" + icon="ellipsis_v" + placement="right" + :toggle-text="$options.i18n.moreActionsText" + text-sr-only + no-caret + > + <gl-disclosure-dropdown-item + data-testid="delete-file" + @action="handleFileDelete([item])" + > + <template #list-item> + {{ $options.i18n.deleteFile }} + </template> + </gl-disclosure-dropdown-item> + </gl-disclosure-dropdown> + </template> - <template #row-details="{ item }"> - <div - class="gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-bg-gray-10 gl-rounded-base gl-inset-border-1-gray-100" - > - <file-sha - v-if="item.fileSha256" - data-testid="sha-256" - title="SHA-256" - :sha="item.fileSha256" - /> - <file-sha v-if="item.fileMd5" data-testid="md5" title="MD5" :sha="item.fileMd5" /> - <file-sha v-if="item.fileSha1" data-testid="sha-1" title="SHA-1" :sha="item.fileSha1" /> - </div> - </template> - </gl-table> + <template #row-details="{ item }"> + <div + class="gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-bg-gray-10 gl-rounded-base gl-inset-border-1-gray-100" + > + <file-sha + v-if="item.fileSha256" + data-testid="sha-256" + title="SHA-256" + :sha="item.fileSha256" + /> + <file-sha v-if="item.fileMd5" data-testid="md5" title="MD5" :sha="item.fileMd5" /> + <file-sha v-if="item.fileSha1" data-testid="sha-1" title="SHA-1" :sha="item.fileSha1" /> + </div> + </template> + </gl-table> + <div class="gl-display-flex gl-justify-content-center"> + <gl-keyset-pagination + v-if="showPagination" + :disabled="isLoading" + v-bind="pageInfo" + :prev-text="$options.i18n.prev" + :next-text="$options.i18n.next" + class="gl-mt-3" + @prev="fetchPreviousFilesPage" + @next="fetchNextFilesPage" + /> + </div> + </template> <gl-modal ref="deleteFilesModal" |