diff --git a/app/assets/javascripts/graphql_shared/queries/users_search_with_mr_permissions.graphql b/app/assets/javascripts/graphql_shared/queries/users_search_with_mr_permissions.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..2bd016feb190aa85ba66d50a2d11978556dd79a4
--- /dev/null
+++ b/app/assets/javascripts/graphql_shared/queries/users_search_with_mr_permissions.graphql
@@ -0,0 +1,24 @@
+#import "../fragments/user.fragment.graphql"
+#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
+
+query projectUsersSearchWithMRPermissions(
+  $search: String!
+  $fullPath: ID!
+  $mergeRequestId: MergeRequestID!
+) {
+  workspace: project(fullPath: $fullPath) {
+    id
+    users: projectMembers(search: $search, relations: [DIRECT, INHERITED, INVITED_GROUPS]) {
+      nodes {
+        id
+        mergeRequestInteraction(id: $mergeRequestId) {
+          canMerge
+        }
+        user {
+          ...User
+          ...UserAvailability
+        }
+      }
+    }
+  }
+}
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar.vue
index da9ff407faf978e192ab270cb10a1a24353b9099..240e12ee5979d0ba674da27d179431e581b339b3 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar.vue
@@ -1,5 +1,6 @@
 <script>
 import { GlIcon } from '@gitlab/ui';
+import { IssuableType } from '~/issues/constants';
 import { __, sprintf } from '~/locale';
 
 export default {
@@ -31,10 +32,11 @@ export default {
       );
     },
     isMergeRequest() {
-      return this.issuableType === 'merge_request';
+      return this.issuableType === IssuableType.MergeRequest;
     },
     hasMergeIcon() {
-      return this.isMergeRequest && !this.user.can_merge;
+      const canMerge = this.user.mergeRequestInteraction?.canMerge || this.user.can_merge;
+      return this.isMergeRequest && !canMerge;
     },
   },
 };
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
index 2a237e7ace03d9adff1a238d891845a5fa980599..578c344da02d736e66ef4e6460590e0fca2efe69 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
@@ -1,5 +1,6 @@
 <script>
 import { GlTooltipDirective, GlLink } from '@gitlab/ui';
+import { IssuableType } from '~/issues/constants';
 import { __ } from '~/locale';
 import { isUserBusy } from '~/set_status_modal/utils';
 import AssigneeAvatar from './assignee_avatar.vue';
