From e13331735e2290bb270b35a32755fc4246a9efc6 Mon Sep 17 00:00:00 2001
From: Sascha Eggenberger <seggenberger@gitlab.com>
Date: Wed, 6 Dec 2023 13:12:28 +0000
Subject: [PATCH] Test case layout update

Changelog: changed
EE: true
---
 .../show/components/issuable_body.vue         |   9 +-
 .../show/components/issuable_edit_form.vue    |   5 +-
 .../show/components/issuable_header.vue       |   2 +-
 .../show/components/issuable_show_root.vue    |   9 ++
 .../show/components/issuable_title.vue        |   6 +-
 .../components/issuable_sidebar_root.vue      |  55 +++++---
 .../stylesheets/components/detail_page.scss   |   6 +-
 app/assets/stylesheets/framework/sidebar.scss |   9 ++
 .../components/test_case_show_root.vue        |  50 ++++++--
 .../components/test_case_sidebar.vue          |  54 +-------
 .../components/test_case_sidebar_todo.vue     |  82 ++++++++++++
 .../projects/quality/test_case_show_spec.rb   |   6 +-
 .../components/test_case_show_root_spec.js    |  16 ++-
 .../components/test_case_sidebar_spec.js      |  55 +-------
 .../components/test_case_sidebar_todo_spec.js | 121 ++++++++++++++++++
 locale/gitlab.pot                             |   6 -
 .../components/issuable_sidebar_root_spec.js  |  16 ++-
 17 files changed, 344 insertions(+), 163 deletions(-)
 create mode 100644 ee/app/assets/javascripts/test_case_show/components/test_case_sidebar_todo.vue
 create mode 100644 ee/spec/frontend/test_case_show/components/test_case_sidebar_todo_spec.js

diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_body.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_body.vue
index dae3ddfe01632..bac71c1eda211 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_body.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_body.vue
@@ -31,6 +31,10 @@ export default {
       type: Boolean,
       required: true,
     },
