diff --git a/app/assets/javascripts/search/sidebar/components/issues_filters.vue b/app/assets/javascripts/search/sidebar/components/issues_filters.vue
index 2ab5dfb8deaf744d140fc1b87fbed10da6f5ab74..8928f80d83a56cb9e1f66992fd2540e6fc9b3a32 100644
--- a/app/assets/javascripts/search/sidebar/components/issues_filters.vue
+++ b/app/assets/javascripts/search/sidebar/components/issues_filters.vue
@@ -2,6 +2,7 @@
 import { GlButton, GlLink } from '@gitlab/ui';
 import { mapActions, mapState, mapGetters } from 'vuex';
 import Tracking from '~/tracking';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 import {
   HR_DEFAULT_CLASSES,
   TRACKING_ACTION_CLICK,
@@ -12,6 +13,8 @@ import {
 import { confidentialFilterData } from '../constants/confidential_filter_data';
 import { stateFilterData } from '../constants/state_filter_data';
 import ConfidentialityFilter from './confidentiality_filter.vue';
+import { labelFilterData } from './label_filter/data';
+import LabelFilter from './label_filter/index.vue';
 import StatusFilter from './status_filter.vue';
 
 export default {
@@ -21,7 +24,9 @@ export default {
     GlLink,
     StatusFilter,
     ConfidentialityFilter,
+    LabelFilter,
   },
+  mixins: [glFeatureFlagsMixin()],
   computed: {
     ...mapState(['urlQuery', 'sidebarDirty', 'useNewNavigation']),
     ...mapGetters(['currentScope']),
@@ -34,6 +39,12 @@ export default {
     showStatusFilter() {
       return Object.values(stateFilterData.scopes).includes(this.currentScope);
     },
+    showLabelFilter() {
+      return (
+        Object.values(labelFilterData.scopes).includes(this.currentScope) &&
+        this.glFeatures.searchIssueLabelAggregation
+      );
+    },
     hrClasses() {
       return [...HR_DEFAULT_CLASSES, 'gl-display-none', 'gl-md-display-block'];
     },
@@ -61,7 +72,8 @@ export default {
     <hr v-if="!useNewNavigation" :class="hrClasses" />
     <status-filter v-if="showStatusFilter" class="gl-mb-5" />
     <confidentiality-filter v-if="showConfidentialityFilter" class="gl-mb-5" />
-    <div class="gl-display-flex gl-align-items-center gl-mt-5">
+    <label-filter v-if="showLabelFilter" />
+    <div class="gl-display-flex gl-align-items-center gl-mt-4">
       <gl-button category="primary" variant="confirm" type="submit" :disabled="!sidebarDirty">
         {{ __('Apply') }}
       </gl-button>
diff --git a/app/assets/javascripts/search/sidebar/components/label_filter/index.vue b/app/assets/javascripts/search/sidebar/components/label_filter/index.vue
new file mode 100644
index 0000000000000000000000000000000000000000..74855482b5d94ebe271b2d5910ad3e7ddcc7bd89
--- /dev/null
+++ b/app/assets/javascripts/search/sidebar/components/label_filter/index.vue
@@ -0,0 +1,291 @@
+<script>
+import {
+  GlSearchBoxByType,
+  GlLabel,
+  GlLoadingIcon,
+  GlDropdownDivider,
+  GlDropdownSectionHeader,
+  GlFormCheckboxGroup,
+  GlDropdownForm,
+  GlAlert,
+  GlOutsideDirective as Outside,
+} from '@gitlab/ui';
+import { mapActions, mapState, mapGetters } from 'vuex';
+import { uniq } from 'lodash';
+import { rgbFromHex } from '@gitlab/ui/dist/utils/utils';
+import { slugify } from '~/lib/utils/text_utility';
+import { s__, sprintf } from '~/locale';
+
+import DropdownKeyboardNavigation from '~/vue_shared/components/dropdown_keyboard_navigation.vue';
+
+import {
+  SEARCH_INPUT_DESCRIBE_BY_NO_DROPDOWN,
+  SEARCH_INPUT_DESCRIBE_BY_WITH_DROPDOWN,
+  SEARCH_DESCRIBED_BY_DEFAULT,
+  SEARCH_DESCRIBED_BY_UPDATED,
+  SEARCH_RESULTS_LOADING,
+} from '~/vue_shared/global_search/constants';
+
+import { HR_DEFAULT_CLASSES, ONLY_SHOW_MD } from '../../constants';
+import LabelDropdownItems from './label_dropdown_items.vue';
+
+import {
+  FIRST_DROPDOWN_INDEX,
+  SEARCH_BOX_INDEX,
+  SEARCH_RESULTS_DESCRIPTION,
+  SEARCH_INPUT_DESCRIPTION,
+  labelFilterData,
+} from './data';
+
+import { trackSelectCheckbox, trackOpenDropdown } from './tracking';
+
+export default {
+  name: 'LabelFilter',
+  directives: { Outside },
+  components: {
+    DropdownKeyboardNavigation,
+    GlSearchBoxByType,
+    LabelDropdownItems,
+    GlLabel,
+    GlDropdownDivider,
+    GlDropdownSectionHeader,
+    GlFormCheckboxGroup,
+    GlDropdownForm,
+    GlLoadingIcon,
+    GlAlert,
+  },
+  data() {
+    return {
+      currentFocusIndex: SEARCH_BOX_INDEX,
+      isFocused: false,
+    };
+  },
+  i18n: {
+    SEARCH_LABELS: s__('GlobalSearch|Search labels'),
+    DROPDOWN_HEADER: s__('GlobalSearch|Label(s)'),
+    AGGREGATIONS_ERROR_MESSAGE: s__('GlobalSearch|Fetching aggregations error.'),
+    SEARCH_DESCRIBED_BY_DEFAULT,
+    SEARCH_RESULTS_LOADING,
+    SEARCH_DESCRIBED_BY_UPDATED,
+    SEARCH_INPUT_DESCRIBE_BY_WITH_DROPDOWN,
+    SEARCH_INPUT_DESCRIBE_BY_NO_DROPDOWN,
+  },
+  computed: {
+    ...mapState(['useSidebarNavigation', 'searchLabelString', 'query', 'aggregations']),
+    ...mapGetters([
+      'filteredLabels',
+      'filteredUnselectedLabels',
+      'filteredAppliedSelectedLabels',
+      'appliedSelectedLabels',
+      'filteredUnappliedSelectedLabels',
+    ]),
+    searchInputDescribeBy() {
+      if (this.isLoggedIn) {
+        return this.$options.i18n.SEARCH_INPUT_DESCRIBE_BY_WITH_DROPDOWN;
+      }
+      return this.$options.i18n.SEARCH_INPUT_DESCRIBE_BY_NO_DROPDOWN;
+    },
+    dropdownResultsDescription() {
+      if (!this.showSearchDropdown) {
+        return ''; // This allows aria-live to see register an update when the dropdown is shown
+      }
+
+      if (this.showDefaultItems) {
+        return sprintf(this.$options.i18n.SEARCH_DESCRIBED_BY_DEFAULT, {
+          count: this.filteredLabels.length,
+        });
+      }
+
+      return this.loading
+        ? this.$options.i18n.SEARCH_RESULTS_LOADING
+        : sprintf(this.$options.i18n.SEARCH_DESCRIBED_BY_UPDATED, {
+            count: this.filteredLabels.length,
+          });
+    },
+    currentFocusedOption() {
+      return this.filteredLabels[this.currentFocusIndex] || null;
+    },
+    currentFocusedId() {
+      return `${slugify(this.currentFocusedOption?.parent_full_name || 'undefined-name')}_${slugify(
+        this.currentFocusedOption?.title || 'undefined-title',
+      )}`;
+    },
+    defaultIndex() {
+      if (this.showDefaultItems) {
+        return SEARCH_BOX_INDEX;
+      }
+      return FIRST_DROPDOWN_INDEX;
+    },
+    hasSelectedLabels() {
+      return this.filteredAppliedSelectedLabels.length > 0;
+    },
+    hasUnselectedLabels() {
+      return this.filteredUnselectedLabels.length > 0;
+    },
+    dividerClasses() {
+      return [...HR_DEFAULT_CLASSES, ...ONLY_SHOW_MD];
+    },
+    labelSearchBox() {
+      return this.$refs.searchLabelInputBox?.$el.querySelector('[role=searchbox]');
+    },
+    combinedSelectedFilters() {
+      const appliedSelectedLabelKeys = this.appliedSelectedLabels.map((label) => label.key);
+      const { labels = [] } = this.query;
+
+      return uniq([...appliedSelectedLabelKeys, ...labels]);
+    },
+    searchLabels: {
+      get() {
+        return this.searchLabelString;
+      },
+      set(value) {
+        this.setLabelFilterSearch({ value });
+      },
+    },
+    selectedFilters: {
+      get() {
+        return this.combinedSelectedFilters;
+      },
+      set(value) {
+        this.setQuery({ key: this.$options.labelFilterData?.filterParam, value });
+
+        trackSelectCheckbox(value);
+      },
+    },
+  },
+  async created() {
+    await this.fetchAllAggregation();
+  },
+  methods: {
+    ...mapActions(['fetchAllAggregation', 'setQuery', 'closeLabel', 'setLabelFilterSearch']),
+    openDropdown() {
+      this.isFocused = true;
+
+      trackOpenDropdown();
+    },
+    closeDropdown(event) {
+      const { target } = event;
+
+      if (this.labelSearchBox !== target) {
+        this.isFocused = false;
+      }
+    },
+    onLabelClose(event) {
+      if (!event?.target?.closest('.gl-label')?.dataset) {
+        return;
+      }
+
+      const { key } = event.target.closest('.gl-label').dataset;
+      this.closeLabel({ key });
+    },
+    reactiveLabelColor(label) {
+      const { color, key } = label;
+
+      return this.query?.labels?.some((labelKey) => labelKey === key)
+        ? color
+        : `rgba(${rgbFromHex(color)}, 0.3)`;
+    },
+    isLabelClosable(label) {
+      const { key } = label;
+      return this.query?.labels?.some((labelKey) => labelKey === key);
+    },
+  },
+  FIRST_DROPDOWN_INDEX,
+  SEARCH_RESULTS_DESCRIPTION,
+  SEARCH_INPUT_DESCRIPTION,
+  labelFilterData,
+};
+</script>
+
+<template>
+  <div class="gl-pb-0 gl-md-pt-0 label-filter gl-relative">
+    <h5
+      class="gl-my-0"
+      data-testid="label-filter-title"
+      :class="{ 'gl-font-sm': useSidebarNavigation }"
+    >
+      {{ $options.labelFilterData.header }}
+    </h5>
+    <div class="gl-my-5">
+      <gl-label
+        v-for="label in appliedSelectedLabels"
+        :key="label.key"
+        class="gl-mr-2 gl-mb-2 gl-bg-gray-10"
+        :data-key="label.key"
+        :background-color="reactiveLabelColor(label)"
+        :title="label.title"
+        :show-close-button="isLabelClosable(label)"
+        @close="onLabelClose"
+      />
+    </div>
+    <gl-search-box-by-type
+      ref="searchLabelInputBox"
+      v-model="searchLabels"
+      role="searchbox"
+      autocomplete="off"
+      :placeholder="$options.i18n.SEARCH_LABELS"
+      :aria-activedescendant="currentFocusedId"
+      :aria-describedby="$options.SEARCH_INPUT_DESCRIPTION"
+      @focusin="openDropdown"
+      @keydown.esc="closeDropdown"
+    />
+    <span :id="$options.SEARCH_INPUT_DESCRIPTION" role="region" class="gl-sr-only">{{
+      searchInputDescribeBy
+    }}</span>
+    <span
+      role="region"
+      :data-testid="$options.SEARCH_RESULTS_DESCRIPTION"
+      class="gl-sr-only"
+      aria-live="polite"
+      aria-atomic="true"
+    >
+      {{ dropdownResultsDescription }}
+    </span>
+    <div
+      v-if="isFocused"
+      v-outside="closeDropdown"
+      data-testid="header-search-dropdown-menu"
+      class="header-search-dropdown-menu gl-overflow-y-auto gl-absolute gl-w-full gl-bg-white gl-border-1 gl-rounded-base gl-border-solid gl-border-gray-200 gl-shadow-x0-y2-b4-s0 gl-mt-3 gl-z-index-1"
+      :class="{
+        'gl-max-w-none!': useSidebarNavigation,
+        'gl-min-w-full!': useSidebarNavigation,
+        'gl-w-full!': useSidebarNavigation,
+      }"
+    >
+      <div class="header-search-dropdown-content gl-py-2">
+        <dropdown-keyboard-navigation
+          v-model="currentFocusIndex"
+          :max="filteredLabels.length - 1"
+          :min="$options.FIRST_DROPDOWN_INDEX"
+          :default-index="defaultIndex"
+          :enable-cycle="true"
+        />
+        <div v-if="!aggregations.error">
+          <gl-dropdown-section-header v-if="hasSelectedLabels || hasUnselectedLabels">{{
+            $options.i18n.DROPDOWN_HEADER
+          }}</gl-dropdown-section-header>
+          <gl-dropdown-form>
+            <gl-form-checkbox-group v-model="selectedFilters">
+              <label-dropdown-items
+                v-if="hasSelectedLabels"
+                data-testid="selected-lavel-items"
+                :labels="filteredAppliedSelectedLabels"
+              />
+              <gl-dropdown-divider v-if="hasSelectedLabels && hasUnselectedLabels" />
+              <label-dropdown-items
+                v-if="hasUnselectedLabels"
+                data-testid="unselected-lavel-items"
+                :labels="filteredUnselectedLabels"
+              />
+            </gl-form-checkbox-group>
+          </gl-dropdown-form>
+        </div>
+        <gl-alert v-else :dismissible="false" variant="danger">
+          {{ $options.i18n.AGGREGATIONS_ERROR_MESSAGE }}
+        </gl-alert>
+        <gl-loading-icon v-if="aggregations.fetching" size="lg" class="my-4" />
+      </div>
+    </div>
+    <hr v-if="!useSidebarNavigation" :class="dividerClasses" />
+  </div>
+</template>
diff --git a/app/assets/javascripts/search/sidebar/components/label_filter/label_dropdown_items.vue b/app/assets/javascripts/search/sidebar/components/label_filter/label_dropdown_items.vue
new file mode 100644
index 0000000000000000000000000000000000000000..7a9e6a2e4fcaf48d29b7d64207645b054b566c52
--- /dev/null
+++ b/app/assets/javascripts/search/sidebar/components/label_filter/label_dropdown_items.vue
@@ -0,0 +1,43 @@
+<script>
+import { GlFormCheckbox } from '@gitlab/ui';
+
+export default {
+  name: 'LabelDropdownItems',
+  components: {
+    GlFormCheckbox,
+  },
+  props: {
+    labels: {
+      type: Array,
+      required: true,
+    },
+  },
+};
+</script>
+<template>
+  <ul class="gl-list-style-none gl-px-0">
+    <li
+      v-for="label in labels"
+      :id="label.key"
+      :ref="label.key"
+      :key="label.key"
+      :aria-label="label.title"
+      tabindex="-1"
+      class="gl-px-5 gl-py-3 label-filter-menu-item"
+    >
+      <gl-form-checkbox
+        class="label-with-color-checkbox gl-display-inline-flex gl-h-5 gl-min-h-5"
+        :value="label.key"
+      >
+        <span
+          data-testid="label-color-indicator"
+          class="gl-rounded-base gl-w-5 gl-h-5 gl-display-inline-block gl-vertical-align-bottom gl-mr-3"
+          :style="{ 'background-color': label.color }"
+        ></span>
+        <span class="gl-reset-text-align gl-m-0 gl-p-0 label-title">{{
+          label.title
+        }}</span></gl-form-checkbox
+      >
+    </li>
+  </ul>
+</template>
diff --git a/app/assets/javascripts/search/sidebar/components/label_filter/tracking.js b/app/assets/javascripts/search/sidebar/components/label_filter/tracking.js
new file mode 100644
index 0000000000000000000000000000000000000000..c38922a559ccad53bfce5c86bafea64e57e46f22
--- /dev/null
+++ b/app/assets/javascripts/search/sidebar/components/label_filter/tracking.js
@@ -0,0 +1,21 @@
+import Tracking from '~/tracking';
+
+export const TRACKING_CATEGORY = 'Language filters';
+export const TRACKING_LABEL_FILTER = 'Label Key';
+
+export const TRACKING_LABEL_DROPDOWN = 'Dropdown';
+export const TRACKING_LABEL_CHECKBOX = 'Label Checkbox';
+
+export const TRACKING_ACTION_SELECT = 'search:agreggations:label:select';
+export const TRACKING_ACTION_SHOW = 'search:agreggations:label:show';
+
+export const trackSelectCheckbox = (value) =>
+  Tracking.event(TRACKING_ACTION_SELECT, TRACKING_LABEL_CHECKBOX, {
+    label: TRACKING_LABEL_FILTER,
+    property: value,
+  });
+
+export const trackOpenDropdown = () =>
+  Tracking.event(TRACKING_ACTION_SHOW, TRACKING_LABEL_DROPDOWN, {
+    label: TRACKING_LABEL_DROPDOWN,
+  });
diff --git a/app/assets/javascripts/search/sidebar/components/language_filter/index.vue b/app/assets/javascripts/search/sidebar/components/language_filter/index.vue
index e531abf523b75b37a7dc88f762cde299c0131540..c10b14bd116b725c7036f18efb894fcf788f2cda 100644
--- a/app/assets/javascripts/search/sidebar/components/language_filter/index.vue
+++ b/app/assets/javascripts/search/sidebar/components/language_filter/index.vue
@@ -2,7 +2,6 @@
 import { GlButton, GlAlert, GlForm } from '@gitlab/ui';
 import { mapState, mapActions, mapGetters } from 'vuex';
 import { __, s__, sprintf } from '~/locale';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 import { HR_DEFAULT_CLASSES, ONLY_SHOW_MD } from '../../constants';
 import { convertFiltersData } from '../../utils';
 import CheckboxFilter from './checkbox_filter.vue';
@@ -24,7 +23,6 @@ export default {
     GlAlert,
     GlForm,
   },
-  mixins: [glFeatureFlagsMixin()],
   data() {
     return {
       showAll: false,
diff --git a/app/assets/javascripts/search/store/getters.js b/app/assets/javascripts/search/store/getters.js
index d31d2b5ae11d4e3bb7fcc164ef235b7b13088d2b..c7cb595f42fd9f6718ded910397519926acaedfa 100644
--- a/app/assets/javascripts/search/store/getters.js
+++ b/app/assets/javascripts/search/store/getters.js
@@ -41,8 +41,11 @@ export const filteredLabels = (state) => {
 export const filteredAppliedSelectedLabels = (state) =>
   filteredLabels(state)?.filter((label) => state?.urlQuery?.labels?.includes(label.key));
 
-export const appliedSelectedLabels = (state) =>
-  labelAggregationBuckets(state)?.filter((label) => state?.urlQuery?.labels?.includes(label.key));
+export const appliedSelectedLabels = (state) => {
+  return labelAggregationBuckets(state)?.filter((label) =>
+    state?.urlQuery?.labels?.includes(label.key),
+  );
+};
 
 export const filteredUnappliedSelectedLabels = (state) =>
   filteredLabels(state)?.filter((label) => state?.query?.labels?.includes(label.key));
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 2665401c70be95c8b69084f8fb2545c032d6d96c..45aefe48538d644c8458620ece99c0a14ccbdf5b 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -34,6 +34,7 @@ def self.search_rate_limited_endpoints
   before_action only: :show do
     update_scope_for_code_search
   end
+
   rescue_from ActiveRecord::QueryCanceled, with: :render_timeout
 
   layout 'search'
diff --git a/ee/app/controllers/ee/search_controller.rb b/ee/app/controllers/ee/search_controller.rb
index ace58586049b81ea4d2cfe60894e37af8c41fc9d..11b75f168f3943d2585d2088b2b767a30aebc7fa 100644
--- a/ee/app/controllers/ee/search_controller.rb
+++ b/ee/app/controllers/ee/search_controller.rb
@@ -38,6 +38,10 @@ def search_rate_limited_endpoints
 
       before_action :check_search_rate_limit!, only: search_rate_limited_endpoints
 
+      before_action only: :show do
+        push_frontend_feature_flag(:search_issue_label_aggregation, current_user)
+      end
+
       after_action :run_index_integrity_worker, only: :show, if: :no_results_for_group_or_project_blobs_advanced_search?
     end
 
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 64a882092d2df58df11ff853269f5216c750dce5..7f2995d2bb42e309bb81052083160cb2a3785438 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -20875,6 +20875,9 @@ msgstr ""
 msgid "GlobalSearch|Close"
 msgstr ""
 
+msgid "GlobalSearch|Fetching aggregations error."
+msgstr ""
+
 msgid "GlobalSearch|Group"
 msgstr ""
 
@@ -20893,6 +20896,9 @@ msgstr ""
 msgid "GlobalSearch|Issues assigned to me"
 msgstr ""
 
+msgid "GlobalSearch|Label(s)"
+msgstr ""
+
 msgid "GlobalSearch|Language"
 msgstr ""
 
@@ -20941,6 +20947,9 @@ msgstr ""
 msgid "GlobalSearch|Search for projects, issues, etc."
 msgstr ""
 
+msgid "GlobalSearch|Search labels"
+msgstr ""
+
 msgid "GlobalSearch|Search results are loading"
 msgstr ""
 
diff --git a/spec/frontend/search/mock_data.js b/spec/frontend/search/mock_data.js
index 58824f8023d93d570390ad44baa7ebaf68042425..7cf8633d74911dbe61a59d9949d7818a941250d4 100644
--- a/spec/frontend/search/mock_data.js
+++ b/spec/frontend/search/mock_data.js
@@ -8,6 +8,7 @@ export const MOCK_QUERY = {
   group_id: 1,
   language: ['C', 'JavaScript'],
   labels: ['60', '37'],
+  search: '*',
 };
 
 export const MOCK_GROUP = {
diff --git a/spec/frontend/search/sidebar/components/label_dropdown_items_spec.js b/spec/frontend/search/sidebar/components/label_dropdown_items_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..135b12956b21f922b44bca2fcbda5d59d51fb0b0
--- /dev/null
+++ b/spec/frontend/search/sidebar/components/label_dropdown_items_spec.js
@@ -0,0 +1,57 @@
+import { GlFormCheckbox } from '@gitlab/ui';
+import Vue from 'vue';
+import Vuex from 'vuex';
+import { shallowMount } from '@vue/test-utils';
+import { PROCESS_LABELS_DATA } from 'jest/search/mock_data';
+import LabelDropdownItems from '~/search/sidebar/components/label_filter/label_dropdown_items.vue';
+
+Vue.use(Vuex);
+
+describe('LabelDropdownItems', () => {
+  let wrapper;
+
+  const defaultProps = {
+    labels: PROCESS_LABELS_DATA,
+  };
+
+  const createComponent = (Props = defaultProps) => {
+    wrapper = shallowMount(LabelDropdownItems, {
+      propsData: {
+        ...Props,
+      },
+    });
+  };
+
+  const findAllLabelItems = () => wrapper.findAll('.label-filter-menu-item');
+  const findFirstLabelCheckbox = () => findAllLabelItems().at(0).findComponent(GlFormCheckbox);
+  const findFirstLabelTitle = () => findAllLabelItems().at(0).findComponent('.label-title');
+  const findFirstLabelColor = () =>
+    findAllLabelItems().at(0).findComponent('[data-testid="label-color-indicator"]');
+
+  describe('Renders correctly', () => {
+    beforeEach(() => {
+      createComponent();
+    });
+
+    it('renders items', () => {
+      expect(findAllLabelItems().exists()).toBe(true);
+      expect(findAllLabelItems()).toHaveLength(defaultProps.labels.length);
+    });
+
+    it('renders items checkbox', () => {
+      expect(findFirstLabelCheckbox().exists()).toBe(true);
+    });
+
+    it('renders label title', () => {
+      expect(findFirstLabelTitle().exists()).toBe(true);
+      expect(findFirstLabelTitle().text()).toBe(defaultProps.labels[0].title);
+    });
+
+    it('renders label color', () => {
+      expect(findFirstLabelColor().exists()).toBe(true);
+      expect(findFirstLabelColor().attributes('style')).toBe(
+        `background-color: ${defaultProps.labels[0].color};`,
+      );
+    });
+  });
+});
diff --git a/spec/frontend/search/sidebar/components/label_filter_spec.js b/spec/frontend/search/sidebar/components/label_filter_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..c5df374d4ef7f085728a387b9a4c6948b5e3ed2d
--- /dev/null
+++ b/spec/frontend/search/sidebar/components/label_filter_spec.js
@@ -0,0 +1,322 @@
+import {
+  GlAlert,
+  GlLoadingIcon,
+  GlSearchBoxByType,
+  GlLabel,
+  GlDropdownForm,
+  GlFormCheckboxGroup,
+  GlDropdownSectionHeader,
+  GlDropdownDivider,
+} from '@gitlab/ui';
+import Vue from 'vue';
+import Vuex from 'vuex';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { MOCK_QUERY, MOCK_LABEL_AGGREGATIONS } from 'jest/search/mock_data';
+import LabelFilter from '~/search/sidebar/components/label_filter/index.vue';
+import LabelDropdownItems from '~/search/sidebar/components/label_filter/label_dropdown_items.vue';
+
+import * as actions from '~/search/store/actions';
+import * as getters from '~/search/store/getters';
+import mutations from '~/search/store/mutations';
+import createState from '~/search/store/state';
+
+import {
+  TRACKING_LABEL_FILTER,
+  TRACKING_LABEL_DROPDOWN,
+  TRACKING_LABEL_CHECKBOX,
+  TRACKING_ACTION_SELECT,
+  TRACKING_ACTION_SHOW,
+} from '~/search/sidebar/components/label_filter/tracking';
+
+import { labelFilterData } from '~/search/sidebar/components/label_filter/data';
+
+import {
+  RECEIVE_AGGREGATIONS_SUCCESS,
+  REQUEST_AGGREGATIONS,
+  RECEIVE_AGGREGATIONS_ERROR,
+} from '~/search/store/mutation_types';
+
+Vue.use(Vuex);
+
+const actionSpies = {
+  fetchAllAggregation: jest.fn(),
+  setQuery: jest.fn(),
+  closeLabel: jest.fn(),
+  setLabelFilterSearch: jest.fn(),
+};
+
+describe('GlobalSearchSidebarLabelFilter', () => {
+  let wrapper;
+  let trackingSpy;
+  let config;
+  let store;
+
+  const createComponent = (initialState) => {
+    config = {
+      actions: {
+        ...actions,
+        fetchAllAggregation: actionSpies.fetchAllAggregation,
+        closeLabel: actionSpies.closeLabel,
+        setLabelFilterSearch: actionSpies.setLabelFilterSearch,
+        setQuery: actionSpies.setQuery,
+      },
+      getters,
+      mutations,
+      state: createState({
+        query: MOCK_QUERY,
+        aggregations: MOCK_LABEL_AGGREGATIONS,
+        ...initialState,
+      }),
+    };
+
+    store = new Vuex.Store(config);
+
+    wrapper = mountExtended(LabelFilter, {
+      store,
+      provide: {
+        glFeatures: {
+          searchIssueLabelAggregation: true,
+        },
+      },
+    });
+  };
+
+  const findComponentTitle = () => wrapper.findComponentByTestId('label-filter-title');
+  const findAllSelectedLabelsAbove = () => wrapper.findAllComponents(GlLabel);
+  const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
+  const findDropdownForm = () => wrapper.findComponent(GlDropdownForm);
+  const findCheckboxGroup = () => wrapper.findComponent(GlFormCheckboxGroup);
+  const findDropdownSectionHeader = () => wrapper.findComponent(GlDropdownSectionHeader);
+  const findDivider = () => wrapper.findComponent(GlDropdownDivider);
+  const findCheckboxFilter = () => wrapper.findAllComponents(LabelDropdownItems);
+  const findAlert = () => wrapper.findComponent(GlAlert);
+  const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+
+  describe('Renders correctly closed', () => {
+    beforeEach(async () => {
+      createComponent();
+      store.commit(RECEIVE_AGGREGATIONS_SUCCESS, MOCK_LABEL_AGGREGATIONS.data);
+
+      await Vue.nextTick();
+    });
+
+    it('renders component title', () => {
+      expect(findComponentTitle().exists()).toBe(true);
+    });
+
+    it('renders selected labels above search box', () => {
+      expect(findAllSelectedLabelsAbove().exists()).toBe(true);
+      expect(findAllSelectedLabelsAbove()).toHaveLength(2);
+    });
+
+    it('renders search box', () => {
+      expect(findSearchBox().exists()).toBe(true);
+    });
+
+    it("doesn't render dropdown form", () => {
+      expect(findDropdownForm().exists()).toBe(false);
+    });
+
+    it("doesn't render checkbox group", () => {
+      expect(findCheckboxGroup().exists()).toBe(false);
+    });
+
+    it("doesn't render dropdown section header", () => {
+      expect(findDropdownSectionHeader().exists()).toBe(false);
+    });
+
+    it("doesn't render divider", () => {
+      expect(findDivider().exists()).toBe(false);
+    });
+
+    it("doesn't render checkbox filter", () => {
+      expect(findCheckboxFilter().exists()).toBe(false);
+    });
+
+    it("doesn't render alert", () => {
+      expect(findAlert().exists()).toBe(false);
+    });
+
+    it("doesn't render loading icon", () => {
+      expect(findLoadingIcon().exists()).toBe(false);
+    });
+  });
+
+  describe('Renders correctly opened', () => {
+    beforeEach(async () => {
+      createComponent();
+      store.commit(RECEIVE_AGGREGATIONS_SUCCESS, MOCK_LABEL_AGGREGATIONS.data);
+
+      await Vue.nextTick();
+      trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+      findSearchBox().vm.$emit('focusin');
+    });
+
+    afterEach(() => {
+      unmockTracking();
+    });
+
+    it('renders component title', () => {
+      expect(findComponentTitle().exists()).toBe(true);
+    });
+
+    it('renders selected labels above search box', () => {
+      // default data need to provide at least two selected labels
+      expect(findAllSelectedLabelsAbove().exists()).toBe(true);
+      expect(findAllSelectedLabelsAbove()).toHaveLength(2);
+    });
+
+    it('renders search box', () => {
+      expect(findSearchBox().exists()).toBe(true);
+    });
+
+    it('renders dropdown form', () => {
+      expect(findDropdownForm().exists()).toBe(true);
+    });
+
+    it('renders checkbox group', () => {
+      expect(findCheckboxGroup().exists()).toBe(true);
+    });
+
+    it('renders dropdown section header', () => {
+      expect(findDropdownSectionHeader().exists()).toBe(true);
+    });
+
+    it('renders divider', () => {
+      expect(findDivider().exists()).toBe(true);
+    });
+
+    it('renders checkbox filter', () => {
+      expect(findCheckboxFilter().exists()).toBe(true);
+    });
+
+    it("doesn't render alert", () => {
+      expect(findAlert().exists()).toBe(false);
+    });
+
+    it("doesn't render loading icon", () => {
+      expect(findLoadingIcon().exists()).toBe(false);
+    });
+
+    it('sends tracking information when dropdown is opened', () => {
+      expect(trackingSpy).toHaveBeenCalledWith(TRACKING_ACTION_SHOW, TRACKING_LABEL_DROPDOWN, {
+        label: TRACKING_LABEL_DROPDOWN,
+      });
+    });
+  });
+
+  describe('Renders loading state correctly', () => {
+    beforeEach(async () => {
+      createComponent();
+      store.commit(REQUEST_AGGREGATIONS);
+      await Vue.nextTick();
+
+      findSearchBox().vm.$emit('focusin');
+    });
+
+    it('renders checkbox filter', () => {
+      expect(findCheckboxFilter().exists()).toBe(false);
+    });
+
+    it("doesn't render alert", () => {
+      expect(findAlert().exists()).toBe(false);
+    });
+
+    it('renders loading icon', () => {
+      expect(findLoadingIcon().exists()).toBe(true);
+    });
+  });
+
+  describe('Renders error state correctly', () => {
+    beforeEach(async () => {
+      createComponent();
+      store.commit(RECEIVE_AGGREGATIONS_ERROR);
+      await Vue.nextTick();
+
+      findSearchBox().vm.$emit('focusin');
+    });
+
+    it("doesn't render checkbox filter", () => {
+      expect(findCheckboxFilter().exists()).toBe(false);
+    });
+
+    it('renders alert', () => {
+      expect(findAlert().exists()).toBe(true);
+    });
+
+    it("doesn't render loading icon", () => {
+      expect(findLoadingIcon().exists()).toBe(false);
+    });
+  });
+
+  describe('Actions', () => {
+    describe('dispatch action when component is created', () => {
+      beforeEach(() => {
+        createComponent();
+      });
+
+      it('renders checkbox filter', async () => {
+        await Vue.nextTick();
+        expect(actionSpies.fetchAllAggregation).toHaveBeenCalled();
+      });
+    });
+
+    describe('Closing label works correctly', () => {
+      beforeEach(async () => {
+        createComponent();
+        store.commit(RECEIVE_AGGREGATIONS_SUCCESS, MOCK_LABEL_AGGREGATIONS.data);
+        await Vue.nextTick();
+      });
+
+      it('renders checkbox filter', async () => {
+        await findAllSelectedLabelsAbove().at(0).find('.btn-reset').trigger('click');
+        expect(actionSpies.closeLabel).toHaveBeenCalled();
+      });
+    });
+
+    describe('label search input box works properly', () => {
+      beforeEach(() => {
+        createComponent();
+      });
+
+      it('renders checkbox filter', () => {
+        findSearchBox().find('input').setValue('test');
+        expect(actionSpies.setLabelFilterSearch).toHaveBeenCalledWith(
+          expect.anything(),
+          expect.objectContaining({
+            value: 'test',
+          }),
+        );
+      });
+    });
+
+    describe('dropdown checkboxes work', () => {
+      beforeEach(async () => {
+        createComponent();
+
+        await findSearchBox().vm.$emit('focusin');
+        await Vue.nextTick();
+
+        trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+
+        await findCheckboxGroup().vm.$emit('input', 6);
+        await Vue.nextTick();
+      });
+
+      it('trigger event', () => {
+        expect(actionSpies.setQuery).toHaveBeenCalledWith(
+          expect.anything(),
+          expect.objectContaining({ key: labelFilterData?.filterParam, value: 6 }),
+        );
+      });
+
+      it('sends tracking information when checkbox is selected', () => {
+        expect(trackingSpy).toHaveBeenCalledWith(TRACKING_ACTION_SELECT, TRACKING_LABEL_CHECKBOX, {
+          label: TRACKING_LABEL_FILTER,
+          property: 6,
+        });
+      });
+    });
+  });
+});
diff --git a/spec/frontend/search/store/actions_spec.js b/spec/frontend/search/store/actions_spec.js
index 18f7e1ce21c502204185229719d2d7a07d372591..2051e7316471b785cb9864c9298826b2d1702af5 100644
--- a/spec/frontend/search/store/actions_spec.js
+++ b/spec/frontend/search/store/actions_spec.js
@@ -133,7 +133,7 @@ describe('Global Search Store Actions', () => {
 
     describe('when groupId is set', () => {
       it('calls Api.groupProjects with expected parameters', () => {
-        actions.fetchProjects({ commit: mockCommit, state }, undefined);
+        actions.fetchProjects({ commit: mockCommit, state }, MOCK_QUERY.search);
         expect(Api.groupProjects).toHaveBeenCalledWith(state.query.group_id, state.query.search, {
           order_by: 'similarity',
           include_subgroups: true,
diff --git a/spec/frontend/search/store/getters_spec.js b/spec/frontend/search/store/getters_spec.js
index 51692cb1ab49a91e6a69391ecbe9de8ddfaea5d9..772acb39a57ddcdd6fc9da78680ac2bc98e80599 100644
--- a/spec/frontend/search/store/getters_spec.js
+++ b/spec/frontend/search/store/getters_spec.js
@@ -33,10 +33,6 @@ describe('Global Search Store Getters', () => {
     useMockLocationHelper();
   });
 
-  afterEach(() => {
-    state = cloneDeep(defaultState);
-  });
-
   describe('frequentGroups', () => {
     it('returns the correct data', () => {
       state.frequentItems[GROUPS_LOCAL_STORAGE_KEY] = MOCK_GROUPS;