Add and use reusable loading state to resource lists

What does this MR do and why?

We use skeleton loaders as placeholders on lists on three lists. At least I found it in three:

  • Issues List
  • Explore - Ai Catalog List
  • Dashboard List

I wanted to create a reusable component which we can easily use everywhere. I decided to put it the directory app/assets/javascripts/vue_shared/components/resource_lists/ which is the new reusable component for resource lists, coming from this epic &13781 (closed)

It is configurable in a way that we can set the number of lines for the left and right loading skeletons, and how many items we want to render in one list. It is not possible to configure the widths of the skeleton bars, because we currently don't need this.

Screenshots or screen recordings

Without loading for reference Before After
Screenshot_2025-07-25_at_16.14.33 Screenshot_2025-07-25_at_16.16.35 Screenshot_2025-07-25_at_16.13.52
Screenshot_2025-07-25_at_16.14.24 Screenshot_2025-07-25_at_16.17.21 Screenshot_2025-07-25_at_16.12.41
Screenshot_2025-07-25_at_16.14.29 Screenshot_2025-07-25_at_16.16.50 Screenshot_2025-07-25_at_16.08.42
- Screenshot_2025-07-25_at_16.16.39 Screenshot_2025-07-25_at_16.08.29

Responsive behavior:

Screen_Recording_2025-07-30_at_11.01.59

How to set up and validate locally

Storybook

  1. Start Storybook with yarn storybook:start
  2. Visit http://localhost:9002/?path=/docs/vue-shared-components-resource-lists-loading-state-list--docs
  3. Visit http://localhost:9002/?path=/docs/vue-shared-components-resource-lists-loading-state-list-item--docs

Loading states mock

Apply this mock to set the `v-if`'s to true for endless spinners
diff --git a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue
index 797d1642e716..a35a5b1337c9 100644
--- a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue
+++ b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue
@@ -384,7 +384,7 @@ export default {
     </issuable-bulk-edit-sidebar>
     <slot name="list-body"></slot>
     <loading-state-list
-      v-if="issuablesLoading"
+      v-if="true"
       :left-lines-count="3"
       :list-length="skeletonItemCount"
     />
diff --git a/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_list.vue b/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_list.vue
index 7a95cd586fd3..6aa094f475ab 100644
--- a/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_list.vue
+++ b/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_list.vue
@@ -57,7 +57,7 @@ export default {
 
 <template>
   <div data-testid="ai-catalog-list">
-    <loading-state-list v-if="isLoading" />
+    <loading-state-list v-if="true" />
 
     <template v-else-if="items.length > 0">
       <ul class="gl-list-style-none gl-m-0 gl-p-0">
diff --git a/ee/app/assets/javascripts/analytics/analytics_dashboards/components/dashboards_list.vue b/ee/app/assets/javascripts/analytics/analytics_dashboards/components/dashboards_list.vue
index d4e8c602c946..a43e9e9b8a21 100644
--- a/ee/app/assets/javascripts/analytics/analytics_dashboards/components/dashboards_list.vue
+++ b/ee/app/assets/javascripts/analytics/analytics_dashboards/components/dashboards_list.vue
@@ -321,7 +321,7 @@ export default {
         @error="onError"
       />
 
-      <loading-state-list v-if="isLoading" />
+      <loading-state-list v-if="true" :right-lines-count="1" />
 
       <template v-else>
         <dashboard-list-item
@@ -333,8 +333,8 @@ export default {
           @clone="onCloneDashboard"
         />
       </template>
-      <li v-if="loadingNewDashboard" class="!gl-px-5">
-        <loading-state-list-item />
+      <li v-if="true" class="!gl-px-5">
+        <loading-state-list-item :right-lines-count="1" />
       </li>
     </ul>
   </div>

app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue

  1. Visit http://gdk.test:3000/dashboard/issues?sort=created_date&state=opened&assignee_username[]=root

ee/app/assets/javascripts/ai/catalog/components/ai_catalog_list.vue

  1. Enable the feature flag: echo "Feature.enable(:global_ai_catalog)" | rails c
  2. Visit http://gdk.test:3000/explore/ai-catalog/agents

ee/app/assets/javascripts/analytics/analytics_dashboards/components/dashboards_list.vue

  1. Visit http://gdk.test:3000/flightjs/Flight/-/analytics/dashboards

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Related to #554018 (closed)

Edited by Vanessa Otto

Merge request reports

Loading