+    hideEditButton: {
+      type: Boolean,
+      required: false,
+    },
     enableAutocomplete: {
       type: Boolean,
       required: true,
@@ -166,6 +170,7 @@ export default {
           :issuable="issuable"
           :status-icon="statusIcon"
           :enable-edit="enableEdit"
+          :hide-edit-button="hideEditButton"
           :workspace-type="workspaceType"
           @edit-issuable="$emit('edit-issuable', $event)"
         >
@@ -181,12 +186,12 @@ export default {
           :task-list-update-path="taskListUpdatePath"
         />
         <slot name="secondary-content"></slot>
-        <small v-if="isUpdated" class="edited-text gl-font-sm!">
+        <small v-if="isUpdated" class="edited-text gl-font-sm! gl-text-secondary">
           {{ __('Edited') }}
           <time-ago-tooltip :time="issuable.updatedAt" tooltip-placement="bottom" />
           <span v-if="updatedBy">
             {{ __('by') }}
-            <gl-link :href="updatedBy.webUrl" class="author-link gl-font-sm!">
+            <gl-link :href="updatedBy.webUrl" class="author-link gl-font-sm! gl-text-secondary">
               <span>{{ updatedBy.name }}</span>
             </gl-link>
           </span>
diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_edit_form.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_edit_form.vue
index 7c3dd5c3623d6..3353374310fc8 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_edit_form.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_edit_form.vue
@@ -153,7 +153,10 @@ export default {
         </template>
       </markdown-field>
     </gl-form-group>
-    <div data-testid="actions" class="col-12 gl-mt-3 gl-mb-3 gl-px-0 clearfix">
+    <div
+      data-testid="actions"
+      class="col-12 gl-mt-3 gl-mb-3 gl-px-0 clearfix gl-display-flex gl-gap-3"
+    >
       <slot
         name="edit-form-actions"
         :issuable-title="title"
diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue
index 760a3e01d6f37..1b95a2abdf991 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue
@@ -221,7 +221,7 @@ export default {
         @click="handleRightSidebarToggleClick"
       />
     </div>
-    <div class="detail-page-header-actions gl-align-self-center gl-display-flex">
+    <div class="detail-page-header-actions gl-align-self-center gl-display-flex gl-gap-3">
       <slot name="header-actions"></slot>
     </div>
   </div>
diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_show_root.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_show_root.vue
index 040f49c7c25d5..1d44c4a1c1461 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_show_root.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_show_root.vue
@@ -32,6 +32,11 @@ export default {
       required: false,
       default: false,
     },
+    hideEditButton: {
+      type: Boolean,
+      required: false,
+      default: false,
+    },
     enableAutocomplete: {
       type: Boolean,
       required: false,
@@ -137,6 +142,7 @@ export default {
       :status-icon="statusIcon"
       :status-icon-class="statusIconClass"
       :enable-edit="enableEdit"
+      :hide-edit-button="hideEditButton"
       :enable-autocomplete="enableAutocomplete"
       :enable-autosave="enableAutosave"
       :enable-zen-mode="enableZenMode"
@@ -169,6 +175,9 @@ export default {
     </issuable-discussion>
 
     <issuable-sidebar>
+      <template #right-sidebar-top-items="{ sidebarExpanded, toggleSidebar }">
+        <slot name="right-sidebar-top-items" v-bind="{ sidebarExpanded, toggleSidebar }"></slot>
+      </template>
       <template #right-sidebar-items="{ sidebarExpanded, toggleSidebar }">
         <slot name="right-sidebar-items" v-bind="{ sidebarExpanded, toggleSidebar }"></slot>
       </template>
diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue
index 5387e39e3eb15..3dae894b1276c 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue
@@ -33,6 +33,10 @@ export default {
       type: Boolean,
       required: true,
     },
+    hideEditButton: {
+      type: Boolean,
+      required: false,
+    },
     workspaceType: {
       type: String,
       required: false,
@@ -70,7 +74,7 @@ export default {
         data-testid="issuable-title"
       ></h1>
       <gl-button
-        v-if="enableEdit"
+        v-if="enableEdit && !hideEditButton"
         v-gl-tooltip.bottom
         :title="$options.i18n.editTitleAndDescription"
         :aria-label="$options.i18n.editTitleAndDescription"
diff --git a/app/assets/javascripts/vue_shared/issuable/sidebar/components/issuable_sidebar_root.vue b/app/assets/javascripts/vue_shared/issuable/sidebar/components/issuable_sidebar_root.vue
index 774267639fc1f..cb9ad6418a471 100644
--- a/app/assets/javascripts/vue_shared/issuable/sidebar/components/issuable_sidebar_root.vue
+++ b/app/assets/javascripts/vue_shared/issuable/sidebar/components/issuable_sidebar_root.vue
@@ -1,13 +1,17 @@
 <script>
-import { GlIcon } from '@gitlab/ui';
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
 import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
 import { getCookie, setCookie, parseBoolean } from '~/lib/utils/common_utils';
+import { __ } from '~/locale';
 
 import { USER_COLLAPSED_GUTTER_COOKIE } from '../constants';
 
 export default {
   components: {
-    GlIcon,
+    GlButton,
+  },
+  directives: {
+    GlTooltip: GlTooltipDirective,
   },
   data() {
     const userExpanded = !parseBoolean(getCookie(USER_COLLAPSED_GUTTER_COOKIE));
@@ -20,6 +24,20 @@ export default {
       isExpanded: userExpanded ? bp.isDesktop() : userExpanded,
     };
   },
+  computed: {
+    toggleLabel() {
+      return this.isExpanded ? __('Collapse sidebar') : __('Expand sidebar');
+    },
+    toggleIcon() {
+      return this.isExpanded ? 'chevron-double-lg-right' : 'chevron-double-lg-left';
+    },
+    expandedToggleClass() {
+      return this.isExpanded ? 'block' : '';
+    },
+    collapsedToggleClass() {
+      return !this.isExpanded ? 'block' : '';
+    },
+  },
   mounted() {
     window.addEventListener('resize', this.handleWindowResize);
     this.updatePageContainerClass();
@@ -59,23 +77,24 @@ export default {
     class="right-sidebar"
     aria-live="polite"
   >
-    <button
-      class="toggle-right-sidebar-button js-toggle-right-sidebar-button w-100 gl-text-decoration-none! gl-display-flex gl-outline-0!"
-      data-testid="toggle-right-sidebar-button"
-      :title="__('Toggle sidebar')"
-      @click="toggleSidebar"
-    >
-      <span v-if="isExpanded" class="collapse-text gl-flex-grow-1 gl-text-left">{{
-        __('Collapse sidebar')
-      }}</span>
-      <gl-icon v-show="isExpanded" data-testid="icon-collapse" name="chevron-double-lg-right" />
-      <gl-icon
-        v-show="!isExpanded"
-        data-testid="icon-expand"
-        name="chevron-double-lg-left"
-        class="gl-ml-2"
+    <div class="right-sidebar-header" :class="expandedToggleClass">
+      <gl-button
+        v-gl-tooltip.hover.left
+        category="tertiary"
+        size="small"
+        class="gl-float-right gutter-toggle toggle-right-sidebar-button js-toggle-right-sidebar-button gl-shadow-none!"
+        :class="collapsedToggleClass"
+        data-testid="toggle-right-sidebar-button"
+        :icon="toggleIcon"
+        :title="toggleLabel"
+        :aria-label="toggleLabel"
+        @click="toggleSidebar"
       />
-    </button>
+      <slot
+        name="right-sidebar-top-items"
+        v-bind="{ sidebarExpanded: isExpanded, toggleSidebar }"
+      ></slot>
+    </div>
     <div data-testid="sidebar-items" class="issuable-sidebar">
       <slot
         name="right-sidebar-items"
diff --git a/app/assets/stylesheets/components/detail_page.scss b/app/assets/stylesheets/components/detail_page.scss
index 56214040fddf6..98ed7f590ea52 100644
--- a/app/assets/stylesheets/components/detail_page.scss
+++ b/app/assets/stylesheets/components/detail_page.scss
@@ -53,13 +53,17 @@
     margin: 0 0 $gl-spacing-scale-4;
     color: $gl-text-color;
     padding: 0 0 0.3em;
-    border-bottom: 1px solid $white-dark;
   }
 
   .description {
     @include clearfix;
 
     margin-top: 6px;
+
+    + .edited-text {
+      display: inline-block;
+      margin-top: $gl-spacing-scale-4;
+    }
   }
 
   .author-link {
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 168aa704a69da..30f6d39f70bba 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -53,12 +53,21 @@
       height: $gl-padding;
     }
   }
+
+  .right-sidebar-header {
+    flex-wrap: wrap;
+  }
 }
 
 .right-sidebar-expanded {
   padding-right: 0;
   z-index: $zindex-dropdown-menu;
 
+  .right-sidebar-header {
+    padding-block: $gl-spacing-scale-4;
+    margin-left: 20px;
+  }
+
   .inline-block {
     @include gl-display-inline-block;
   }
diff --git a/ee/app/assets/javascripts/test_case_show/components/test_case_show_root.vue b/ee/app/assets/javascripts/test_case_show/components/test_case_show_root.vue
index 34d4810f4fe3a..cfa2e2f3b7222 100644
--- a/ee/app/assets/javascripts/test_case_show/components/test_case_show_root.vue
+++ b/ee/app/assets/javascripts/test_case_show/components/test_case_show_root.vue
@@ -18,6 +18,7 @@ import { s__, __ } from '~/locale';
 
 import TestCaseGraphQL from '../mixins/test_case_graphql';
 import TestCaseSidebar from './test_case_sidebar.vue';
+import TestCaseSidebarTodo from './test_case_sidebar_todo.vue';
 
 const stateEvent = {
   Close: 'CLOSE',
@@ -34,6 +35,7 @@ export default {
     GlAlert,
     IssuableShow,
     TestCaseSidebar,
+    TestCaseSidebarTodo,
     GlDisclosureDropdown,
     GlDisclosureDropdownGroup,
     GlDisclosureDropdownItem,
@@ -70,7 +72,13 @@ export default {
       return this.isTestCaseOpen ? __('Open') : __('Archived');
     },
     testCaseActionTitle() {
-      return this.isTestCaseOpen ? __('Archive test case') : __('Reopen test case');
+      return this.isTestCaseOpen ? __('Archive') : __('Reopen');
+    },
+    editCaseActionTitle() {
+      return __('Edit');
+    },
+    editCaseActionAriaLabel() {
+      return __('Edit title and description');
     },
     todo() {
       const todos = this.testCase.currentUserTodos.nodes;
@@ -83,6 +91,15 @@ export default {
         id: getIdFromGraphQLId(label.id),
       }));
     },
+    editTestCaseItem() {
+      return {
+        text: this.editCaseActionTitle,
+        action: this.handleEditTestCase,
+        extraAttrs: {
+          'data-testid': 'edit-test-case',
+        },
+      };
+    },
     toggleTestCaseStateItem() {
       return { text: this.testCaseActionTitle, action: this.handleTestCaseStateChange };
     },
@@ -159,6 +176,7 @@ export default {
       :issuable="testCase"
       :status-icon="statusIcon"
       :enable-edit="canEditTestCase"
+      hide-edit-button
       :enable-autocomplete="true"
       :enable-task-list="true"
       :edit-form-visible="editTestCaseFormVisible"
@@ -168,9 +186,7 @@ export default {
       :task-list-update-path="updatePath"
       :task-list-lock-version="lockVersion"
       :workspace-type="$options.WORKSPACE_PROJECT"
-      status-icon-class="gl-sm-display-none"
       show-work-item-type-icon
-      @edit-issuable="handleEditTestCase"
       @task-list-update-success="handleTaskListUpdateSuccess"
       @task-list-update-failure="handleTaskListUpdateFailure"
     >
@@ -196,6 +212,7 @@ export default {
           category="secondary"
           :toggle-text="__('Options')"
         >
+          <gl-disclosure-dropdown-item :item="editTestCaseItem" />
           <gl-disclosure-dropdown-item
             :item="toggleTestCaseStateItem"
             data-testid="toggle-state-dropdown-item"
@@ -204,10 +221,20 @@ export default {
             <gl-disclosure-dropdown-item :item="newTestCaseItem" />
           </gl-disclosure-dropdown-group>
         </gl-disclosure-dropdown>
+        <gl-button
+          v-if="canEditTestCase"
+          data-testid="edit-test-case"
+          class="gl-display-none gl-md-display-inline-block"
+          :loading="testCaseStateChangeInProgress"
+          :title="editCaseActionAriaLabel"
+          :aria-label="editCaseActionAriaLabel"
+          @click="handleEditTestCase"
+          >{{ editCaseActionTitle }}</gl-button
+        >
         <gl-button
           v-if="canEditTestCase"
           data-testid="archive-test-case"
-          class="gl-display-none gl-md-display-inline-block gl-mr-2"
+          class="gl-display-none gl-md-display-inline-block"
           :loading="testCaseStateChangeInProgress"
           @click="handleTestCaseStateChange"
           >{{ testCaseActionTitle }}</gl-button
@@ -233,19 +260,22 @@ export default {
           @click.prevent="handleSaveTestCase(issuableMeta)"
           >{{ __('Save changes') }}</gl-button
         >
-        <gl-button
-          data-testid="cancel-test-case-edit"
-          class="gl-float-right"
-          @click="handleCancelClick"
-        >
+        <gl-button data-testid="cancel-test-case-edit" @click="handleCancelClick">
           {{ __('Cancel') }}
         </gl-button>
       </template>
+      <template #right-sidebar-top-items="{ sidebarExpanded, toggleSidebar }">
+        <test-case-sidebar-todo
+          :sidebar-expanded="sidebarExpanded"
+          :todo="todo"
+          @test-case-updated="handleTestCaseUpdated"
+          @sidebar-toggle="toggleSidebar"
+        />
+      </template>
       <template #right-sidebar-items="{ sidebarExpanded, toggleSidebar }">
         <test-case-sidebar
           :sidebar-expanded="sidebarExpanded"
           :selected-labels="selectedLabels"
-          :todo="todo"
           :moved="testCase.moved"
           @test-case-updated="handleTestCaseUpdated"
           @sidebar-toggle="toggleSidebar"
diff --git a/ee/app/assets/javascripts/test_case_show/components/test_case_sidebar.vue b/ee/app/assets/javascripts/test_case_show/components/test_case_sidebar.vue
index 519e51da1de2d..2d767bed52ffe 100644
--- a/ee/app/assets/javascripts/test_case_show/components/test_case_sidebar.vue
+++ b/ee/app/assets/javascripts/test_case_show/components/test_case_sidebar.vue
@@ -1,6 +1,5 @@
 <script>
-import { GlTooltipDirective as GlTooltip, GlButton, GlIcon, GlLoadingIcon } from '@gitlab/ui';
-import { s__, __ } from '~/locale';
+import { s__ } from '~/locale';
 import ProjectSelect from '~/sidebar/components/move/issuable_move_dropdown.vue';
 import LabelsSelectWidget from '~/sidebar/components/labels/labels_select_widget/labels_select_root.vue';
 import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
@@ -11,16 +10,10 @@ export default {
   TYPE_TEST_CASE,
   WORKSPACE_PROJECT,
   components: {
-    GlButton,
-    GlIcon,
-    GlLoadingIcon,
     ProjectSelect,
     LabelsSelectWidget,
     SidebarConfidentialityWidget,
   },
-  directives: {
-    GlTooltip,
-  },
   mixins: [TestCaseGraphQL],
   inject: [
     'projectFullPath',
@@ -37,11 +30,6 @@ export default {
       type: Boolean,
       required: true,
     },
-    todo: {
-      type: Object,
-      required: false,
-      default: null,
-    },
     selectedLabels: {
       type: Array,
       required: true,
@@ -59,18 +47,6 @@ export default {
     };
   },
   computed: {
-    isTodoPending() {
-      return this.todo?.state === 'pending';
-    },
-    todoUpdateInProgress() {
-      return this.$apollo.queries.testCase.loading || this.testCaseTodoUpdateInProgress;
-    },
-    todoActionText() {
-      return this.isTodoPending ? __('Mark as done') : __('Add a to do');
-    },
-    todoIcon() {
-      return this.isTodoPending ? 'todo-done' : 'todo-add';
-    },
     selectProjectDropdownButtonTitle() {
       return this.testCaseMoveInProgress
         ? s__('TestCases|Moving test case')
@@ -81,13 +57,6 @@ export default {
     this.sidebarEl = document.querySelector('aside.right-sidebar');
   },
   methods: {
-    handleTodoButtonClick() {
-      if (this.isTodoPending) {
-        this.markTestCaseTodoDone();
-      } else {
-        this.addTestCaseAsTodo();
-      }
-    },
     toggleSidebar() {
       this.$emit('sidebar-toggle');
     },
@@ -175,27 +144,6 @@ export default {
 
 <template>
   <div class="test-case-sidebar-items">
-    <template v-if="canEditTestCase">
-      <div v-if="sidebarExpanded" data-testid="todo" class="block todo gl-display-flex">
-        <span class="gl-flex-grow-1">{{ __('To Do') }}</span>
-        <gl-button :loading="todoUpdateInProgress" size="small" @click="handleTodoButtonClick">{{
-          todoActionText
-        }}</gl-button>
-      </div>
-      <div v-else class="block todo">
-        <gl-button
-          v-gl-tooltip:body.viewport.left
-          :title="todoActionText"
-          class="sidebar-collapsed-icon"
-          category="tertiary"
-          data-testid="collapsed-button"
-          @click="handleTodoButtonClick"
-        >
-          <gl-loading-icon v-if="todoUpdateInProgress" size="sm" />
-          <gl-icon v-else :name="todoIcon" :class="{ 'todo-undone': isTodoPending }" />
-        </gl-button>
-      </div>
-    </template>
     <labels-select-widget
       :iid="String(testCaseId)"
       :full-path="projectFullPath"
diff --git a/ee/app/assets/javascripts/test_case_show/components/test_case_sidebar_todo.vue b/ee/app/assets/javascripts/test_case_show/components/test_case_sidebar_todo.vue
new file mode 100644
index 0000000000000..a215d7f241d0a
--- /dev/null
+++ b/ee/app/assets/javascripts/test_case_show/components/test_case_sidebar_todo.vue
@@ -0,0 +1,82 @@
+<script>
+import { GlTooltipDirective as GlTooltip, GlButton, GlIcon, GlLoadingIcon } from '@gitlab/ui';
+import { __ } from '~/locale';
+import { TYPE_TEST_CASE, WORKSPACE_PROJECT } from '~/issues/constants';
+import TestCaseGraphQL from '../mixins/test_case_graphql';
+
+export default {
+  TYPE_TEST_CASE,
+  WORKSPACE_PROJECT,
+  components: {
+    GlButton,
+    GlIcon,
+    GlLoadingIcon,
+  },
+  directives: {
+    GlTooltip,
+  },
+  mixins: [TestCaseGraphQL],
+  inject: ['projectFullPath', 'testCaseId', 'canEditTestCase'],
+  props: {
+    sidebarExpanded: {
+      type: Boolean,
+      required: true,
+    },
+    todo: {
+      type: Object,
+      required: false,
+      default: null,
+    },
+  },
+  computed: {
+    isTodoPending() {
+      return this.todo?.state === 'pending';
+    },
+    todoUpdateInProgress() {
+      return this.$apollo.queries.testCase.loading || this.testCaseTodoUpdateInProgress;
+    },
+    todoActionText() {
+      return this.isTodoPending ? __('Mark as done') : __('Add a to do');
+    },
+    todoIcon() {
+      return this.isTodoPending ? 'todo-done' : 'todo-add';
+    },
+  },
+  methods: {
+    handleTodoButtonClick() {
+      if (this.isTodoPending) {
+        this.markTestCaseTodoDone();
+      } else {
+        this.addTestCaseAsTodo();
+      }
+    },
+  },
+};
+</script>
+
+<template>
+  <div v-if="canEditTestCase" class="gl-order-n1">
+    <div v-if="sidebarExpanded" data-testid="todo" class="todo gl-display-flex">
+      <gl-button
+        :loading="todoUpdateInProgress"
+        size="small"
+        data-testid="expanded-button"
+        @click="handleTodoButtonClick"
+        >{{ todoActionText }}</gl-button
+      >
+    </div>
+    <div v-else class="block todo">
+      <gl-button
+        v-gl-tooltip:body.viewport.left
+        :title="todoActionText"
+        class="sidebar-collapsed-icon"
+        category="tertiary"
+        data-testid="collapsed-button"
+        @click="handleTodoButtonClick"
+      >
+        <gl-loading-icon v-if="todoUpdateInProgress" size="sm" />
+        <gl-icon v-else :name="todoIcon" :class="{ 'todo-undone': isTodoPending }" />
+      </gl-button>
+    </div>
+  </div>
+</template>
diff --git a/ee/spec/features/projects/quality/test_case_show_spec.rb b/ee/spec/features/projects/quality/test_case_show_spec.rb
index 3ff8e8dfaa92d..2128447e9bf53 100644
--- a/ee/spec/features/projects/quality/test_case_show_spec.rb
+++ b/ee/spec/features/projects/quality/test_case_show_spec.rb
@@ -39,16 +39,16 @@
       end
 
       it 'shows action buttons' do
-        expect(page).to have_button('Archive test case')
+        expect(page).to have_button('Archive')
         expect(page).to have_link('New test case', href: new_project_quality_test_case_path(project))
         expect(page).not_to have_button('Options')
       end
 
       it 'archives test case' do
-        click_button('Archive test case')
+        click_button('Archive')
 
         expect(page).to have_css('.gl-badge', text: 'Archived')
-        expect(page).to have_button('Reopen test case')
+        expect(page).to have_button('Reopen')
       end
     end
 
diff --git a/ee/spec/frontend/test_case_show/components/test_case_show_root_spec.js b/ee/spec/frontend/test_case_show/components/test_case_show_root_spec.js
index 76fd056f0a9ed..cec3c3aa33e7a 100644
--- a/ee/spec/frontend/test_case_show/components/test_case_show_root_spec.js
+++ b/ee/spec/frontend/test_case_show/components/test_case_show_root_spec.js
@@ -13,6 +13,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
 
 import TestCaseShowRoot from 'ee/test_case_show/components/test_case_show_root.vue';
 import TestCaseSidebar from 'ee/test_case_show/components/test_case_sidebar.vue';
+import TestCaseSidebarTodo from 'ee/test_case_show/components/test_case_sidebar_todo.vue';
 import projectTestCase from 'ee/test_case_show/queries/project_test_case.query.graphql';
 import projectTestCaseTaskList from 'ee/test_case_show/queries/test_case_tasklist.query.graphql';
 import { mockCurrentUserTodo } from 'jest/vue_shared/issuable/list/mock_data';
@@ -45,7 +46,9 @@ describe('TestCaseShowRoot', () => {
   const findBadge = () => wrapper.findComponent(GlBadge);
   const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
   const findIssuableShow = () => wrapper.findComponent(IssuableShow);
+  const findIssuableEditButton = () => wrapper.findByTestId('edit-test-case');
   const findTestCaseSidebar = () => wrapper.findComponent(TestCaseSidebar);
+  const findTestCaseSidebarTodo = () => wrapper.findComponent(TestCaseSidebarTodo);
   const findToggleStateButton = () => wrapper.findByTestId('archive-test-case');
   const findToggleStateDropdownItem = () => wrapper.findByTestId('toggle-state-dropdown-item');
 
@@ -86,8 +89,8 @@ describe('TestCaseShowRoot', () => {
   describe('computed', () => {
     describe.each`
       state       | isTestCaseOpen | statusIcon              | statusBadgeText | testCaseActionTitle
-      ${'opened'} | ${true}        | ${'issue-open-m'}       | ${'Open'}       | ${'Archive test case'}
-      ${'closed'} | ${false}       | ${'mobile-issue-close'} | ${'Archived'}   | ${'Reopen test case'}
+      ${'opened'} | ${true}        | ${'issue-open-m'}       | ${'Open'}       | ${'Archive'}
+      ${'closed'} | ${false}       | ${'mobile-issue-close'} | ${'Archived'}   | ${'Reopen'}
     `(
       'when `testCase.state` is $state',
       ({ state, isTestCaseOpen, statusIcon, statusBadgeText, testCaseActionTitle }) => {
@@ -292,7 +295,6 @@ describe('TestCaseShowRoot', () => {
 
       it('renders IssuableShow', () => {
         const {
-          canEditTestCase,
           descriptionPreviewPath,
           descriptionHelpPath,
           updatePath,
@@ -308,8 +310,8 @@ describe('TestCaseShowRoot', () => {
           enableAutocomplete: true,
           enableTaskList: true,
           issuable: mockTestCase,
-          enableEdit: canEditTestCase,
-          taskCompletionStatus: {},
+          enableEdit: true,
+          taskCompletionStatus: null,
           taskListUpdatePath: updatePath,
           taskListLockVersion: lockVersion,
           workspaceType: 'project',
@@ -318,7 +320,7 @@ describe('TestCaseShowRoot', () => {
 
       describe('when IssuableShow emits `edit-issuable`', () => {
         beforeEach(() => {
-          findIssuableShow().vm.$emit('edit-issuable');
+          findIssuableEditButton().trigger('click');
         });
 
         it('renders edit-form-actions slot contents', () => {
@@ -389,7 +391,7 @@ describe('TestCaseShowRoot', () => {
 
       it('renders test-case-sidebar', () => {
         expect(findTestCaseSidebar().exists()).toBe(true);
-        expect(findTestCaseSidebar().props('todo')).toEqual(mockCurrentUserTodo);
+        expect(findTestCaseSidebarTodo().props('todo')).toEqual(mockCurrentUserTodo);
       });
 
       it('updates `sidebarExpanded` prop on `sidebar-toggle` event', async () => {
diff --git a/ee/spec/frontend/test_case_show/components/test_case_sidebar_spec.js b/ee/spec/frontend/test_case_show/components/test_case_sidebar_spec.js
index 6bebe8dacbe38..fd4a236ca509a 100644
--- a/ee/spec/frontend/test_case_show/components/test_case_sidebar_spec.js
+++ b/ee/spec/frontend/test_case_show/components/test_case_sidebar_spec.js
@@ -1,9 +1,7 @@
-import { GlButton, GlIcon, GlLoadingIcon } from '@gitlab/ui';
-
 import Vue, { nextTick } from 'vue';
 import VueApollo from 'vue-apollo';
 import TestCaseSidebar from 'ee/test_case_show/components/test_case_sidebar.vue';
-import { mockCurrentUserTodo, mockLabels } from 'jest/vue_shared/issuable/list/mock_data';
+import { mockLabels } from 'jest/vue_shared/issuable/list/mock_data';
 import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
 import ProjectSelect from '~/sidebar/components/move/issuable_move_dropdown.vue';
 import LabelsSelectWidget from '~/sidebar/components/labels/labels_select_widget/labels_select_root.vue';
@@ -13,7 +11,6 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
 import projectTestCase from 'ee/test_case_show/queries/project_test_case.query.graphql';
 
 import { TYPE_TEST_CASE, WORKSPACE_PROJECT } from '~/issues/constants';
-import waitForPromises from 'helpers/wait_for_promises';
 import { mockProvide, mockTaskCompletionResponse } from '../mock_data';
 
 Vue.use(VueApollo);
@@ -24,7 +21,6 @@ describe('TestCaseSidebar', () => {
   const createComponent = ({
     provide = {},
     sidebarExpanded = true,
-    todo = mockCurrentUserTodo,
     selectedLabels = mockLabels,
     moved = false,
   } = {}) => {
@@ -38,7 +34,6 @@ describe('TestCaseSidebar', () => {
       apolloProvider,
       propsData: {
         sidebarExpanded,
-        todo,
         selectedLabels,
         moved,
       },
@@ -49,8 +44,6 @@ describe('TestCaseSidebar', () => {
   const findSidebarConfidentialityWidget = () =>
     wrapper.findComponent(SidebarConfidentialityWidget);
   const findProjectSelect = () => wrapper.findComponent(ProjectSelect);
-  const findCollapsedTodoButton = () => wrapper.findByTestId('collapsed-button');
-  const findExpandedTodoEl = () => wrapper.findByTestId('todo');
 
   beforeEach(() => {
     setHTMLFixture('<aside class="right-sidebar"></aside>');
@@ -60,52 +53,6 @@ describe('TestCaseSidebar', () => {
     resetHTMLFixture();
   });
 
-  describe('To Do section', () => {
-    it('does not render', () => {
-      createComponent({
-        provide: {
-          canEditTestCase: false,
-        },
-      });
-
-      expect(findExpandedTodoEl().exists()).toBe(false);
-      expect(findCollapsedTodoButton().exists()).toBe(false);
-    });
-
-    describe('when expanded', () => {
-      it('renders expanded todo button', () => {
-        createComponent();
-
-        const todoEl = findExpandedTodoEl();
-
-        expect(todoEl.text()).toContain('To Do');
-        expect(todoEl.findComponent(GlButton).text()).toBe('Add a to do');
-      });
-    });
-
-    describe('when collapsed', () => {
-      it('renders collapsed todo button', async () => {
-        createComponent({
-          sidebarExpanded: false,
-        });
-        await waitForPromises();
-
-        const todoButton = findCollapsedTodoButton();
-
-        expect(todoButton.attributes('title')).toBe('Add a to do');
-        expect(todoButton.findComponent(GlIcon).exists()).toBe(true);
-      });
-
-      it('display loading icon', () => {
-        createComponent({
-          sidebarExpanded: false,
-        });
-
-        expect(findCollapsedTodoButton().findComponent(GlLoadingIcon).exists()).toBe(true);
-      });
-    });
-  });
-
   describe('Label select widget', () => {
     it('renders label-select', () => {
       createComponent();
diff --git a/ee/spec/frontend/test_case_show/components/test_case_sidebar_todo_spec.js b/ee/spec/frontend/test_case_show/components/test_case_sidebar_todo_spec.js
new file mode 100644
index 0000000000000..8b967451fa310
--- /dev/null
+++ b/ee/spec/frontend/test_case_show/components/test_case_sidebar_todo_spec.js
@@ -0,0 +1,121 @@
+import { GlButton, GlLoadingIcon } from '@gitlab/ui';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import TestCaseSidebarTodo from 'ee/test_case_show/components/test_case_sidebar_todo.vue';
+import { mockCurrentUserTodo } from 'jest/vue_shared/issuable/list/mock_data';
+import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import projectTestCase from 'ee/test_case_show/queries/project_test_case.query.graphql';
+import waitForPromises from 'helpers/wait_for_promises';
+import { mockProvide, mockTaskCompletionResponse } from '../mock_data';
+
+Vue.use(VueApollo);
+
+describe('TestCaseSidebarTodo', () => {
+  let wrapper;
+
+  const createComponent = ({
+    provide = {},
+    sidebarExpanded = true,
+    todo = mockCurrentUserTodo,
+  } = {}) => {
+    const apolloProvider = createMockApollo([[projectTestCase, mockTaskCompletionResponse]]);
+
+    wrapper = shallowMountExtended(TestCaseSidebarTodo, {
+      provide: {
+        ...mockProvide,
+        ...provide,
+      },
+      apolloProvider,
+      propsData: {
+        sidebarExpanded,
+        todo,
+      },
+      stubs: {
+        GlButton,
+      },
+    });
+  };
+
+  const findCollapsedTodoButton = () => wrapper.findByTestId('collapsed-button');
+  const findExpandedTodoButton = () => wrapper.findByTestId('expanded-button');
+  const findExpandedTodoEl = () => wrapper.findByTestId('todo');
+
+  beforeEach(() => {
+    setHTMLFixture('<aside class="right-sidebar"></aside>');
+  });
+
+  afterEach(() => {
+    resetHTMLFixture();
+  });
+
+  describe('To Do section', () => {
+    it('does not render when the test case can not be edited', () => {
+      createComponent({
+        provide: {
+          canEditTestCase: false,
+        },
+      });
+
+      expect(findExpandedTodoEl().exists()).toBe(false);
+      expect(findCollapsedTodoButton().exists()).toBe(false);
+    });
+
+    describe('when expanded', () => {
+      it('sidebarExpanded set to `true` renders expanded todo button', () => {
+        createComponent({
+          sidebarExpanded: true,
+        });
+
+        expect(findCollapsedTodoButton().exists()).toBe(false);
+        expect(findExpandedTodoButton().exists()).toBe(true);
+      });
+
+      it('renders expanded todo button', () => {
+        createComponent();
+
+        const todoEl = findExpandedTodoEl();
+        expect(todoEl.findComponent(GlButton).text()).toBe('Add a to do');
+      });
+
+      it('display loading icon', () => {
+        createComponent({
+          sidebarExpanded: true,
+        });
+
+        expect(findExpandedTodoButton().findComponent(GlLoadingIcon).exists()).toBe(true);
+      });
+    });
+
+    describe('when collapsed', () => {
+      it('sidebarExpanded set to `false` renders collapsed todo button', () => {
+        createComponent({
+          sidebarExpanded: false,
+        });
+
+        expect(findCollapsedTodoButton().exists()).toBe(true);
+        expect(findExpandedTodoButton().exists()).toBe(false);
+      });
+
+      it('renders collapsed todo button', async () => {
+        createComponent({
+          sidebarExpanded: false,
+        });
+        await waitForPromises();
+
+        const todoButton = findCollapsedTodoButton();
+
+        expect(todoButton.attributes('title')).toBe('Add a to do');
+      });
+
+      it('display loading icon', () => {
+        createComponent({
+          sidebarExpanded: false,
+        });
+
+        expect(findCollapsedTodoButton().findComponent(GlLoadingIcon).exists()).toBe(true);
+      });
+    });
+  });
+});
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 66468232633e2..13130f93e2696 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6398,9 +6398,6 @@ msgstr ""
 msgid "Archive project"
 msgstr ""
 
-msgid "Archive test case"
-msgstr ""
-
 msgid "Archived"
 msgstr ""
 
@@ -40184,9 +40181,6 @@ msgstr ""
 msgid "Reopen milestone"
 msgstr ""
 
-msgid "Reopen test case"
-msgstr ""
-
 msgid "Reopen this %{quick_action_target}"
 msgstr ""
 
diff --git a/spec/frontend/vue_shared/issuable/sidebar/components/issuable_sidebar_root_spec.js b/spec/frontend/vue_shared/issuable/sidebar/components/issuable_sidebar_root_spec.js
index f2509aead776c..d5c6ece8cb5a4 100644
--- a/spec/frontend/vue_shared/issuable/sidebar/components/issuable_sidebar_root_spec.js
+++ b/spec/frontend/vue_shared/issuable/sidebar/components/issuable_sidebar_root_spec.js
@@ -1,3 +1,4 @@
+import { GlButton, GlIcon } from '@gitlab/ui';
 import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
 import { nextTick } from 'vue';
 import Cookies from '~/lib/utils/cookies';
@@ -18,6 +19,10 @@ const createComponent = () => {
         <button class="js-todo">Todo</button>
       `,
     },
+    stubs: {
+      GlButton,
+      GlIcon,
+    },
   });
 };
 
@@ -62,9 +67,8 @@ describe('IssuableSidebarRoot', () => {
       const buttonEl = findToggleSidebarButton();
 
       expect(buttonEl.exists()).toBe(true);
-      expect(buttonEl.attributes('title')).toBe('Toggle sidebar');
-      expect(buttonEl.find('span').text()).toBe('Collapse sidebar');
-      expect(wrapper.findByTestId('icon-collapse').isVisible()).toBe(true);
+      expect(buttonEl.attributes('title')).toBe('Collapse sidebar');
+      expect(wrapper.findByTestId('chevron-double-lg-right-icon').isVisible()).toBe(true);
     });
 
     describe('when collapsing the sidebar', () => {
@@ -116,12 +120,12 @@ describe('IssuableSidebarRoot', () => {
       assertPageLayoutClasses({ isExpanded: false });
     });
 
-    it('renders sidebar toggle button with text and icon', () => {
+    it('renders sidebar toggle button with title and icon', () => {
       const buttonEl = findToggleSidebarButton();
 
       expect(buttonEl.exists()).toBe(true);
-      expect(buttonEl.attributes('title')).toBe('Toggle sidebar');
-      expect(wrapper.findByTestId('icon-expand').isVisible()).toBe(true);
+      expect(buttonEl.attributes('title')).toBe('Expand sidebar');
+      expect(wrapper.findByTestId('chevron-double-lg-left-icon').isVisible()).toBe(true);
     });
   });
 
-- 
GitLab