diff --git a/app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue b/app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue index c2bd01701df1f2506a3c78316dd51d829f2edaca..bad804ad1721411092324fdf00ceed4982227c84 100644 --- a/app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue +++ b/app/assets/javascripts/registry/explorer/components/list_page/registry_header.vue @@ -43,6 +43,11 @@ export default { required: false, default: false, }, + metadataLoading: { + type: Boolean, + required: false, + default: false, + }, }, loader: { repeat: 10, @@ -92,7 +97,11 @@ export default { </script> <template> - <title-area :title="$options.i18n.CONTAINER_REGISTRY_TITLE" :info-messages="infoMessages"> + <title-area + :title="$options.i18n.CONTAINER_REGISTRY_TITLE" + :info-messages="infoMessages" + :metadata-loading="metadataLoading" + > <template #right-actions> <slot name="commands"></slot> </template> diff --git a/app/assets/javascripts/registry/explorer/pages/list.vue b/app/assets/javascripts/registry/explorer/pages/list.vue index 3192ba82db85a9849e1e8fb652986f319dce9e0f..b443d425bfbfe20d4eecb24b5d36641f60201c48 100644 --- a/app/assets/javascripts/registry/explorer/pages/list.vue +++ b/app/assets/javascripts/registry/explorer/pages/list.vue @@ -242,6 +242,7 @@ export default { <template v-else> <registry-header + :metadata-loading="isLoading" :images-count="containerRepositoriesCount" :expiration-policy="config.expirationPolicy" :help-page-path="config.helpPagePath" diff --git a/app/assets/javascripts/vue_shared/components/registry/title_area.vue b/app/assets/javascripts/vue_shared/components/registry/title_area.vue index 4d47a34c9a3574a0da80279e928d93fc9569cbcd..fb60e1c0b1d1f6e6cb0100005bfa8684179a46e0 100644 --- a/app/assets/javascripts/vue_shared/components/registry/title_area.vue +++ b/app/assets/javascripts/vue_shared/components/registry/title_area.vue @@ -1,5 +1,5 @@ <script> -import { GlAvatar, GlSprintf, GlLink } from '@gitlab/ui'; +import { GlAvatar, GlSprintf, GlLink, GlSkeletonLoader } from '@gitlab/ui'; export default { name: 'TitleArea', @@ -7,6 +7,7 @@ export default { GlAvatar, GlSprintf, GlLink, + GlSkeletonLoader, }, props: { avatar: { @@ -24,6 +25,11 @@ export default { default: () => [], required: false, }, + metadataLoading: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -68,13 +74,23 @@ export default { </div> <div class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mt-3"> - <div - v-for="(row, metadataIndex) in metadataSlots" - :key="metadataIndex" - class="gl-display-flex gl-align-items-center gl-mr-5" - > - <slot :name="row"></slot> - </div> + <template v-if="!metadataLoading"> + <div + v-for="(row, metadataIndex) in metadataSlots" + :key="metadataIndex" + class="gl-display-flex gl-align-items-center gl-mr-5" + > + <slot :name="row"></slot> + </div> + </template> + <template v-else> + <div class="gl-w-full"> + <gl-skeleton-loader :width="200" :height="16" preserve-aspect-ratio="xMinYMax meet"> + <circle cx="6" cy="8" r="6" /> + <rect x="16" y="4" width="200" height="8" rx="4" /> + </gl-skeleton-loader> + </div> + </template> </div> </div> <div v-if="$slots['right-actions']" class="gl-mt-3"> diff --git a/changelogs/unreleased/290759-adjust-container-registry-metadata-during-loading.yml b/changelogs/unreleased/290759-adjust-container-registry-metadata-during-loading.yml new file mode 100644 index 0000000000000000000000000000000000000000..0fab286b2e506628a0bf239a03945737babe6c28 --- /dev/null +++ b/changelogs/unreleased/290759-adjust-container-registry-metadata-during-loading.yml @@ -0,0 +1,5 @@ +--- +title: Adjust container registry metadata during loading +merge_request: 50181 +author: +type: changed diff --git a/spec/frontend/registry/explorer/components/list_page/registry_header_spec.js b/spec/frontend/registry/explorer/components/list_page/registry_header_spec.js index 3c997093d46ff23087e28ad4a86eaf6db00c120c..58439c185e382d6d0912e699ace3b5935fcc69cb 100644 --- a/spec/frontend/registry/explorer/components/list_page/registry_header_spec.js +++ b/spec/frontend/registry/explorer/components/list_page/registry_header_spec.js @@ -41,9 +41,12 @@ describe('registry_header', () => { describe('header', () => { it('has a title', () => { - mountComponent(); + mountComponent({ metadataLoading: true }); - expect(findTitleArea().props('title')).toBe(CONTAINER_REGISTRY_TITLE); + expect(findTitleArea().props()).toMatchObject({ + title: CONTAINER_REGISTRY_TITLE, + metadataLoading: true, + }); }); it('has a commands slot', () => { diff --git a/spec/frontend/registry/explorer/pages/list_spec.js b/spec/frontend/registry/explorer/pages/list_spec.js index 7d32a66701118209f7b968f0bba86dd5d65a5a24..071b6e85ab2a82996e9dce3e734b5eb953576eff 100644 --- a/spec/frontend/registry/explorer/pages/list_spec.js +++ b/spec/frontend/registry/explorer/pages/list_spec.js @@ -116,6 +116,7 @@ describe('List Page', () => { expect(findRegistryHeader().exists()).toBe(true); expect(findRegistryHeader().props()).toMatchObject({ imagesCount: 2, + metadataLoading: false, }); }); @@ -170,6 +171,12 @@ describe('List Page', () => { expect(findCliCommands().exists()).toBe(false); }); + + it('title has the metadataLoading props set to true', () => { + mountComponent(); + + expect(findRegistryHeader().props('metadataLoading')).toBe(true); + }); }); describe('list is empty', () => { diff --git a/spec/frontend/vue_shared/components/registry/title_area_spec.js b/spec/frontend/vue_shared/components/registry/title_area_spec.js index b743a663f062438aa7ce83f0def6ea0a3a05b456..cb56acd90d33ef90b51dd1c86993d552e6b395b8 100644 --- a/spec/frontend/vue_shared/components/registry/title_area_spec.js +++ b/spec/frontend/vue_shared/components/registry/title_area_spec.js @@ -1,4 +1,4 @@ -import { GlAvatar, GlSprintf, GlLink } from '@gitlab/ui'; +import { GlAvatar, GlSprintf, GlLink, GlSkeletonLoader } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import component from '~/vue_shared/components/registry/title_area.vue'; @@ -15,6 +15,7 @@ describe('title area', () => { const findInfoMessages = () => wrapper.findAll('[data-testid="info-message"]'); const findDynamicSlot = () => wrapper.find(`[data-testid="${DYNAMIC_SLOT}`); const findSlotOrderElements = () => wrapper.findAll('[slot-test]'); + const findSkeletonLoader = () => wrapper.find(GlSkeletonLoader); const mountComponent = ({ propsData = { title: 'foo' }, slots } = {}) => { wrapper = shallowMount(component, { @@ -100,6 +101,29 @@ describe('title area', () => { expect(findMetadataSlot(name).exists()).toBe(true); }); }); + + it('is/are hidden when metadata-loading is true', async () => { + mountComponent({ slots: slotMocks, propsData: { title: 'foo', metadataLoading: true } }); + + await wrapper.vm.$nextTick(); + slotNames.forEach(name => { + expect(findMetadataSlot(name).exists()).toBe(false); + }); + }); + }); + + describe('metadata skeleton loader', () => { + it('is hidden when metadata loading is false', () => { + mountComponent(); + + expect(findSkeletonLoader().exists()).toBe(false); + }); + + it('is shown when metadata loading is true', () => { + mountComponent({ propsData: { metadataLoading: true } }); + + expect(findSkeletonLoader().exists()).toBe(true); + }); }); describe('dynamic slots', () => {