diff --git a/ee/app/assets/javascripts/security_dashboard/components/shared/filters/severity_filter.vue b/ee/app/assets/javascripts/security_dashboard/components/shared/filters/severity_filter.vue
index 2b00eae874d5bc91af07cbb8cac63f02398dad7c..f9a72e9923833a22833f3c4eaab5c7595e4ec7e4 100644
--- a/ee/app/assets/javascripts/security_dashboard/components/shared/filters/severity_filter.vue
+++ b/ee/app/assets/javascripts/security_dashboard/components/shared/filters/severity_filter.vue
@@ -1,79 +1,80 @@
 <script>
-import { GlDropdown } from '@gitlab/ui';
-import { xor } from 'lodash';
+import { GlCollapsibleListbox } from '@gitlab/ui';
 import { s__ } from '~/locale';
 import { SEVERITY_LEVELS } from 'ee/security_dashboard/store/constants';
-import DropdownButtonText from './dropdown_button_text.vue';
+import { getSelectedOptionsText } from 'ee/security_dashboard/components/shared/filters/utils';
 import QuerystringSync from './querystring_sync.vue';
-import FilterItem from './filter_item.vue';
 import { ALL_ID } from './constants';
 
 // For backwards compatibility with existing bookmarks, the ID needs to be capitalized.
