From 87c40a31cb5b29d6c68e4dffc8c41ebe73aaa7b2 Mon Sep 17 00:00:00 2001
From: Sascha Eggenberger <seggenberger@gitlab.com>
Date: Mon, 29 Jan 2024 17:07:50 +0100
Subject: [PATCH] Pipeline manual action: Migrate dropdown to
 GlDisclosureDropdown

Changelog: changed
---
 .../components/pipelines_manual_actions.vue   | 83 ++++++++++++-------
 .../projects/pipelines/pipelines_spec.rb      | 13 +--
 .../pipelines_manual_actions_spec.js          | 20 +++--
 3 files changed, 72 insertions(+), 44 deletions(-)

diff --git a/app/assets/javascripts/ci/pipelines_page/components/pipelines_manual_actions.vue b/app/assets/javascripts/ci/pipelines_page/components/pipelines_manual_actions.vue
index ebf1744aee21..2251a53b9bc3 100644
--- a/app/assets/javascripts/ci/pipelines_page/components/pipelines_manual_actions.vue
+++ b/app/assets/javascripts/ci/pipelines_page/components/pipelines_manual_actions.vue
@@ -1,5 +1,11 @@
 <script>
-import { GlDropdown, GlDropdownItem, GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
+import {
+  GlDisclosureDropdown,
+  GlDisclosureDropdownItem,
+  GlIcon,
+  GlLoadingIcon,
+  GlTooltipDirective,
+} from '@gitlab/ui';
 import { createAlert } from '~/alert';
 import axios from '~/lib/utils/axios_utils';
 import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
@@ -16,8 +22,8 @@ export default {
   },
   components: {
     GlCountdown,
-    GlDropdown,
-    GlDropdownItem,
+    GlDisclosureDropdown,
+    GlDisclosureDropdownItem,
     GlIcon,
     GlLoadingIcon,
   },
@@ -52,6 +58,7 @@ export default {
       isLoading: false,
       actions: [],
       hasDropdownBeenShown: false,
+      isDropdownVisible: false,
     };
   },
   computed: {
@@ -101,58 +108,76 @@ export default {
         });
     },
     fetchActions() {
+      this.isDropdownVisible = true;
       this.hasDropdownBeenShown = true;
 
       this.$apollo.queries.actions.refetch();
 
       this.trackClick();
     },
+    hideAction() {
+      this.isDropdownVisible = false;
+    },
     trackClick() {
       this.track('click_manual_actions', { label: TRACKING_CATEGORIES.table });
     },
+    jobItem(job) {
+      return {
+        text: job.name,
+        extraAttrs: {
+          disabled: !job.canPlayJob,
+        },
+      };
+    },
   },
 };
 </script>
 <template>
-  <gl-dropdown
-    v-gl-tooltip
-    :title="__('Run manual or delayed jobs')"
+  <gl-disclosure-dropdown
+    v-gl-tooltip.left="isDropdownVisible ? '' : __('Run manual or delayed jobs')"
     :loading="isLoading"
     data-testid="pipelines-manual-actions-dropdown"
     right
     lazy
     icon="play"
     @shown="fetchActions"
+    @hidden="hideAction"
   >
-    <gl-dropdown-item v-if="isActionsLoading">
-      <div class="gl-display-flex">
-        <gl-loading-icon class="mr-2" />
-        <span>{{ __('Loading...') }}</span>
-      </div>
-    </gl-dropdown-item>
+    <gl-disclosure-dropdown-item v-if="isActionsLoading">
+      <template #list-item>
+        <div class="gl-display-flex">
+          <gl-loading-icon class="mr-2" />
+          <span>{{ __('Loading...') }}</span>
+        </div>
+      </template>
+    </gl-disclosure-dropdown-item>
 
-    <gl-dropdown-item
+    <gl-disclosure-dropdown-item
       v-for="action in actions"
       v-else
       :key="action.id"
-      :disabled="!action.canPlayJob"
-      @click="onClickAction(action)"
+      :item="jobItem(action)"
+      @action="onClickAction(action)"
     >
-      <div class="gl-display-flex gl-justify-content-space-between gl-flex-wrap">
-        {{ action.name }}
-        <span v-if="action.scheduledAt">
-          <gl-icon name="clock" />
-          <gl-countdown :end-date-string="action.scheduledAt" />
-        </span>
-      </div>
-    </gl-dropdown-item>
+      <template #list-item>
+        <div class="gl-display-flex gl-justify-content-space-between gl-flex-wrap">
+          {{ action.name }}
+          <span v-if="action.scheduledAt">
+            <gl-icon name="clock" />
+            <gl-countdown :end-date-string="action.scheduledAt" />
+          </span>
+        </div>
+      </template>
+    </gl-disclosure-dropdown-item>
 
     <template #footer>
-      <gl-dropdown-item v-if="isDropdownLimitReached">
-        <span class="gl-font-sm gl-text-gray-300!" data-testid="limit-reached-msg">
-          {{ __('Showing first 50 actions.') }}
-        </span>
-      </gl-dropdown-item>
+      <gl-disclosure-dropdown-item v-if="isDropdownLimitReached">
+        <template #list-item>
+          <span class="gl-font-sm gl-text-gray-300!" data-testid="limit-reached-msg">
+            {{ __('Showing first 50 actions.') }}
+          </span>
+        </template>
+      </gl-disclosure-dropdown-item>
     </template>
