diff --git a/app/assets/javascripts/design_management/components/design_notes/design_note.vue b/app/assets/javascripts/design_management/components/design_notes/design_note.vue
index 022c5981d329533adee8f0c4317dd1eb201148c5..44888e59d6672c0171c76b86d739f7551c34a746 100644
--- a/app/assets/javascripts/design_management/components/design_notes/design_note.vue
+++ b/app/assets/javascripts/design_management/components/design_notes/design_note.vue
@@ -306,7 +306,6 @@ export default {
         <emoji-picker
           v-if="canAwardEmoji"
           toggle-class="note-action-button note-emoji-button btn-icon btn-default-tertiary"
-          boundary="viewport"
           :right="false"
           data-testid="note-emoji-button"
           @click="handleAwardEmoji"
@@ -333,6 +332,7 @@ export default {
           no-caret
           left
           :items="dropdownItems"
+          data-testid="more-actions"
         />
       </div>
     </div>
diff --git a/app/assets/javascripts/emoji/components/category.vue b/app/assets/javascripts/emoji/components/category.vue
index 80850475b9656f2a45f024b98c4cbce9da2e76bb..eefb487b79beebb4d605a053461f36d7d2c86ffe 100644
--- a/app/assets/javascripts/emoji/components/category.vue
+++ b/app/assets/javascripts/emoji/components/category.vue
@@ -43,7 +43,9 @@ export default {
 
 <template>
   <gl-intersection-observer class="gl-px-5 gl-h-full" @appear="categoryAppeared">
-    <div class="gl-top-0 gl-py-3 gl-w-full gl-z-index-1 emoji-picker-category-header">
+    <div
+      class="gl-top-0 gl-py-3 gl-w-full gl-z-index-1 gl-font-sm gl-ml-n2 emoji-picker-category-header"
+    >
       <b>{{ categoryTitle }}</b>
     </div>
     <template v-if="emojis.length">
diff --git a/app/assets/javascripts/emoji/components/emoji_group.vue b/app/assets/javascripts/emoji/components/emoji_group.vue
index bb0c3b0a694c2bb157fb78c0218d85883ceb08ab..a6eb96cf947df0339faf1ff21fd922e3aee40f79 100644
--- a/app/assets/javascripts/emoji/components/emoji_group.vue
+++ b/app/assets/javascripts/emoji/components/emoji_group.vue
@@ -36,6 +36,7 @@ export default {
         data-testid="emoji-button"
         button-text-classes="gl-display-none!"
         @click="clickEmoji(emoji)"
+        @keydown.enter="clickEmoji(emoji)"
       >
         <template #emoji>
           <gl-emoji :data-name="emoji" class="gl-mr-0!" />
diff --git a/app/assets/javascripts/emoji/components/picker.vue b/app/assets/javascripts/emoji/components/picker.vue
index 8b1784ae5510eb0d8817a0c5455774633adb3c64..2c7e424dff51a2ba73a34958e4697d4fe78f40de 100644
--- a/app/assets/javascripts/emoji/components/picker.vue
+++ b/app/assets/javascripts/emoji/components/picker.vue
@@ -1,9 +1,18 @@
 <!-- eslint-disable vue/multi-word-component-names -->
 <script>
-import { GlButton, GlIcon, GlDropdown, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui';
+import {
+  GlButton,
+  GlIcon,
+  GlDisclosureDropdown,
+  GlDisclosureDropdownGroup,
+  GlDisclosureDropdownItem,
+  GlSearchBoxByType,
+  GlTooltipDirective,
+} from '@gitlab/ui';
 import { findLastIndex } from 'lodash';
 import VirtualList from 'vue-virtual-scroll-list';
 import { getEmojiCategoryMap, state } from '~/emoji';
+import { __ } from '~/locale';
 import { CATEGORY_NAMES, CATEGORY_ICON_MAP, FREQUENTLY_USED_KEY } from '../constants';
 import Category from './category.vue';
 import EmojiList from './emoji_list.vue';
@@ -13,13 +22,17 @@ export default {
   components: {
     GlButton,
     GlIcon,
-    GlDropdown,
-    GlDropdownItem,
+    GlDisclosureDropdown,
+    GlDisclosureDropdownGroup,
+    GlDisclosureDropdownItem,
     GlSearchBoxByType,
     VirtualList,
     Category,
     EmojiList,
   },
+  directives: {
+    GlTooltip: GlTooltipDirective,
+  },
   inject: {
     newCustomEmojiPath: {
       default: '',
@@ -41,14 +54,10 @@ export default {
       required: false,
       default: true,
     },
-    boundary: {
-      type: String,
-      required: false,
-      default: '',
-    },
   },
   data() {
     return {
+      isVisible: false,
       currentCategory: 0,
       searchValue: '',
     };
@@ -64,6 +73,18 @@ export default {
         icon: CATEGORY_ICON_MAP[category],
       }));
     },
+    placement() {
+      return this.right ? 'right' : 'left';
+    },
+    newCustomEmoji() {
+      return {
+        text: __('Create new emoji'),
+        href: this.newCustomEmojiPath,
+        extraAttrs: {
+          'data-testid': 'create-new-emoji',
+        },
+      };
+    },
   },
   methods: {
     categoryAppeared(category) {
@@ -77,15 +98,12 @@ export default {
     },
     selectEmoji({ category, emoji }) {
       this.$emit('click', emoji);
-      this.$refs.dropdown.hide();
+      this.$refs.dropdown.close();
 
       if (category !== 'custom') {
         addToFrequentlyUsed(emoji);
       }
     },
-    getBoundaryElement() {
-      return this.boundary || document.querySelector('.content-wrapper') || 'scrollParent';
-    },
     onSearchInput() {
       this.$refs.virtualScoller.setScrollTop(0);
       this.$refs.virtualScoller.forceRender();
@@ -95,55 +113,72 @@ export default {
 
       this.currentCategory = findLastIndex(Object.values(categories), ({ top }) => offset >= top);
     },
+    onShow() {
+      this.isVisible = true;
+      this.$refs.searchValue.focusInput();
+
+      this.$emit('shown');
+    },
     onHide() {
+      this.isVisible = false;
       this.currentCategory = 0;
       this.searchValue = '';
       this.$emit('hidden');
     },
   },
+  i18n: {
+    addReaction: __('Add reaction'),
+  },
 };
 </script>
 
 <template>
   <div class="emoji-picker">
-    <gl-dropdown
+    <gl-disclosure-dropdown
       ref="dropdown"
-      :toggle-class="toggleClass"
-      :boundary="getBoundaryElement()"
       :class="dropdownClass"
-      menu-class="dropdown-extended-height"
-      category="secondary"
-      no-flip
-      :right="right"
-      lazy
-      @shown="$emit('shown')"
+      :placement="placement"
+      @shown="onShow"
       @hidden="onHide"
     >
-      <template #button-content>
-        <slot name="button-content">
-          <gl-icon class="award-control-icon-neutral gl-button-icon gl-icon" name="slight-smile" />
-          <gl-icon
-            class="award-control-icon-positive gl-button-icon gl-icon gl-left-3!"
-            name="smiley"
-          />
-          <gl-icon
-            class="award-control-icon-super-positive gl-button-icon gl-icon gl-left-3!"
-            name="smile"
-          />
-        </slot>
-        <span class="gl-sr-only">{{ __('Add reaction') }}</span>
+      <template #toggle>
+        <gl-button
+          v-gl-tooltip
+          :title="$options.i18n.addReaction"
+          :class="toggleClass"
+          class="gl-relative gl-h-full"
+          data-testid="add-reaction-button"
+        >
+          <slot name="button-content">
+            <span class="reaction-control-icon reaction-control-icon-neutral">
+              <gl-icon class="award-control-icon-neutral gl-button-icon" name="slight-smile" />
+            </span>
+            <span class="reaction-control-icon reaction-control-icon-positive">
+              <gl-icon class="award-control-icon-positive gl-button-icon" name="smiley" />
+            </span>
+            <span class="reaction-control-icon reaction-control-icon-super-positive">
+              <gl-icon class="award-control-icon-super-positive gl-button-icon" name="smile" />
+            </span>
+          </slot>
+        </gl-button>
+      </template>
+
+      <template #header>
+        <gl-search-box-by-type
+          ref="searchValue"
+          v-model="searchValue"
+          class="add-reaction-search gl-border-b-1 gl-border-b-solid gl-border-b-gray-200"
+          borderless
+          autofocus
+          debounce="500"
+          :aria-label="__('Search for an emoji')"
+          @input="onSearchInput"
+        />
       </template>
-      <gl-search-box-by-type
-        v-model="searchValue"
-        class="gl-mx-5! gl-mb-2!"
-        autofocus
-        debounce="500"
-        :aria-label="__('Search for an emoji')"
-        @input="onSearchInput"
-      />
+
       <div
         v-show="!searchValue"
-        class="gl-display-flex gl-mx-5 gl-border-b-solid gl-border-gray-100 gl-border-b-1"
+        class="award-list gl-display-flex gl-border-b-solid gl-border-gray-100 gl-border-b-1"
       >
         <gl-button
           v-for="(category, index) in categoryNames"
@@ -154,9 +189,10 @@ export default {
           :icon="category.icon"
           :aria-label="category.name"
           @click="scrollToCategory(category.name)"
+          @keydown.enter="scrollToCategory(category.name)"
         />
       </div>
-      <emoji-list :search-value="searchValue">
+      <emoji-list v-if="isVisible" :search-value="searchValue">
         <template #default="{ filteredCategories }">
           <virtual-list
             ref="virtualScoller"
@@ -176,11 +212,10 @@ export default {
           </virtual-list>
         </template>
       </emoji-list>
-      <template v-if="newCustomEmojiPath" #footer>
-        <gl-dropdown-item :href="newCustomEmojiPath">
-          {{ __('Create new emoji') }}
-        </gl-dropdown-item>
-      </template>
-    </gl-dropdown>
+
+      <gl-disclosure-dropdown-group v-if="newCustomEmojiPath" bordered class="gl-mt-0!">
+        <gl-disclosure-dropdown-item :item="newCustomEmoji" />
+      </gl-disclosure-dropdown-group>
+    </gl-disclosure-dropdown>
   </div>
 </template>
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index 5a1795d747915617be77adb14796774cec7ecce0..963557ff1b3d81afe7ae2f1e0fc1b2ba977affa6 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -22,7 +22,6 @@ import TimelineEventButton from './note_actions/timeline_event_button.vue';
 
 export default {
   i18n: {
-    addReactionLabel: __('Add reaction'),
     editCommentLabel: __('Edit comment'),
     deleteCommentLabel: __('Delete comment'),
     moreActionsLabel: __('More actions'),
@@ -317,8 +316,6 @@ export default {
     />
     <emoji-picker
       v-if="canAwardEmoji"
-      v-gl-tooltip
-      :title="$options.i18n.addReactionLabel"
       toggle-class="note-action-button note-emoji-button btn-icon btn-default-tertiary"
       data-testid="note-emoji-button"
       @click="setAwardEmoji"
diff --git a/app/assets/javascripts/set_status_modal/set_status_form.vue b/app/assets/javascripts/set_status_modal/set_status_form.vue
index 19062e10758e30143c1693b922aacc5d1870c243..8786dccecc4fe117a8346d836827203be729921c 100644
--- a/app/assets/javascripts/set_status_modal/set_status_form.vue
+++ b/app/assets/javascripts/set_status_modal/set_status_form.vue
@@ -185,7 +185,6 @@ export default {
         <emoji-picker
           dropdown-class="gl-h-full"
           toggle-class="btn emoji-menu-toggle-button gl-px-4! gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
-          boundary="viewport"
           :right="false"
           @click="handleEmojiClick"
         >
diff --git a/app/assets/javascripts/vue_shared/components/awards_list.vue b/app/assets/javascripts/vue_shared/components/awards_list.vue
index 3c19df9c19689dd5e22916a37e8ab674bc0ff20e..1750551db7bbfb55ef96ec1b67f91b357ad7d647 100644
--- a/app/assets/javascripts/vue_shared/components/awards_list.vue
+++ b/app/assets/javascripts/vue_shared/components/awards_list.vue
@@ -1,5 +1,5 @@
 <script>
-import { GlIcon, GlButton, GlTooltipDirective } from '@gitlab/ui';
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
 import { groupBy } from 'lodash';
 import SafeHtml from '~/vue_shared/directives/safe_html';
 import EmojiPicker from '~/emoji/components/picker.vue';
@@ -13,7 +13,6 @@ const NO_USER_ID = -1;
 export default {
   components: {
     GlButton,
-    GlIcon,
     EmojiPicker,
   },
   directives: {
@@ -45,11 +44,6 @@ export default {
       required: false,
       default: 'selected',
     },
-    boundary: {
-      type: String,
-      required: false,
-      default: '',
-    },
   },
   data() {
     return {
@@ -197,28 +191,13 @@ export default {
     </gl-button>
     <div v-if="canAwardEmoji" class="award-menu-holder gl-my-2">
       <emoji-picker
-        v-gl-tooltip.viewport
-        :title="__('Add reaction')"
         :toggle-class="['add-reaction-button btn-icon gl-relative!', { 'is-active': isMenuOpen }]"
         :right="false"
-        :boundary="boundary"
         data-testid="emoji-picker"
         @click="handleAward"
         @shown="setIsMenuOpen(true)"
         @hidden="setIsMenuOpen(false)"
-      >
-        <template #button-content>
-          <span class="reaction-control-icon reaction-control-icon-neutral">
-            <gl-icon name="slight-smile" />
-          </span>
-          <span class="reaction-control-icon reaction-control-icon-positive">
-            <gl-icon name="smiley" />
-          </span>
-          <span class="reaction-control-icon reaction-control-icon-super-positive">
-            <gl-icon name="smile" />
-          </span>
-        </template>
-      </emoji-picker>
+      />
     </div>
   </div>
 </template>
diff --git a/app/assets/javascripts/work_items/components/notes/work_item_note_awards_list.vue b/app/assets/javascripts/work_items/components/notes/work_item_note_awards_list.vue
index c3b3c0e6db7f7dc89c178bbc5ebd943c16b58ecf..ab93ab3cbdd01e4e6c67865844c1a4c880ef28b5 100644
--- a/app/assets/javascripts/work_items/components/notes/work_item_note_awards_list.vue
+++ b/app/assets/javascripts/work_items/components/notes/work_item_note_awards_list.vue
@@ -29,9 +29,6 @@ export default {
     },
   },
   computed: {
-    awardsListBoundary() {
-      return this.isModal ? '.modal-body' : '';
-    },
     awards() {
       return this.note.awardEmoji.nodes.map((award) => {
         return {
@@ -93,7 +90,6 @@ export default {
     :awards="awards"
     :can-award-emoji="hasAwardEmojiPermission"
     :current-user-id="currentUserId"
-    :boundary="awardsListBoundary"
     class="gl-px-2"
     @award="handleAward($event)"
   />
diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss
index e11fa7d880180521a07f955d5d355bcf04cc4ed2..5f68ff64759795ace893ec363747d655ee094c0f 100644
--- a/app/assets/stylesheets/framework/awards.scss
+++ b/app/assets/stylesheets/framework/awards.scss
@@ -269,7 +269,6 @@
   // other gl-buttons despite all child elements being set to
   // `position:absolute`
 
-
   .reaction-control-icon {
     .gl-icon {
       height: $default-icon-size;
@@ -335,3 +334,25 @@
     }
   }
 }
+
+.add-reaction-search {
+  $input-focus-ring-border-radius: calc(#{$gl-border-radius-large} - #{$gl-border-size-1});
+
+  input {
+    border-top-left-radius: $input-focus-ring-border-radius !important;
+    border-top-right-radius: $input-focus-ring-border-radius !important;
+  }
+}
+
+.award-list .gl-button:focus {
+  @include gl-focus(
+    $outline: true,
+    $outline-offset: -2px,
+    $important: true
+  );
+  box-shadow: none !important;
+}
+
+.design-note.note-form .emoji-picker .gl-new-dropdown-panel {
+  left: 0 !important;
+}
diff --git a/app/assets/stylesheets/framework/emojis.scss b/app/assets/stylesheets/framework/emojis.scss
index 779567978175591c0e910d7d76bef068182ac2c5..39b60aa3fae125ce3391227ba2c064fd525259a3 100644
--- a/app/assets/stylesheets/framework/emojis.scss
+++ b/app/assets/stylesheets/framework/emojis.scss
@@ -50,8 +50,9 @@ gl-emoji {
   }
 }
 
-.emoji-picker .gl-dropdown .dropdown-menu {
-  width: 350px;
+.emoji-picker .gl-dropdown .dropdown-menu,
+.emoji-picker .gl-new-dropdown .gl-new-dropdown-panel {
+  width: 350px !important;
 }
 
 .emoji-picker-category-tab {
diff --git a/spec/features/merge_request/user_creates_custom_emoji_spec.rb b/spec/features/merge_request/user_creates_custom_emoji_spec.rb
index 35593836dab8bdeb5fe34544e1cc4ecb79bc4616..142a2b20e3d1ad9a468d2f3fdf9554b2e1be569a 100644
--- a/spec/features/merge_request/user_creates_custom_emoji_spec.rb
+++ b/spec/features/merge_request/user_creates_custom_emoji_spec.rb
@@ -20,8 +20,8 @@
       wait_for_requests
     end
 
-    it 'shows link to create custom emoji' do
-      first('.add-reaction-button').click
+    it 'shows link to create custom emoji', :js do
+      find_by_testid('add-reaction-button').click
 
       wait_for_requests
 
@@ -48,8 +48,8 @@
       wait_for_requests
     end
 
-    it 'shows link to create custom emoji' do
-      first('.add-reaction-button').click
+    it 'shows link to create custom emoji', :js do
+      find_by_testid('add-reaction-button').click
 
       wait_for_requests
 
diff --git a/spec/features/projects/issues/design_management/user_views_design_spec.rb b/spec/features/projects/issues/design_management/user_views_design_spec.rb
index bd9d1092e17a817a975a4081268094afc8ead3ce..ab95b20ad40f8474a9d3093de59871389db4f919 100644
--- a/spec/features/projects/issues/design_management/user_views_design_spec.rb
+++ b/spec/features/projects/issues/design_management/user_views_design_spec.rb
@@ -16,7 +16,7 @@ def add_diff_note_emoji(diff_note, emoji_name)
     page.within(first(".image-notes li#note_#{diff_note.id}.design-note")) do
       page.find('[data-testid="note-emoji-button"] .note-emoji-button').click
 
-      page.within('ul.dropdown-menu') do
+      page.within('.emoji-picker') do
         page.find('input[type="search"]').set(emoji_name)
         page.find('button[data-testid="emoji-button"]:first-child').click
       end
diff --git a/spec/frontend/design_management/components/design_notes/design_note_spec.js b/spec/frontend/design_management/components/design_notes/design_note_spec.js
index 7ff488f7fcb4f00062b0ef2cfc5f02e5e9125583..8c9bf8da3fb0d72c43e9afb8fd2b5db4c896c866 100644
--- a/spec/frontend/design_management/components/design_notes/design_note_spec.js
+++ b/spec/frontend/design_management/components/design_notes/design_note_spec.js
@@ -48,7 +48,7 @@ describe('Design note component', () => {
   const findReplyForm = () => wrapper.findComponent(DesignReplyForm);
   const findEditButton = () => wrapper.findByTestId('note-edit');
   const findNoteContent = () => wrapper.findByTestId('note-text');
-  const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
+  const findDropdown = () => wrapper.findByTestId('more-actions');
   const findDropdownItems = () => findDropdown().findAllComponents(GlDisclosureDropdownItem);
   const findEditDropdownItem = () => findDropdownItems().at(0);
   const findCopyLinkDropdownItem = () => findDropdownItems().at(1);
@@ -353,7 +353,6 @@ describe('Design note component', () => {
 
       expect(emojiPicker.exists()).toBe(true);
       expect(emojiPicker.props()).toMatchObject({
-        boundary: 'viewport',
         right: false,
       });
     });
diff --git a/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js b/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js
index 7ae2884170e1dc8e8b70a722000327001308f597..2542fc237a10f2a4f2d75f3e2c240a89a8dc69bc 100644
--- a/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js
+++ b/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js
@@ -99,7 +99,6 @@ describe('SetStatusModalWrapper', () => {
     it('renders emoji picker dropdown with custom positioning', () => {
       expect(getEmojiPicker().props()).toMatchObject({
         right: false,
-        boundary: 'viewport',
       });
     });
 
diff --git a/spec/frontend/work_items/components/work_item_award_emoji_spec.js b/spec/frontend/work_items/components/work_item_award_emoji_spec.js
index a756bfa6889b6343fad4a1360aa27e5bf2057772..e6c2743975bf1edc92cf3177d6c2b7eaa2d26043 100644
--- a/spec/frontend/work_items/components/work_item_award_emoji_spec.js
+++ b/spec/frontend/work_items/components/work_item_award_emoji_spec.js
@@ -144,7 +144,6 @@ describe('WorkItemAwardEmoji component', () => {
 
     expect(findAwardsList().exists()).toBe(true);
     expect(findAwardsList().props()).toEqual({
-      boundary: '',
       canAwardEmoji: true,
       currentUserId: 5,
       defaultAwards: [EMOJI_THUMBSUP, EMOJI_THUMBSDOWN],