-export const DROPDOWN_OPTIONS = Object.entries(SEVERITY_LEVELS).map(([id, text]) => ({
-  id: id.toUpperCase(),
+export const SEVERITY_LEVEL_ITEMS = Object.entries(SEVERITY_LEVELS).map(([id, text]) => ({
+  value: id.toUpperCase(),
   text,
 }));
-const VALID_IDS = DROPDOWN_OPTIONS.map(({ id }) => id);
+
+export const FILTER_ITEMS = [
+  {
+    value: ALL_ID,
+    text: s__('SecurityReports|All severities'),
+  },
+  ...SEVERITY_LEVEL_ITEMS,
+];
+
+const VALID_IDS = SEVERITY_LEVEL_ITEMS.map(({ value }) => value);
 
 export default {
-  components: { GlDropdown, DropdownButtonText, QuerystringSync, FilterItem },
+  components: { GlCollapsibleListbox, QuerystringSync },
   data: () => ({
-    selected: [],
+    selectedIds: [ALL_ID],
   }),
   computed: {
-    selectedItemTexts() {
-      const options = DROPDOWN_OPTIONS.filter(({ id }) => this.selected.includes(id));
-      // Return the text for selected items, or all items if nothing is selected.
-      return options.length ? options.map(({ text }) => text) : [this.$options.i18n.allItemsText];
+    toggleText() {
+      return getSelectedOptionsText(FILTER_ITEMS, this.selectedIds);
     },
   },
   watch: {
-    selected() {
-      this.$emit('filter-changed', { severity: this.selected });
+    selectedIds: {
+      handler() {
+        this.$emit('filter-changed', {
+          severity: this.selectedIds.filter((value) => value !== ALL_ID),
+        });
+      },
     },
   },
   methods: {
-    deselectAll() {
-      this.selected = [];
-    },
-    toggleSelected(id) {
-      this.selected = xor(this.selected, [id]);
-    },
-    setSelected(ids) {
-      this.selected = ids.filter((id) => VALID_IDS.includes(id));
+    updateSelected(selected) {
+      if (!selected.length || selected.at(-1) === ALL_ID) {
+        this.selectedIds = [ALL_ID];
+      } else {
+        this.selectedIds = selected.filter((value) => value !== ALL_ID);
+      }
     },
   },
   i18n: {
     label: s__('SecurityReports|Severity'),
-    allItemsText: s__('SecurityReports|All severities'),
   },
-  DROPDOWN_OPTIONS,
-  ALL_ID,
+  FILTER_ITEMS,
+  VALID_IDS,
 };
 </script>
 
 <template>
   <div>
-    <querystring-sync querystring-key="severity" :value="selected" @input="setSelected" />
+    <querystring-sync
+      :value="selectedIds"
+      querystring-key="severity"
+      :valid-values="$options.VALID_IDS"
+      @input="updateSelected"
+    />
     <label class="gl-mb-2">{{ $options.i18n.label }}</label>
-    <gl-dropdown :header-text="$options.i18n.label" block toggle-class="gl-mb-0">
-      <template #button-text>
-        <dropdown-button-text :items="selectedItemTexts" :name="$options.i18n.label" />
-      </template>
-      <filter-item
-        :is-checked="!selected.length"
-        :text="$options.i18n.allItemsText"
-        :data-testid="$options.ALL_ID"
-        @click="deselectAll"
-      />
-      <filter-item
-        v-for="{ id, text } in $options.DROPDOWN_OPTIONS"
-        :key="id"
-        :data-testid="id"
-        :is-checked="selected.includes(id)"
-        :text="text"
-        @click="toggleSelected(id)"
-      />
-    </gl-dropdown>
+    <gl-collapsible-listbox
+      :header-text="$options.i18n.label"
+      :items="$options.FILTER_ITEMS"
+      :selected="selectedIds"
+      :toggle-text="toggleText"
+      multiple
+      block
+      @select="updateSelected"
+    />
   </div>
 </template>
diff --git a/ee/spec/frontend/security_dashboard/components/pipeline/filters_spec.js b/ee/spec/frontend/security_dashboard/components/pipeline/filters_spec.js
index 797ec56b94e91310161250dd36f83fba95780091..ddb9af2d7ebe5b87f15b9a9dd5a8c033470cb2e3 100644
--- a/ee/spec/frontend/security_dashboard/components/pipeline/filters_spec.js
+++ b/ee/spec/frontend/security_dashboard/components/pipeline/filters_spec.js
@@ -69,15 +69,16 @@ describe('Filter component', () => {
 
       createWrapper({ mountFn: mount });
       await nextTick();
+
       // The other filters will trigger the mock as well, so we'll clear it before clicking on a
       // scanner filter item.
       mock.mockClear();
 
       const filterId = 'severity';
       const optionId = 'MEDIUM';
-      const option = wrapper.findByTestId(optionId);
-      option.vm.$emit('click');
-      await nextTick();
+      const option = wrapper.findByTestId(`listbox-item-${optionId}`);
+
+      await option.trigger('click');
 
       expect(mock).toHaveBeenCalledTimes(1);
       expect(mock).toHaveBeenCalledWith(expect.any(Object), { [filterId]: [optionId] });
diff --git a/ee/spec/frontend/security_dashboard/components/shared/filters/severity_filter_spec.js b/ee/spec/frontend/security_dashboard/components/shared/filters/severity_filter_spec.js
index 648ee7d310619372d8f3b41d8d3a477ef4789b2c..a477e8dcee8b08ff1dbbcd63d882dfbe540a8fdb 100644
--- a/ee/spec/frontend/security_dashboard/components/shared/filters/severity_filter_spec.js
+++ b/ee/spec/frontend/security_dashboard/components/shared/filters/severity_filter_spec.js
@@ -1,15 +1,13 @@
-import { GlDropdown } from '@gitlab/ui';
-import { nextTick } from 'vue';
+import { GlCollapsibleListbox } from '@gitlab/ui';
 import SeverityFilter, {
-  DROPDOWN_OPTIONS,
+  SEVERITY_LEVEL_ITEMS,
+  FILTER_ITEMS,
 } from 'ee/security_dashboard/components/shared/filters/severity_filter.vue';
 import { ALL_ID } from 'ee/security_dashboard/components/shared/filters/constants';
 import QuerystringSync from 'ee/security_dashboard/components/shared/filters/querystring_sync.vue';
-import DropdownButtonText from 'ee/security_dashboard/components/shared/filters/dropdown_button_text.vue';
-import FilterItem from 'ee/security_dashboard/components/shared/filters/filter_item.vue';
 import { mountExtended } from 'helpers/vue_test_utils_helper';
 
-const OPTION_IDS = DROPDOWN_OPTIONS.map(({ id }) => id);
+const OPTION_IDS = SEVERITY_LEVEL_ITEMS.map(({ value }) => value);
 
 describe('Severity Filter component', () => {
   let wrapper;
@@ -21,20 +19,13 @@ describe('Severity Filter component', () => {
   };
 
   const findQuerystringSync = () => wrapper.findComponent(QuerystringSync);
-  const findDropdownItems = () => wrapper.findAllComponents(FilterItem);
-  const findDropdownItem = (id) => wrapper.findByTestId(id);
+  const findListbox = () => wrapper.findComponent(GlCollapsibleListbox);
+  const findListboxItem = (id) => wrapper.findByTestId(`listbox-item-${id}`);
 
-  const clickDropdownItem = async (id) => {
-    findDropdownItem(id).vm.$emit('click');
-    await nextTick();
-  };
+  const clickListboxItem = (id) => findListboxItem(id).trigger('click');
 
   const expectSelectedItems = (ids) => {
-    const checkedItems = findDropdownItems()
-      .wrappers.filter((item) => item.props('isChecked'))
-      .map((item) => item.attributes('data-testid'));
-
-    expect(checkedItems).toEqual(ids);
+    expect(findListbox().props('selected')).toEqual(ids);
   };
 
   beforeEach(() => {
@@ -45,25 +36,23 @@ describe('Severity Filter component', () => {
     it('has expected props', () => {
       expect(findQuerystringSync().props()).toMatchObject({
         querystringKey: 'severity',
-        value: [],
+        value: [ALL_ID],
+        validValues: OPTION_IDS,
       });
     });
 
-    it('receives empty array when All Statuses option is clicked', async () => {
-      await clickDropdownItem(ALL_ID);
+    it('receives "ALL_ID" when All Statuses option is clicked', async () => {
+      await clickListboxItem(ALL_ID);
 
-      expect(findQuerystringSync().props('value')).toEqual([]);
+      expect(findQuerystringSync().props('value')).toEqual([ALL_ID]);
     });
 
     it.each`
-      emitted                          | expected
-      ${['HIGH', 'MEDIUM']}            | ${['HIGH', 'MEDIUM']}
-      ${['INVALID', 'LOW', 'UNKNOWN']} | ${['LOW', 'UNKNOWN']}
-      ${['INVALID']}                   | ${[ALL_ID]}
-      ${[]}                            | ${[ALL_ID]}
+      emitted               | expected
+      ${['HIGH', 'MEDIUM']} | ${['HIGH', 'MEDIUM']}
+      ${[]}                 | ${[ALL_ID]}
     `('restores selected items - $emitted', async ({ emitted, expected }) => {
-      findQuerystringSync().vm.$emit('input', emitted);
-      await nextTick();
+      await findQuerystringSync().vm.$emit('input', emitted);
 
       expectSelectedItems(expected);
     });
@@ -74,49 +63,25 @@ describe('Severity Filter component', () => {
       expect(wrapper.find('label').text()).toBe(SeverityFilter.i18n.label);
     });
 
-    it('shows the dropdown with correct header text', () => {
-      expect(wrapper.findComponent(GlDropdown).props('headerText')).toBe(SeverityFilter.i18n.label);
-    });
-
-    it('shows the DropdownButtonText component with the correct props', () => {
-      expect(wrapper.findComponent(DropdownButtonText).props()).toMatchObject({
-        items: [SeverityFilter.i18n.allItemsText],
-        name: SeverityFilter.i18n.label,
+    it('shows the ListBox component with the correct props', () => {
+      expect(findListbox().props()).toMatchObject({
+        items: FILTER_ITEMS,
+        toggleText: 'All severities',
+        multiple: true,
+        block: true,
       });
     });
   });
 
   describe('dropdown items', () => {
-    it('shows all dropdown items with correct text', () => {
-      expect(findDropdownItems()).toHaveLength(DROPDOWN_OPTIONS.length + 1);
-
-      expect(findDropdownItem(ALL_ID).text()).toBe(SeverityFilter.i18n.allItemsText);
-      DROPDOWN_OPTIONS.forEach(({ id, text }) => {
-        expect(findDropdownItem(id).text()).toBe(text);
-      });
-    });
+    it.each(OPTION_IDS)('toggles the item selection when clicked on %s', async (id) => {
+      await clickListboxItem(id);
 
-    it('allows multiple items to be selected', async () => {
-      const ids = [];
+      expectSelectedItems([id]);
 
-      for await (const id of OPTION_IDS) {
-        await clickDropdownItem(id);
-        ids.push(id);
+      await clickListboxItem(id);
 
-        expectSelectedItems(ids);
-      }
-    });
-
-    it('toggles the item selection when clicked on', async () => {
-      for await (const id of OPTION_IDS) {
-        await clickDropdownItem(id);
-
-        expectSelectedItems([id]);
-
-        await clickDropdownItem(id);
-
-        expectSelectedItems([ALL_ID]);
-      }
+      expectSelectedItems([ALL_ID]);
     });
 
     it('selects ALL item when created', () => {
@@ -124,15 +89,15 @@ describe('Severity Filter component', () => {
     });
 
     it('selects ALL item and deselects everything else when it is clicked', async () => {
-      await clickDropdownItem(OPTION_IDS[0]);
-      await clickDropdownItem(ALL_ID);
-      await clickDropdownItem(ALL_ID); // Click again to verify that it doesn't toggle.
+      await clickListboxItem(OPTION_IDS[0]);
+      await clickListboxItem(ALL_ID);
+      await clickListboxItem(ALL_ID); // Click again to verify that it doesn't toggle.
 
       expectSelectedItems([ALL_ID]);
     });
 
     it('deselects the ALL item when another item is clicked', async () => {
-      await clickDropdownItem(OPTION_IDS[0]);
+      await clickListboxItem(OPTION_IDS[0]);
 
       expectSelectedItems([OPTION_IDS[0]]);
     });
@@ -143,10 +108,10 @@ describe('Severity Filter component', () => {
       const ids = [];
 
       for await (const id of OPTION_IDS) {
-        await clickDropdownItem(id);
+        await clickListboxItem(id);
         ids.push(id);
 
-        expect(wrapper.emitted('filter-changed')[ids.length - 1][0].severity).toEqual(ids);
+        expect(wrapper.emitted('filter-changed').at(-1)[0].severity).toEqual(ids);
       }
     });
   });