-  </gl-dropdown>
+  </gl-disclosure-dropdown>
 </template>
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 9c052b150bec..00bb9141aa04 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -295,7 +295,7 @@
 
         context 'when manual action was played' do
           before do
-            find('[data-testid="pipelines-manual-actions-dropdown"]').click
+            find('[data-testid="pipelines-manual-actions-dropdown"] button').click
 
             wait_for_requests
 
@@ -326,7 +326,7 @@
         end
 
         it "has link to the delayed job's action" do
-          find('[data-testid="pipelines-manual-actions-dropdown"]').click
+          find('[data-testid="pipelines-manual-actions-dropdown"] button').click
 
           wait_for_requests
 
@@ -345,7 +345,7 @@
           end
 
           it "shows 00:00:00 as the remaining time" do
-            find('[data-testid="pipelines-manual-actions-dropdown"]').click
+            find('[data-testid="pipelines-manual-actions-dropdown"] button').click
 
             wait_for_requests
 
@@ -354,7 +354,8 @@
         end
 
         context 'when user played a delayed job immediately' do
-          let(:manual_action_selector) { '[data-testid="pipelines-manual-actions-dropdown"]' }
+          let(:manual_action_selector) { '[data-testid="pipelines-manual-actions-dropdown"] button' }
+          let(:manual_action_dropdown) { '[data-testid="pipelines-manual-actions-dropdown"]' }
 
           before do
             find(manual_action_selector).click
@@ -363,8 +364,8 @@
             end
 
             # Wait for UI to transition to ensure a request has been made
-            within(manual_action_selector) { find('.gl-spinner') }
-            within(manual_action_selector) { find('[data-testid="play-icon"]') }
+            within(manual_action_dropdown) { find('.gl-spinner') }
+            within(manual_action_dropdown) { find('[data-testid="play-icon"]') }
 
             wait_for_requests
           end
diff --git a/spec/frontend/ci/pipelines_page/components/pipelines_manual_actions_spec.js b/spec/frontend/ci/pipelines_page/components/pipelines_manual_actions_spec.js
index a24e136f1ff0..04d9735640cb 100644
--- a/spec/frontend/ci/pipelines_page/components/pipelines_manual_actions_spec.js
+++ b/spec/frontend/ci/pipelines_page/components/pipelines_manual_actions_spec.js
@@ -1,4 +1,4 @@
-import { GlDropdown, GlDropdownItem, GlLoadingIcon } from '@gitlab/ui';
+import { GlDisclosureDropdown, GlDisclosureDropdownItem, GlLoadingIcon } from '@gitlab/ui';
 import MockAdapter from 'axios-mock-adapter';
 import Vue, { nextTick } from 'vue';
 import VueApollo from 'vue-apollo';
@@ -48,14 +48,14 @@ describe('Pipeline manual actions', () => {
         iid: 100,
       },
       stubs: {
-        GlDropdown,
+        GlDisclosureDropdown,
       },
       apolloProvider: createMockApollo([[getPipelineActionsQuery, queryHandler]]),
     });
   };
 
-  const findDropdown = () => wrapper.findComponent(GlDropdown);
-  const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
+  const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
+  const findAllDropdownItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem);
   const findAllCountdowns = () => wrapper.findAllComponents(GlCountdown);
   const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
   const findLimitMessage = () => wrapper.findByTestId('limit-reached-msg');
@@ -101,14 +101,16 @@ describe('Pipeline manual actions', () => {
     });
 
     it("displays a disabled action when it's not playable", () => {
-      expect(findAllDropdownItems().at(0).attributes('disabled')).toBeDefined();
+      expect(findAllDropdownItems().at(0).props('item')).toMatchObject({
+        extraAttrs: { disabled: true },
+      });
     });
 
     describe('on action click', () => {
       it('makes a request and toggles the loading state', async () => {
         mock.onPost(mockPath).reply(HTTP_STATUS_OK);
 
-        findAllDropdownItems().at(1).vm.$emit('click');
+        findAllDropdownItems().at(1).vm.$emit('action');
 
         await nextTick();
 
@@ -122,7 +124,7 @@ describe('Pipeline manual actions', () => {
       it('makes a failed request and toggles the loading state', async () => {
         mock.onPost(mockPath).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
 
-        findAllDropdownItems().at(1).vm.$emit('click');
+        findAllDropdownItems().at(1).vm.$emit('action');
 
         await nextTick();
 
@@ -163,7 +165,7 @@ describe('Pipeline manual actions', () => {
 
         confirmAction.mockResolvedValueOnce(true);
 
-        findAllDropdownItems().at(2).vm.$emit('click');
+        findAllDropdownItems().at(2).vm.$emit('action');
 
         expect(confirmAction).toHaveBeenCalled();
 
@@ -177,7 +179,7 @@ describe('Pipeline manual actions', () => {
 
         confirmAction.mockResolvedValueOnce(false);
 
-        findAllDropdownItems().at(2).vm.$emit('click');
+        findAllDropdownItems().at(2).vm.$emit('action');
 
         expect(confirmAction).toHaveBeenCalled();
 
-- 
GitLab