@@ -71,7 +72,8 @@ export default {
   },
   computed: {
     cannotMerge() {
-      return this.issuableType === 'merge_request' && !this.user.can_merge;
+      const canMerge = this.user.mergeRequestInteraction?.canMerge || this.user.can_merge;
+      return this.issuableType === IssuableType.MergeRequest && !canMerge;
     },
     tooltipTitle() {
       const { name = '', availability = '' } = this.user;
diff --git a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
index 6a74ab83c220cd7af09f325e1c9b9ca214be8066..856687c00ae1df996ac6fba4e3a37561be6a6dc3 100644
--- a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
@@ -58,7 +58,7 @@ export default {
       return this.users.length > 2;
     },
     allAssigneesCanMerge() {
-      return this.users.every((user) => user.can_merge);
+      return this.users.every((user) => user.can_merge || user.mergeRequestInteraction?.canMerge);
     },
     sidebarAvatarCounter() {
       if (this.users.length > DEFAULT_MAX_COUNTER) {
@@ -77,7 +77,9 @@ export default {
         return '';
       }
 
-      const mergeLength = this.users.filter((u) => u.can_merge).length;
+      const mergeLength = this.users.filter(
+        (u) => u.can_merge || u.mergeRequestInteraction?.canMerge,
+      ).length;
 
       if (mergeLength === this.users.length) {
         return '';
diff --git a/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
index a3379784bc11cdb6a75370e9ce4bd5a79078aa4b..59a4eb54bbea819712b9dd98ef7b30d62226b910 100644
--- a/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
@@ -44,7 +44,7 @@ export default {
   <div class="gl-display-flex gl-flex-direction-column issuable-assignees">
     <div
       v-if="emptyUsers"
-      class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-mt-2 hide-collapsed"
+      class="gl-display-flex gl-align-items-center gl-text-gray-500 hide-collapsed"
       data-testid="none"
     >
       <span> {{ __('None') }}</span>
@@ -65,7 +65,7 @@ export default {
       v-else
       :users="users"
       :issuable-type="issuableType"
-      class="gl-text-gray-800 gl-mt-2 hide-collapsed"
+      class="gl-text-gray-800 hide-collapsed"
       @toggle-attention-requested="toggleAttentionRequested"
     />
   </div>
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
index 9c031ae64f8fdd3323500cfd74125d5ca63e1bc2..7743004a2933932e7797ef83d2f165aa88417721 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
@@ -1,6 +1,5 @@
 <script>
 import { GlDropdownItem } from '@gitlab/ui';
-import { cloneDeep } from 'lodash';
 import Vue from 'vue';
 import createFlash from '~/flash';
 import { IssuableType } from '~/issues/constants';
@@ -101,7 +100,10 @@ export default {
         }
         const issuable = data.workspace?.issuable;
         if (issuable) {
-          this.selected = cloneDeep(issuable.assignees.nodes);
+          this.selected = issuable.assignees.nodes.map((node) => ({
+            ...node,
+            canMerge: node.mergeRequestInteraction?.canMerge || false,
+          }));
         }
       },
       error() {
@@ -141,6 +143,7 @@ export default {
         username: gon?.current_username,
         name: gon?.current_user_fullname,
         avatarUrl: gon?.current_user_avatar_url,
+        canMerge: this.issuable?.userPermissions?.canMerge || false,
       };
     },
     signedIn() {
@@ -206,8 +209,8 @@ export default {
     expandWidget() {
       this.$refs.toggle.expand();
     },
-    focusSearch() {
-      this.$refs.userSelect.focusSearch();
+    showDropdown() {
+      this.$refs.userSelect.showDropdown();
     },
     showError() {
       createFlash({ message: __('An error occurred while fetching participants.') });
@@ -236,11 +239,11 @@ export default {
       :initial-loading="isAssigneesLoading"
       :title="assigneeText"
       :is-dirty="isDirty"
-      @open="focusSearch"
+      @open="showDropdown"
       @close="saveAssignees"
     >
       <template #collapsed>
-        <slot name="collapsed" :users="assignees" :on-click="expandWidget"></slot>
+        <slot name="collapsed" :users="assignees"></slot>
         <issuable-assignees
           :users="assignees"
           :issuable-type="issuableType"
@@ -256,12 +259,13 @@ export default {
           :text="$options.i18n.assignees"
           :header-text="$options.i18n.assignTo"
           :iid="iid"
+          :issuable-id="issuableId"
           :full-path="fullPath"
           :allow-multiple-assignees="allowMultipleAssignees"
           :current-user="currentUser"
           :issuable-type="issuableType"
           :is-editing="edit"
-          class="gl-w-full dropdown-menu-user"
+          class="gl-w-full dropdown-menu-user gl-mt-n3"
           @toggle="collapseWidget"
           @error="showError"
           @input="setDirtyState"
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_invite_members.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_invite_members.vue
index 8ef65ef7308b1cdbc66f44bca3b77a6ec3c53d33..28bc5afc1a403dd514e4eb6c527a117bdf5c0d09 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_invite_members.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_invite_members.vue
@@ -30,6 +30,6 @@ export default {
     :event="$options.dataTrackEvent"
     :label="$options.dataTrackLabel"
     :trigger-source="triggerSource"
-    classes="gl-display-block gl-pl-6 gl-hover-text-decoration-none gl-hover-text-blue-800!"
+    classes="gl-display-block gl-pl-0 gl-hover-text-decoration-none gl-hover-text-blue-800!"
   />
 </template>
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue
index e2a38a100b968fe9992189598552c85a38889e77..19f588b28be3d8fa7d256e171fb6a173905a319a 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_participant.vue
@@ -1,17 +1,24 @@
 <script>
-import { GlAvatarLabeled, GlAvatarLink } from '@gitlab/ui';
+import { GlAvatarLabeled, GlAvatarLink, GlIcon } from '@gitlab/ui';
+import { IssuableType } from '~/issues/constants';
 import { s__, sprintf } from '~/locale';
 
 export default {
   components: {
     GlAvatarLabeled,
     GlAvatarLink,
+    GlIcon,
   },
   props: {
     user: {
       type: Object,
       required: true,
     },
+    issuableType: {
+      type: String,
+      required: false,
+      default: IssuableType.Issue,
+    },
   },
   computed: {
     userLabel() {
@@ -22,6 +29,9 @@ export default {
         author: this.user.name,
       });
     },
+    hasCannotMergeIcon() {
+      return this.issuableType === IssuableType.MergeRequest && !this.user.canMerge;
+    },
   },
 };
 </script>
@@ -31,9 +41,19 @@ export default {
     <gl-avatar-labeled
       :size="32"
       :label="userLabel"
-      :sub-label="user.username"
+      :sub-label="`@${user.username}`"
       :src="user.avatarUrl || user.avatar || user.avatar_url"
-      class="gl-align-items-center"
-    />
+      class="gl-align-items-center gl-relative"
+    >
+      <template #meta>
+        <gl-icon
+          v-if="hasCannotMergeIcon"
+          name="warning-solid"
+          aria-hidden="true"
+          class="merge-icon"
+          :size="12"
+        />
+      </template>
+    </gl-avatar-labeled>
   </gl-avatar-link>
 </template>
diff --git a/app/assets/javascripts/sidebar/constants.js b/app/assets/javascripts/sidebar/constants.js
index f53579eb75ab93e6dbe7b750842cae7ccebc9d16..989dc574bc3f58bfc788611ef04e6d22f21aeafa 100644
--- a/app/assets/javascripts/sidebar/constants.js
+++ b/app/assets/javascripts/sidebar/constants.js
@@ -1,7 +1,8 @@
 import { s__, sprintf } from '~/locale';
 import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutation.graphql';
+import userSearchQuery from '~/graphql_shared/queries/users_search.query.graphql';
+import userSearchWithMRPermissionsQuery from '~/graphql_shared/queries/users_search_with_mr_permissions.graphql';
 import { IssuableType, WorkspaceType } from '~/issues/constants';
-import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
 import epicConfidentialQuery from '~/sidebar/queries/epic_confidential.query.graphql';
 import epicDueDateQuery from '~/sidebar/queries/epic_due_date.query.graphql';
 import epicParticipantsQuery from '~/sidebar/queries/epic_participants.query.graphql';
@@ -55,8 +56,6 @@ import projectIssueMilestoneMutation from './queries/project_issue_milestone.mut
 import projectIssueMilestoneQuery from './queries/project_issue_milestone.query.graphql';
 import projectMilestonesQuery from './queries/project_milestones.query.graphql';
 
-export const ASSIGNEES_DEBOUNCE_DELAY = DEFAULT_DEBOUNCE_AND_THROTTLE_MS;
-
 export const defaultEpicSort = 'TITLE_ASC';
 
 export const epicIidPattern = /^&(?<iid>\d+)$/;
@@ -93,6 +92,15 @@ export const participantsQueries = {
   },
 };
 
+export const userSearchQueries = {
+  [IssuableType.Issue]: {
+    query: userSearchQuery,
+  },
+  [IssuableType.MergeRequest]: {
+    query: userSearchWithMRPermissionsQuery,
+  },
+};
+
 export const confidentialityQueries = {
   [IssuableType.Issue]: {
     query: issueConfidentialQuery,
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index 32e27ca713751d3ec13de5119319073db0c0b01c..2a7d967cb612ab963abff41937bc557b6b0ae0cb 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -10,6 +10,7 @@ import {
   isInIssuePage,
   isInDesignPage,
   isInIncidentPage,
+  isInMRPage,
   parseBoolean,
 } from '~/lib/utils/common_utils';
 import { __ } from '~/locale';
@@ -136,6 +137,8 @@ function mountAssigneesComponent() {
   if (!el) return;
 
   const { id, iid, fullPath, editable } = getSidebarOptions();
+  const isIssuablePage = isInIssuePage() || isInIncidentPage() || isInDesignPage();
+  const issuableType = isIssuablePage ? IssuableType.Issue : IssuableType.MergeRequest;
   // eslint-disable-next-line no-new
   new Vue({
     el,
@@ -153,21 +156,16 @@ function mountAssigneesComponent() {
         props: {
           iid: String(iid),
           fullPath,
-          issuableType:
-            isInIssuePage() || isInIncidentPage() || isInDesignPage()
-              ? IssuableType.Issue
-              : IssuableType.MergeRequest,
+          issuableType,
           issuableId: id,
           allowMultipleAssignees: !el.dataset.maxAssignees,
         },
         scopedSlots: {
-          collapsed: ({ users, onClick }) =>
+          collapsed: ({ users }) =>
             createElement(CollapsedAssigneeList, {
               props: {
                 users,
-              },
-              nativeOn: {
-                click: onClick,
+                issuableType,
               },
             }),
         },
@@ -616,7 +614,7 @@ function mountCopyEmailComponent() {
 }
 
 const isAssigneesWidgetShown =
-  (isInIssuePage() || isInDesignPage()) && gon.features.issueAssigneesWidget;
+  (isInIssuePage() || isInDesignPage() || isInMRPage()) && gon.features.issueAssigneesWidget;
 
 export function mountSidebar(mediator, store) {
   initInviteMembersModal();
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql
index 81e19e48d753a833d01c4bf793560daf63550875..7127940bb05df29acaf1e7a9980ccd0e5b32c7c6 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql
+++ b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql
@@ -10,8 +10,14 @@ query getMrAssignees($fullPath: ID!, $iid: String!) {
         nodes {
           ...User
           ...UserAvailability
+          mergeRequestInteraction {
+            canMerge
+          }
         }
       }
+      userPermissions {
+        canMerge
+      }
     }
   }
 }
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql
index 77140ea36d89f68c6190ac09411b24d8556f21be..5fec2ccbdfb3a3f13068bc40c74073dbb12042df 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql
+++ b/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql
@@ -2,21 +2,18 @@
 #import "~/graphql_shared/fragments/user_availability.fragment.graphql"
 
 mutation mergeRequestSetAssignees($iid: String!, $assigneeUsernames: [String!]!, $fullPath: ID!) {
-  mergeRequestSetAssignees(
+  issuableSetAssignees: mergeRequestSetAssignees(
     input: { iid: $iid, assigneeUsernames: $assigneeUsernames, projectPath: $fullPath }
   ) {
-    mergeRequest {
+    issuable: mergeRequest {
       id
       assignees {
         nodes {
           ...User
           ...UserAvailability
-        }
-      }
-      participants {
-        nodes {
-          ...User
-          ...UserAvailability
+          mergeRequestInteraction {
+            canMerge
+          }
         }
       }
     }
diff --git a/app/assets/javascripts/vue_shared/components/user_select/user_select.vue b/app/assets/javascripts/vue_shared/components/user_select/user_select.vue
index b85cae0c64f1076a01db67757985a3c059402a29..9df5254155ede91377a4bbae9b06a578f7b79418 100644
--- a/app/assets/javascripts/vue_shared/components/user_select/user_select.vue
+++ b/app/assets/javascripts/vue_shared/components/user_select/user_select.vue
@@ -1,4 +1,5 @@
 <script>
+import { debounce } from 'lodash';
 import {
   GlDropdown,
   GlDropdownForm,
@@ -6,11 +7,14 @@ import {
   GlDropdownItem,
   GlSearchBoxByType,
   GlLoadingIcon,
+  GlTooltipDirective,
 } from '@gitlab/ui';
-import searchUsers from '~/graphql_shared/queries/users_search.query.graphql';
 import { __ } from '~/locale';
 import SidebarParticipant from '~/sidebar/components/assignees/sidebar_participant.vue';
-import { ASSIGNEES_DEBOUNCE_DELAY, participantsQueries } from '~/sidebar/constants';
+import { IssuableType } from '~/issues/constants';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
+import { participantsQueries, userSearchQueries } from '~/sidebar/constants';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
 
 export default {
   i18n: {
@@ -25,6 +29,9 @@ export default {
     SidebarParticipant,
     GlLoadingIcon,
   },
+  directives: {
+    GlTooltip: GlTooltipDirective,
+  },
   props: {
     headerText: {
       type: String,
@@ -58,13 +65,18 @@ export default {
     issuableType: {
       type: String,
       required: false,
-      default: 'issue',
+      default: IssuableType.Issue,
     },
     isEditing: {
       type: Boolean,
       required: false,
       default: true,
     },
+    issuableId: {
+      type: Number,
+      required: false,
+      default: null,
+    },
   },
   data() {
     return {
@@ -89,28 +101,35 @@ export default {
         };
       },
       update(data) {
-        return data.workspace?.issuable?.participants.nodes;
+        return data.workspace?.issuable?.participants.nodes.map((node) => ({
+          ...node,
+          canMerge: false,
+        }));
       },
       error() {
         this.$emit('error');
       },
     },
     searchUsers: {
-      query: searchUsers,
+      query() {
+        return userSearchQueries[this.issuableType].query;
+      },
       variables() {
-        return {
-          fullPath: this.fullPath,
-          search: this.search,
-          first: 20,
-        };
+        return this.searchUsersVariables;
       },
       skip() {
         return !this.isEditing;
       },
       update(data) {
-        return data.workspace?.users?.nodes.filter((x) => x?.user).map(({ user }) => user) || [];
+        return (
+          data.workspace?.users?.nodes
+            .filter((x) => x?.user)
+            .map((node) => ({
+              ...node.user,
+              canMerge: node.mergeRequestInteraction?.canMerge || false,
+            })) || []
+        );
       },
-      debounce: ASSIGNEES_DEBOUNCE_DELAY,
       error() {
         this.$emit('error');
         this.isSearching = false;
@@ -121,6 +140,23 @@ export default {
     },
   },
   computed: {
+    isMergeRequest() {
+      return this.issuableType === IssuableType.MergeRequest;
+    },
+    searchUsersVariables() {
+      const variables = {
+        fullPath: this.fullPath,
+        search: this.search,
+        first: 20,
+      };
+      if (!this.isMergeRequest) {
+        return variables;
+      }
+      return {
+        ...variables,
+        mergeRequestId: convertToGraphQLId('MergeRequest', this.issuableId),
+      };
+    },
     isLoading() {
       return this.$apollo.queries.searchUsers.loading || this.$apollo.queries.participants.loading;
     },
@@ -135,8 +171,8 @@ export default {
 
       // TODO this de-duplication is temporary (BE fix required)
       // https://gitlab.com/gitlab-org/gitlab/-/issues/327822
-      const mergedSearchResults = filteredParticipants
-        .concat(this.searchUsers)
+      const mergedSearchResults = this.searchUsers
+        .concat(filteredParticipants)
         .reduce(
           (acc, current) => (acc.some((user) => current.id === user.id) ? acc : [...acc, current]),
           [],
@@ -179,6 +215,7 @@ export default {
       return this.selectedFiltered.length === 0;
     },
   },
+
   watch: {
     // We need to add this watcher to track the moment when user is alredy typing
     // but query is still not started due to debounce
@@ -188,15 +225,21 @@ export default {
       }
     },
   },
+  created() {
+    this.debouncedSearchKeyUpdate = debounce(this.setSearchKey, DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+  },
   methods: {
     selectAssignee(user) {
       let selected = [...this.value];
       if (!this.allowMultipleAssignees) {
         selected = [user];
+        this.$emit('input', selected);
+        this.$refs.dropdown.hide();
+        this.$emit('toggle');
       } else {
         selected.push(user);
+        this.$emit('input', selected);
       }
-      this.$emit('input', selected);
     },
     unselect(name) {
       const selected = this.value.filter((user) => user.username !== name);
@@ -205,6 +248,9 @@ export default {
     focusSearch() {
       this.$refs.search.focusInput();
     },
+    showDropdown() {
+      this.$refs.dropdown.show();
+    },
     showDivider(list) {
       return list.length > 0 && this.isSearchEmpty;
     },
@@ -216,22 +262,37 @@ export default {
       const currentUser = usersCopy.find((user) => user.username === this.currentUser.username);
 
       if (currentUser) {
+        currentUser.canMerge = this.currentUser.canMerge;
         const index = usersCopy.indexOf(currentUser);
         usersCopy.splice(0, 0, usersCopy.splice(index, 1)[0]);
       }
 
       return usersCopy;
     },
+    setSearchKey(value) {
+      this.search = value.trim();
+    },
+    tooltipText(user) {
+      if (!this.isMergeRequest) {
+        return '';
+      }
+      return user.canMerge ? '' : __('Cannot merge');
+    },
   },
 };
 </script>
 
 <template>
-  <gl-dropdown class="show" :text="text" @toggle="$emit('toggle')">
+  <gl-dropdown ref="dropdown" :text="text" @toggle="$emit('toggle')" @shown="focusSearch">
     <template #header>
       <p class="gl-font-weight-bold gl-text-center gl-mt-2 gl-mb-4">{{ headerText }}</p>
       <gl-dropdown-divider />
-      <gl-search-box-by-type ref="search" v-model.trim="search" class="js-dropdown-input-field" />
+      <gl-search-box-by-type
+        ref="search"
+        :value="search"
+        class="js-dropdown-input-field"
+        @input="debouncedSearchKeyUpdate"
+      />
     </template>
     <gl-dropdown-form class="gl-relative gl-min-h-7">
       <gl-loading-icon
@@ -247,7 +308,7 @@ export default {
             :is-checked="selectedIsEmpty"
             :is-check-centered="true"
             data-testid="unassign"
-            @click="$emit('input', [])"
+            @click.native.capture.stop="$emit('input', [])"
           >
             <span :class="selectedIsEmpty ? 'gl-pl-0' : 'gl-pl-6'" class="gl-font-weight-bold">{{
               $options.i18n.unassigned
@@ -258,27 +319,44 @@ export default {
         <gl-dropdown-item
           v-for="item in selectedFiltered"
           :key="item.id"
+          v-gl-tooltip.left.viewport
+          :title="tooltipText(item)"
+          boundary="viewport"
           is-checked
           is-check-centered
           data-testid="selected-participant"
-          @click.stop="unselect(item.username)"
+          @click.native.capture.stop="unselect(item.username)"
         >
-          <sidebar-participant :user="item" />
+          <sidebar-participant :user="item" :issuable-type="issuableType" />
         </gl-dropdown-item>
         <template v-if="showCurrentUser">
           <gl-dropdown-divider />
-          <gl-dropdown-item data-testid="current-user" @click.stop="selectAssignee(currentUser)">
-            <sidebar-participant :user="currentUser" class="gl-pl-6!" />
+          <gl-dropdown-item
+            data-testid="current-user"
+            @click.native.capture.stop="selectAssignee(currentUser)"
+          >
+            <sidebar-participant
+              :user="currentUser"
+              :issuable-type="issuableType"
+              class="gl-pl-6!"
+            />
           </gl-dropdown-item>
         </template>
         <gl-dropdown-divider v-if="showDivider(unselectedFiltered)" />
         <gl-dropdown-item
           v-for="unselectedUser in unselectedFiltered"
           :key="unselectedUser.id"
+          v-gl-tooltip.left.viewport
+          :title="tooltipText(unselectedUser)"
+          boundary="viewport"
           data-testid="unselected-participant"
-          @click="selectAssignee(unselectedUser)"
+          @click.native.capture.stop="selectAssignee(unselectedUser)"
         >
-          <sidebar-participant :user="unselectedUser" class="gl-pl-6!" />
+          <sidebar-participant
+            :user="unselectedUser"
+            :issuable-type="issuableType"
+            class="gl-pl-6!"
+          />
         </gl-dropdown-item>
         <gl-dropdown-item v-if="noUsersFound" data-testid="empty-results" class="gl-pl-6!">
           {{ __('No matching results') }}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 6a27a0770c07c1c7a934d957971a68c698c4b097..c00af802c0645d8d70a72d17238e817757fbc574 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -108,12 +108,15 @@
   .merge-icon {
     color: $orange-400;
     position: absolute;
-    bottom: 0;
-    right: 0;
     filter: drop-shadow(0 0 0.5px $white) drop-shadow(0 0 1px $white) drop-shadow(0 0 2px $white);
   }
 }
 
+.assignee .merge-icon {
+  top: calc(50% + 0.25rem);
+  left: 1.275rem;
+}
+
 .reviewer .merge-icon {
   bottom: -3px;
   right: -3px;
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 04f311f58e9ccfd674ae869ae591c39f8639d8c8..5259bf90dd03a8d78cc697efddfbdde32925a6c0 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -43,6 +43,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
     push_frontend_feature_flag(:rebase_without_ci_ui, project, default_enabled: :yaml)
     push_frontend_feature_flag(:markdown_continue_lists, project, default_enabled: :yaml)
     push_frontend_feature_flag(:secure_vulnerability_training, project, default_enabled: :yaml)
+    push_frontend_feature_flag(:issue_assignees_widget, @project, default_enabled: :yaml)
     # Usage data feature flags
     push_frontend_feature_flag(:users_expanding_widgets_usage_data, project, default_enabled: :yaml)
     push_frontend_feature_flag(:diff_settings_usage_data, default_enabled: :yaml)
diff --git a/ee/app/assets/javascripts/boards/components/assignee_select.vue b/ee/app/assets/javascripts/boards/components/assignee_select.vue
index a40528cdf558bd71259bab8c5ca7c8da5e03d443..de518c61a0e56775cf3d0e3ff1f61eefdc4151fd 100644
--- a/ee/app/assets/javascripts/boards/components/assignee_select.vue
+++ b/ee/app/assets/javascripts/boards/components/assignee_select.vue
@@ -5,7 +5,7 @@ import { mapActions, mapGetters } from 'vuex';
 import searchGroupUsers from '~/graphql_shared/queries/group_users_search.query.graphql';
 import searchProjectUsers from '~/graphql_shared/queries/users_search.query.graphql';
 import { s__ } from '~/locale';
-import { ASSIGNEES_DEBOUNCE_DELAY } from '~/sidebar/constants';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
 import DropdownWidget from '~/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue';
 import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
 
@@ -59,7 +59,7 @@ export default {
         // https://gitlab.com/gitlab-org/gitlab/-/issues/329750
         return data.workspace?.users?.nodes.filter((x) => x?.user).map(({ user }) => user) || [];
       },
-      debounce: ASSIGNEES_DEBOUNCE_DELAY,
+      debounce: DEFAULT_DEBOUNCE_AND_THROTTLE_MS,
       error() {
         this.setError({ message: this.$options.i18n.errorSearchingUsers });
       },
diff --git a/ee/spec/features/merge_request/user_creates_multiple_assignees_mr_spec.rb b/ee/spec/features/merge_request/user_creates_multiple_assignees_mr_spec.rb
index 0279f10141169d2ee04e8553a439f4a24d78b269..af3802036619f80d2aeec89eacc600a4369c1f05 100644
--- a/ee/spec/features/merge_request/user_creates_multiple_assignees_mr_spec.rb
+++ b/ee/spec/features/merge_request/user_creates_multiple_assignees_mr_spec.rb
@@ -9,5 +9,15 @@
     stub_licensed_features(multiple_merge_request_assignees: true)
   end
 
-  it_behaves_like 'multiple assignees merge request', 'creates', 'Create merge request'
+  context 'when GraphQL assignees widget feature flag is disabled' do
+    before do
+      stub_feature_flags(issue_assignees_widget: false)
+    end
+
+    it_behaves_like 'multiple assignees merge request', 'creates', 'Create merge request'
+  end
+
+  context 'when GraphQL assignees widget feature flag is enabled' do
+    it_behaves_like 'multiple assignees widget merge request', 'creates', 'Create merge request'
+  end
 end
diff --git a/ee/spec/features/merge_request/user_edits_multiple_assignees_mr_spec.rb b/ee/spec/features/merge_request/user_edits_multiple_assignees_mr_spec.rb
index d1e03bf0e353a9e0abb3545e3af3c110882364d4..a355756410a6d76a1fc9e6c2d4ae0ee013437a5a 100644
--- a/ee/spec/features/merge_request/user_edits_multiple_assignees_mr_spec.rb
+++ b/ee/spec/features/merge_request/user_edits_multiple_assignees_mr_spec.rb
@@ -9,5 +9,15 @@
     stub_licensed_features(multiple_merge_request_assignees: true)
   end
 
-  it_behaves_like 'multiple assignees merge request', 'updates', 'Save changes'
+  context 'when GraphQL assignees widget feature flag is disabled' do
+    before do
+      stub_feature_flags(issue_assignees_widget: false)
+    end
+
+    it_behaves_like 'multiple assignees merge request', 'updates', 'Save changes'
+  end
+
+  context 'when GraphQL assignees widget feature flag is enabled' do
+    it_behaves_like 'multiple assignees widget merge request', 'updates', 'Save changes'
+  end
 end
diff --git a/ee/spec/frontend/boards/components/assignee_select_spec.js b/ee/spec/frontend/boards/components/assignee_select_spec.js
index d4a85f2b0ff95cd0c68ed043a9bb0ab0a20bdaec..facdf96eb1e7e7458a659f50d2fd303185077408 100644
--- a/ee/spec/frontend/boards/components/assignee_select_spec.js
+++ b/ee/spec/frontend/boards/components/assignee_select_spec.js
@@ -15,7 +15,7 @@ import { projectMembersResponse, groupMembersResponse, mockUser2 } from 'jest/si
 import defaultStore from '~/boards/stores';
 import searchGroupUsersQuery from '~/graphql_shared/queries/group_users_search.query.graphql';
 import searchProjectUsersQuery from '~/graphql_shared/queries/users_search.query.graphql';
-import { ASSIGNEES_DEBOUNCE_DELAY } from '~/sidebar/constants';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
 import DropdownWidget from '~/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue';
 
 Vue.use(VueApollo);
@@ -103,7 +103,7 @@ describe('Assignee select component', () => {
     it('trigger query and renders dropdown with returned users', async () => {
       findEditButton().vm.$emit('click');
       await waitForPromises();
-      jest.advanceTimersByTime(ASSIGNEES_DEBOUNCE_DELAY);
+      jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
       await waitForPromises();
       expect(usersQueryHandlerSuccess).toHaveBeenCalled();
 
@@ -140,7 +140,7 @@ describe('Assignee select component', () => {
 
       findEditButton().vm.$emit('click');
       await waitForPromises();
-      jest.advanceTimersByTime(ASSIGNEES_DEBOUNCE_DELAY);
+      jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
       await nextTick();
 
       expect(queryHandler).toHaveBeenCalled();
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 261e11148003397b062fcf46751720c815a06fc8..aaa478378a90ec575a63f9cd5a2d9fe7cd2296cb 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -106,6 +106,7 @@
       end
 
       context 'when GraphQL assignees widget feature flag is enabled' do
+        # TODO: Move to shared examples when feature flag is removed: https://gitlab.com/gitlab-org/gitlab/-/issues/328185
         context 'when a privileged user can invite' do
           it 'shows a link for inviting members and launches invite modal' do
             project.add_maintainer(user)
diff --git a/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb b/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb
index 5894ec923c20ce35eb392aa164cc5fe66a4fe705..92b9b7851480622a9fecbc74923a8c1bd1249929 100644
--- a/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb
+++ b/spec/features/merge_request/user_edits_assignees_sidebar_spec.rb
@@ -17,66 +17,172 @@
   let(:sidebar_assignee_block) { page.find('.js-issuable-sidebar .assignee') }
   let(:sidebar_assignee_avatar_link) { sidebar_assignee_block.find_all('a').find { |a| a['href'].include? assignee.username } }
   let(:sidebar_assignee_tooltip) { sidebar_assignee_avatar_link['title'] || '' }
-  let(:sidebar_assignee_dropdown_item) { sidebar_assignee_block.find(".dropdown-menu li[data-user-id=\"#{assignee.id}\"]") }
-  let(:sidebar_assignee_dropdown_tooltip) { sidebar_assignee_dropdown_item.find('a')['data-title'] || '' }
 
-  context 'when user is an owner' do
+  context 'when GraphQL assignees widget feature flag is disabled' do
+    let(:sidebar_assignee_dropdown_item) { sidebar_assignee_block.find(".dropdown-menu li[data-user-id=\"#{assignee.id}\"]") }
+    let(:sidebar_assignee_dropdown_tooltip) { sidebar_assignee_dropdown_item.find('a')['data-title'] || '' }
+
     before do
-      stub_const('Autocomplete::UsersFinder::LIMIT', users_find_limit)
+      stub_feature_flags(issue_assignees_widget: false)
+    end
 
-      sign_in(project.first_owner)
+    context 'when user is an owner' do
+      before do
+        stub_const('Autocomplete::UsersFinder::LIMIT', users_find_limit)
 
-      merge_request.assignees << assignee
+        sign_in(project.first_owner)
 
-      visit project_merge_request_path(project, merge_request)
+        merge_request.assignees << assignee
 
-      wait_for_requests
+        visit project_merge_request_path(project, merge_request)
+
+        wait_for_requests
+      end
+
+      shared_examples 'when assigned' do |expected_tooltip: ''|
+        it 'shows assignee name' do
+          expect(sidebar_assignee_block).to have_text(assignee.name)
+        end
+
+        it "shows assignee tooltip '#{expected_tooltip}'" do
+          expect(sidebar_assignee_tooltip).to eql(expected_tooltip)
+        end
+
+        context 'when edit is clicked' do
+          before do
+            sidebar_assignee_block.click_link('Edit')
+
+            wait_for_requests
+          end
+
+          it "shows assignee tooltip '#{expected_tooltip}" do
+            expect(sidebar_assignee_dropdown_tooltip).to eql(expected_tooltip)
+          end
+        end
+      end
+
+      context 'when assigned to maintainer' do
+        let(:assignee) { project_maintainers.last }
+
+        it_behaves_like 'when assigned', expected_tooltip: ''
+      end
+
+      context 'when assigned to developer' do
+        let(:assignee) { project_developers.last }
+
+        it_behaves_like 'when assigned', expected_tooltip: 'Cannot merge'
+      end
     end
 
-    shared_examples 'when assigned' do |expected_tooltip: ''|
-      it 'shows assignee name' do
-        expect(sidebar_assignee_block).to have_text(assignee.name)
+    context 'with invite members considerations' do
+      let_it_be(:user) { create(:user) }
+
+      before do
+        sign_in(user)
       end
 
-      it "shows assignee tooltip '#{expected_tooltip}'" do
-        expect(sidebar_assignee_tooltip).to eql(expected_tooltip)
+      include_examples 'issuable invite members' do
+        let(:issuable_path) { project_merge_request_path(project, merge_request) }
       end
+    end
+  end
+
+  context 'when GraphQL assignees widget feature flag is enabled' do
+    let(:sidebar_assignee_dropdown_item) { sidebar_assignee_block.find(".dropdown-item", text: assignee.username ) }
+    let(:sidebar_assignee_dropdown_tooltip) { sidebar_assignee_dropdown_item['title']}
+
+    context 'when user is an owner' do
+      before do
+        stub_const('Autocomplete::UsersFinder::LIMIT', users_find_limit)
+
+        sign_in(project.first_owner)
+
+        merge_request.assignees << assignee
 
-      context 'when edit is clicked' do
-        before do
-          sidebar_assignee_block.click_link('Edit')
+        visit project_merge_request_path(project, merge_request)
 
-          wait_for_requests
+        wait_for_requests
+      end
+
+      shared_examples 'when assigned' do |expected_tooltip: ''|
+        it 'shows assignee name' do
+          expect(sidebar_assignee_block).to have_text(assignee.name)
         end
 
-        it "shows assignee tooltip '#{expected_tooltip}" do
-          expect(sidebar_assignee_dropdown_tooltip).to eql(expected_tooltip)
+        it "shows assignee tooltip '#{expected_tooltip}'" do
+          expect(sidebar_assignee_tooltip).to eql(expected_tooltip)
+        end
+
+        context 'when edit is clicked' do
+          before do
+            open_assignees_dropdown
+          end
+
+          it "shows assignee tooltip '#{expected_tooltip}" do
+            expect(sidebar_assignee_dropdown_tooltip).to eql(expected_tooltip)
+          end
         end
       end
-    end
 
-    context 'when assigned to maintainer' do
-      let(:assignee) { project_maintainers.last }
+      context 'when assigned to maintainer' do
+        let(:assignee) { project_maintainers.last }
 
-      it_behaves_like 'when assigned', expected_tooltip: ''
-    end
+        it_behaves_like 'when assigned', expected_tooltip: ''
+      end
 
-    context 'when assigned to developer' do
-      let(:assignee) { project_developers.last }
+      context 'when assigned to developer' do
+        let(:assignee) { project_developers.last }
 
-      it_behaves_like 'when assigned', expected_tooltip: 'Cannot merge'
+        it_behaves_like 'when assigned', expected_tooltip: 'Cannot merge'
+      end
     end
-  end
 
-  context 'with invite members considerations' do
-    let_it_be(:user) { create(:user) }
+    context 'with invite members considerations' do
+      let_it_be(:user) { create(:user) }
 
-    before do
-      sign_in(user)
+      before do
+        sign_in(user)
+      end
+
+      # TODO: Move to shared examples when feature flag is removed: https://gitlab.com/gitlab-org/gitlab/-/issues/328185
+      context 'when a privileged user can invite' do
+        it 'shows a link for inviting members and launches invite modal' do
+          project.add_maintainer(user)
+          visit project_merge_request_path(project, merge_request)
+
+          open_assignees_dropdown
+
+          page.within '.dropdown-menu-user' do
+            expect(page).to have_link('Invite members')
+            expect(page).to have_selector('[data-track-action="click_invite_members"]')
+            expect(page).to have_selector('[data-track-label="edit_assignee"]')
+          end
+
+          click_link 'Invite members'
+
+          expect(page).to have_content("You're inviting members to the")
+        end
+      end
+
+      context 'when user cannot invite members in assignee dropdown' do
+        it 'shows author in assignee dropdown and no invite link' do
+          project.add_developer(user)
+          visit project_merge_request_path(project, merge_request)
+
+          open_assignees_dropdown
+
+          page.within '.dropdown-menu-user' do
+            expect(page).not_to have_link('Invite members')
+          end
+        end
+      end
     end
+  end
 
-    include_examples 'issuable invite members' do
-      let(:issuable_path) { project_merge_request_path(project, merge_request) }
+  def open_assignees_dropdown
+    page.within('.assignee') do
+      click_button('Edit')
+      wait_for_requests
     end
   end
 end
diff --git a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
index db1cffbd2cb14820938db85991ffd627f2a91c79..5fd364afbe46377c8b07841238f6861dc69b1224 100644
--- a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
+++ b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
@@ -1,4 +1,4 @@
-import { GlSearchBoxByType, GlDropdown } from '@gitlab/ui';
+import { GlSearchBoxByType } from '@gitlab/ui';
 import { shallowMount } from '@vue/test-utils';
 import Vue, { nextTick } from 'vue';
 
@@ -76,7 +76,16 @@ describe('Sidebar assignees widget', () => {
         SidebarEditableItem,
         UserSelect,
         GlSearchBoxByType,
-        GlDropdown,
+        GlDropdown: {
+          template: `
+            <div>
+              <slot name="footer"></slot>
+            </div>
+          `,
+          methods: {
+            show: jest.fn(),
+          },
+        },
       },
     });
   };
diff --git a/spec/frontend/sidebar/components/assignees/sidebar_participant_spec.js b/spec/frontend/sidebar/components/assignees/sidebar_participant_spec.js
index 88a5f4ea8b7f4bf2e273043c2c298821e804fedf..71424aaead38e4ee6522cedbf16c6df24bb59f14 100644
--- a/spec/frontend/sidebar/components/assignees/sidebar_participant_spec.js
+++ b/spec/frontend/sidebar/components/assignees/sidebar_participant_spec.js
@@ -1,5 +1,6 @@
-import { GlAvatarLabeled } from '@gitlab/ui';
+import { GlAvatarLabeled, GlIcon } from '@gitlab/ui';
 import { shallowMount } from '@vue/test-utils';
+import { IssuableType } from '~/issues/constants';
 import SidebarParticipant from '~/sidebar/components/assignees/sidebar_participant.vue';
 
 const user = {
@@ -13,14 +14,24 @@ describe('Sidebar participant component', () => {
   let wrapper;
 
   const findAvatar = () => wrapper.findComponent(GlAvatarLabeled);
+  const findIcon = () => wrapper.findComponent(GlIcon);
 
-  const createComponent = (status = null) => {
+  const createComponent = ({
+    status = null,
+    issuableType = IssuableType.Issue,
+    canMerge = false,
+  } = {}) => {
     wrapper = shallowMount(SidebarParticipant, {
       propsData: {
         user: {
           ...user,
+          canMerge,
           status,
         },
+        issuableType,
+      },
+      stubs: {
+        GlAvatarLabeled,
       },
     });
   };
@@ -29,15 +40,35 @@ describe('Sidebar participant component', () => {
     wrapper.destroy();
   });
 
-  it('when user is not busy', () => {
+  it('does not show `Busy` status when user is not busy', () => {
     createComponent();
 
     expect(findAvatar().props('label')).toBe(user.name);
   });
 
-  it('when user is busy', () => {
-    createComponent({ availability: 'BUSY' });
+  it('shows `Busy` status when user is busy', () => {
+    createComponent({ status: { availability: 'BUSY' } });
 
     expect(findAvatar().props('label')).toBe(`${user.name} (Busy)`);
   });
+
+  it('does not render a warning icon', () => {
+    createComponent();
+
+    expect(findIcon().exists()).toBe(false);
+  });
+
+  describe('when on merge request sidebar', () => {
+    it('when project member cannot merge', () => {
+      createComponent({ issuableType: IssuableType.MergeRequest });
+
+      expect(findIcon().exists()).toBe(true);
+    });
+
+    it('when project member can merge', () => {
+      createComponent({ issuableType: IssuableType.MergeRequest, canMerge: true });
+
+      expect(findIcon().exists()).toBe(false);
+    });
+  });
 });
diff --git a/spec/frontend/sidebar/mock_data.js b/spec/frontend/sidebar/mock_data.js
index 30972484a08fac96031e93aa0de644622490523a..fbca00636b6f99e3f96dba75b615d51f81c02761 100644
--- a/spec/frontend/sidebar/mock_data.js
+++ b/spec/frontend/sidebar/mock_data.js
@@ -428,7 +428,7 @@ const mockUser1 = {
 
 export const mockUser2 = {
   __typename: 'UserCore',
-  id: 'gid://gitlab/User/4',
+  id: 'gid://gitlab/User/5',
   avatarUrl: '/avatar2',
   name: 'rookie',
   username: 'rookie',
@@ -457,6 +457,33 @@ export const searchResponse = {
   },
 };
 
+export const searchResponseOnMR = {
+  data: {
+    workspace: {
+      __typename: 'Project',
+      id: '1',
+      users: {
+        nodes: [
+          {
+            id: 'gid://gitlab/User/1',
+            user: mockUser1,
+            mergeRequestInteraction: {
+              canMerge: true,
+            },
+          },
+          {
+            id: 'gid://gitlab/User/4',
+            user: mockUser2,
+            mergeRequestInteraction: {
+              canMerge: false,
+            },
+          },
+        ],
+      },
+    },
+  },
+};
+
 export const projectMembersResponse = {
   data: {
     workspace: {
diff --git a/spec/frontend/vue_shared/components/user_select_spec.js b/spec/frontend/vue_shared/components/user_select_spec.js
index 411a15e1c74d291fbc2a680d7377f692332fbbfb..cb4769109446391fa626a527c6ddc8a3cf2a64b8 100644
--- a/spec/frontend/vue_shared/components/user_select_spec.js
+++ b/spec/frontend/vue_shared/components/user_select_spec.js
@@ -1,4 +1,4 @@
-import { GlSearchBoxByType, GlDropdown } from '@gitlab/ui';
+import { GlSearchBoxByType } from '@gitlab/ui';
 import { shallowMount } from '@vue/test-utils';
 import { cloneDeep } from 'lodash';
 import Vue, { nextTick } from 'vue';
@@ -6,11 +6,14 @@ import VueApollo from 'vue-apollo';
 import createMockApollo from 'helpers/mock_apollo_helper';
 import waitForPromises from 'helpers/wait_for_promises';
 import searchUsersQuery from '~/graphql_shared/queries/users_search.query.graphql';
-import { ASSIGNEES_DEBOUNCE_DELAY } from '~/sidebar/constants';
+import searchUsersQueryOnMR from '~/graphql_shared/queries/users_search_with_mr_permissions.graphql';
+import { IssuableType } from '~/issues/constants';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
 import getIssueParticipantsQuery from '~/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql';
 import UserSelect from '~/vue_shared/components/user_select/user_select.vue';
 import {
   searchResponse,
+  searchResponseOnMR,
   projectMembersResponse,
   participantsQueryResponse,
 } from '../../sidebar/mock_data';
@@ -28,7 +31,7 @@ const assignee = {
 const mockError = jest.fn().mockRejectedValue('Error!');
 
 const waitForSearch = async () => {
-  jest.advanceTimersByTime(ASSIGNEES_DEBOUNCE_DELAY);
+  jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
   await nextTick();
   await waitForPromises();
 };
@@ -58,6 +61,7 @@ describe('User select dropdown', () => {
   } = {}) => {
     fakeApollo = createMockApollo([
       [searchUsersQuery, searchQueryHandler],
+      [searchUsersQueryOnMR, jest.fn().mockResolvedValue(searchResponseOnMR)],
       [getIssueParticipantsQuery, participantsQueryHandler],
     ]);
     wrapper = shallowMount(UserSelect, {
@@ -76,7 +80,18 @@ describe('User select dropdown', () => {
         ...props,
       },
       stubs: {
-        GlDropdown,
+        GlDropdown: {
+          template: `
+            <div>
+              <slot name="header"></slot>
+              <slot></slot>
+              <slot name="footer"></slot>
+            </div>
+          `,
+          methods: {
+            hide: jest.fn(),
+          },
+        },
       },
     });
   };
@@ -132,11 +147,19 @@ describe('User select dropdown', () => {
     expect(findSelectedParticipants()).toHaveLength(1);
   });
 
+  it('does not render a `Cannot merge` tooltip', async () => {
+    createComponent();
+    await waitForPromises();
+
+    expect(findUnselectedParticipants().at(0).attributes('title')).toBe('');
+  });
+
   describe('when search is empty', () => {
     it('renders a merged list of participants and project members', async () => {
       createComponent();
       await waitForPromises();
-      expect(findUnselectedParticipants()).toHaveLength(3);
+
+      expect(findUnselectedParticipants()).toHaveLength(4);
     });
 
     it('renders `Unassigned` link with the checkmark when there are no selected users', async () => {
@@ -162,7 +185,7 @@ describe('User select dropdown', () => {
         },
       });
       await waitForPromises();
-      findUnassignLink().vm.$emit('click');
+      findUnassignLink().trigger('click');
 
       expect(wrapper.emitted('input')).toEqual([[[]]]);
     });
@@ -175,7 +198,7 @@ describe('User select dropdown', () => {
       });
       await waitForPromises();
 
-      findSelectedParticipants().at(0).vm.$emit('click', new Event('click'));
+      findSelectedParticipants().at(0).trigger('click');
       expect(wrapper.emitted('input')).toEqual([[[]]]);
     });
 
@@ -187,8 +210,9 @@ describe('User select dropdown', () => {
       });
       await waitForPromises();
 
-      findUnselectedParticipants().at(0).vm.$emit('click');
-      expect(wrapper.emitted('input')).toEqual([
+      findUnselectedParticipants().at(0).trigger('click');
+
+      expect(wrapper.emitted('input')).toMatchObject([
         [
           [
             {
@@ -214,7 +238,7 @@ describe('User select dropdown', () => {
       });
       await waitForPromises();
 
-      findUnselectedParticipants().at(0).vm.$emit('click');
+      findUnselectedParticipants().at(0).trigger('click');
       expect(wrapper.emitted('input')[0][0]).toHaveLength(2);
     });
   });
@@ -232,7 +256,7 @@ describe('User select dropdown', () => {
       createComponent();
       await waitForPromises();
       findSearchField().vm.$emit('input', 'roo');
-      jest.advanceTimersByTime(ASSIGNEES_DEBOUNCE_DELAY);
+      jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
       await nextTick();
 
       expect(findParticipantsLoading().exists()).toBe(true);
@@ -273,4 +297,19 @@ describe('User select dropdown', () => {
       expect(findEmptySearchResults().exists()).toBe(true);
     });
   });
+
+  describe('when on merge request sidebar', () => {
+    beforeEach(() => {
+      createComponent({ props: { issuableType: IssuableType.MergeRequest, issuableId: 1 } });
+      return waitForPromises();
+    });
+
+    it('does not render a `Cannot merge` tooltip for a user that has merge permission', () => {
+      expect(findUnselectedParticipants().at(0).attributes('title')).toBe('');
+    });
+
+    it('renders a `Cannot merge` tooltip for a user that does not have merge permission', () => {
+      expect(findUnselectedParticipants().at(1).attributes('title')).toBe('Cannot merge');
+    });
+  });
 });
diff --git a/spec/support/shared_examples/features/multiple_assignees_widget_mr_shared_examples.rb b/spec/support/shared_examples/features/multiple_assignees_widget_mr_shared_examples.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bbde448a1a13e6121fe65a98e0245e7e92402c48
--- /dev/null
+++ b/spec/support/shared_examples/features/multiple_assignees_widget_mr_shared_examples.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'multiple assignees widget merge request' do |action, save_button_title|
+  it "#{action} a MR with multiple assignees", :js do
+    find('.js-assignee-search').click
+    page.within '.dropdown-menu-user' do
+      click_link user.name
+      click_link user2.name
+    end
+
+    # Extra click needed in order to toggle the dropdown
+    find('.js-assignee-search').click
+
+    expect(all('input[name="merge_request[assignee_ids][]"]', visible: false).map(&:value))
+      .to match_array([user.id.to_s, user2.id.to_s])
+
+    page.within '.js-assignee-search' do
+      expect(page).to have_content "#{user2.name} + 1 more"
+    end
+
+    click_button save_button_title
+
+    page.within '.issuable-sidebar' do
+      page.within '.assignee' do
+        expect(page).to have_content '2 Assignees'
+
+        click_button('Edit')
+
+        expect(page).to have_content user.name
+        expect(page).to have_content user2.name
+      end
+    end
+
+    page.within '.dropdown-menu-user' do
+      click_link user.name
+    end
+
+    page.within '.issuable-sidebar' do
+      page.within '.assignee' do
+        # Closing dropdown to persist
+        click_button('Apply')
+
+        expect(page).to have_content user2.name
+      end
+    end
+  end
+end