From ae3932c335a21d22cbc0cacf2507eae6c61003c1 Mon Sep 17 00:00:00 2001
From: Anton Kalmykov <anton.kalmykov@proton.me>
Date: Thu, 12 Sep 2024 11:40:00 +0300
Subject: [PATCH] Modernize sorting and filtering in the Admin Area Groups

Update sorting and filtering in the Admin Area > Groups to follow the
pattern used across the product for better visual alignment

Closes https://gitlab.com/gitlab-org/gitlab/-/issues/437016
Changelog: changed
---
 .rubocop_todo/rspec/feature_category.yml      |   1 -
 .../components/filtered_search_and_sort.vue   | 102 ++++++++++++++
 .../javascripts/admin/groups/constants.js     |  39 ++++++
 app/assets/javascripts/admin/groups/index.js  |  16 +++
 .../pages/admin/groups/index/index.js         |   2 +
 app/helpers/explore_helper.rb                 |   4 -
 app/helpers/sorting_helper.rb                 |  17 ---
 app/views/admin/groups/index.html.haml        |  11 +-
 app/views/shared/groups/_dropdown.html.haml   |   5 -
 doc/administration/admin_area.md              |  11 +-
 locale/gitlab.pot                             |   3 +
 qa/qa/page/admin/overview/groups/index.rb     |   8 +-
 .../admin/groups_controller_spec.rb           |  37 +-----
 .../filtered_search_and_sort_spec.js          | 125 ++++++++++++++++++
 spec/helpers/sorting_helper_spec.rb           |  17 ---
 .../shared/groups/_dropdown.html.haml_spec.rb |  53 --------
 16 files changed, 307 insertions(+), 144 deletions(-)
 create mode 100644 app/assets/javascripts/admin/groups/components/filtered_search_and_sort.vue
 create mode 100644 app/assets/javascripts/admin/groups/constants.js
 create mode 100644 app/assets/javascripts/admin/groups/index.js
 delete mode 100644 app/views/shared/groups/_dropdown.html.haml
 create mode 100644 spec/frontend/admin/groups/components/filtered_search_and_sort_spec.js
 delete mode 100644 spec/views/shared/groups/_dropdown.html.haml_spec.rb

diff --git a/.rubocop_todo/rspec/feature_category.yml b/.rubocop_todo/rspec/feature_category.yml
index d26260c3561dc..6ce431c55b1be 100644
--- a/.rubocop_todo/rspec/feature_category.yml
+++ b/.rubocop_todo/rspec/feature_category.yml
@@ -4192,7 +4192,6 @@ RSpec/FeatureCategory:
     - 'spec/views/shared/_label_row.html.haml_spec.rb'
     - 'spec/views/shared/_milestones_sort_dropdown.html.haml_spec.rb'
     - 'spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb'
-    - 'spec/views/shared/groups/_dropdown.html.haml_spec.rb'
     - 'spec/views/shared/issuable/_sidebar.html.haml_spec.rb'
     - 'spec/views/shared/milestones/_issuable.html.haml_spec.rb'
     - 'spec/views/shared/milestones/_issuables.html.haml_spec.rb'
diff --git a/app/assets/javascripts/admin/groups/components/filtered_search_and_sort.vue b/app/assets/javascripts/admin/groups/components/filtered_search_and_sort.vue
new file mode 100644
index 0000000000000..dbcc081461d04
--- /dev/null
+++ b/app/assets/javascripts/admin/groups/components/filtered_search_and_sort.vue
@@ -0,0 +1,102 @@
+<script>
+import FilteredSearchAndSort from '~/groups_projects/components/filtered_search_and_sort.vue';
+import {
+  FILTERED_SEARCH_NAMESPACE,
+  FILTERED_SEARCH_TERM_KEY,
+  SORT_DIRECTION_ASC,
+  SORT_DIRECTION_DESC,
+  SORT_OPTION_CREATED_DATE,
+  SORT_OPTIONS,
+} from '~/admin/groups/constants';
+import { RECENT_SEARCHES_STORAGE_KEY_GROUPS } from '~/filtered_search/recent_searches_storage_keys';
+import { objectToQuery, queryToObject, visitUrl } from '~/lib/utils/url_utility';
+
+export default {
+  components: {
+    FilteredSearchAndSort,
+  },
+  computed: {
+    defaultSortOption() {
+      return SORT_OPTION_CREATED_DATE;
+    },
+    defaultSortBy() {
+      return `${this.defaultSortOption.value}_${SORT_DIRECTION_DESC}`;
+    },
+    queryAsObject() {
+      return queryToObject(document.location.search);
+    },
+    queryAsObjectWithoutPagination() {
+      const { page, ...queryAsObject } = this.queryAsObject;
+      return queryAsObject;
+    },
+    sortByQuery() {
+      return this.queryAsObject.sort;
+    },
+    sortBy() {
+      return this.sortByQuery || this.defaultSortBy;
+    },
+    sortOptions() {
+      return SORT_OPTIONS;
+    },
+    activeSortOption() {
+      return (
+        this.sortOptions.find((option) => this.sortBy.includes(option.value)) ||
+        this.defaultSortOption
+      );
+    },
+    isAscending() {
+      return this.sortBy.endsWith(SORT_DIRECTION_ASC);
+    },
+  },
+  methods: {
+    visitUrlWithQueryObject(queryObject) {
+      return visitUrl(`?${objectToQuery(queryObject)}`);
+    },
+    onSortChange(sortBy, isAscending) {
+      const sort = `${sortBy}_${isAscending ? SORT_DIRECTION_ASC : SORT_DIRECTION_DESC}`;
+      this.visitUrlWithQueryObject({ ...this.queryAsObjectWithoutPagination, sort });
+    },
+    onSortDirectionChange(isAscending) {
+      this.onSortChange(this.activeSortOption.value, isAscending);
+    },
+    onSortByChange(sortBy) {
+      this.onSortChange(sortBy, this.isAscending);
+    },
+    onFilter(filtersQuery) {
+      const queryObject = { ...filtersQuery };
+
+      if (this.sortByQuery) {
+        queryObject.sort = this.sortByQuery;
+      }
+
+      this.visitUrlWithQueryObject(queryObject);
+    },
+  },
+  filteredSearch: {
+    recentSearchesStorageKey: RECENT_SEARCHES_STORAGE_KEY_GROUPS,
+    namespace: FILTERED_SEARCH_NAMESPACE,
+    termKey: FILTERED_SEARCH_TERM_KEY,
+    tokens: [],
+  },
+};
+</script>
+
+<template>
+  <div class="gl-mb-4" data-testid="admin-groups-filtered-search-and-sort">
+    <filtered-search-and-sort
+      :filtered-search-namespace="$options.filteredSearch.namespace"
+      :filtered-search-tokens="$options.filteredSearch.tokens"
+      :filtered-search-term-key="$options.filteredSearch.termKey"
+      :filtered-search-recent-searches-storage-key="
+        $options.filteredSearch.recentSearchesStorageKey
+      "
+      :is-ascending="isAscending"
+      :sort-options="sortOptions"
+      :active-sort-option="activeSortOption"
+      :filtered-search-query="queryAsObject"
+      @filter="onFilter"
+      @sort-direction-change="onSortDirectionChange"
+      @sort-by-change="onSortByChange"
+    />
+  </div>
+</template>
diff --git a/app/assets/javascripts/admin/groups/constants.js b/app/assets/javascripts/admin/groups/constants.js
new file mode 100644
index 0000000000000..be10a8907eb97
--- /dev/null
+++ b/app/assets/javascripts/admin/groups/constants.js
@@ -0,0 +1,39 @@
+import { __ } from '~/locale';
+
+export const FILTERED_SEARCH_NAMESPACE = 'admin-groups';
+export const FILTERED_SEARCH_TERM_KEY = 'name';
+
+export const SORT_DIRECTION_ASC = 'asc';
+export const SORT_DIRECTION_DESC = 'desc';
+
+const NAME = 'name';
+const CREATED = 'created';
+const LATEST_ACTIVITY = 'latest_activity';
+const STORAGE_SIZE = 'storage_size';
+
+export const SORT_OPTION_NAME = {
+  text: __('Name'),
+  value: NAME,
+};
+
+export const SORT_OPTION_CREATED_DATE = {
+  text: __('Created date'),
+  value: CREATED,
+};
+
+export const SORT_OPTION_UPDATED_DATE = {
+  text: __('Updated date'),
+  value: LATEST_ACTIVITY,
+};
+
+export const SORT_OPTION_STORAGE_SIZE = {
+  text: __('Storage size'),
+  value: STORAGE_SIZE,
+};
+
+export const SORT_OPTIONS = [
+  SORT_OPTION_NAME,
+  SORT_OPTION_CREATED_DATE,
+  SORT_OPTION_UPDATED_DATE,
+  SORT_OPTION_STORAGE_SIZE,
+];
diff --git a/app/assets/javascripts/admin/groups/index.js b/app/assets/javascripts/admin/groups/index.js
new file mode 100644
index 0000000000000..1ceb105bdbfd6
--- /dev/null
+++ b/app/assets/javascripts/admin/groups/index.js
@@ -0,0 +1,16 @@
+import Vue from 'vue';
+import FilteredSearchAndSort from './components/filtered_search_and_sort.vue';
+
+export const initAdminGroupsFilteredSearchAndSort = () => {
+  const el = document.getElementById('js-admin-groups-filtered-search-and-sort');
+
+  if (!el) return false;
+
+  return new Vue({
+    el,
+    name: 'AdminGroupsFilteredSearchAndSort',
+    render(createElement) {
+      return createElement(FilteredSearchAndSort);
+    },
+  });
+};
diff --git a/app/assets/javascripts/pages/admin/groups/index/index.js b/app/assets/javascripts/pages/admin/groups/index/index.js
index 36c70a8664376..c438d0dd15c12 100644
--- a/app/assets/javascripts/pages/admin/groups/index/index.js
+++ b/app/assets/javascripts/pages/admin/groups/index/index.js
@@ -1,3 +1,5 @@
 import initConfirmDanger from '~/init_confirm_danger';
+import { initAdminGroupsFilteredSearchAndSort } from '~/admin/groups/index';
 
 initConfirmDanger();
+initAdminGroupsFilteredSearchAndSort();
diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb
index 0b58ebc1fb25b..b2ad8c8fe3d04 100644
--- a/app/helpers/explore_helper.rb
+++ b/app/helpers/explore_helper.rb
@@ -21,10 +21,6 @@ def filter_projects_path(options = {})
     request_path_with_options(options)
   end
 
-  def filter_groups_path(options = {})
-    request_path_with_options(options)
-  end
-
   def public_visibility_restricted?
     Gitlab::VisibilityLevel.public_visibility_restricted?
   end
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index 7798f3905da7d..668fd204c7d55 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -73,23 +73,6 @@ def forks_reverse_sort_options_hash
     }
   end
 
-  def groups_sort_options_hash
-    {
-      sort_value_name => sort_title_name,
-      sort_value_name_desc => sort_title_name_desc,
-      sort_value_recently_created => sort_title_recently_created,
-      sort_value_oldest_created => sort_title_oldest_created,
-      sort_value_latest_activity => sort_title_recently_updated,
-      sort_value_oldest_activity => sort_title_oldest_updated
-    }
-  end
-
-  def admin_groups_sort_options_hash
-    groups_sort_options_hash.merge(
-      sort_value_largest_group => sort_title_largest_group
-    )
-  end
-
   def milestones_sort_options_hash
     {
       sort_value_due_date_soon => sort_title_due_date_soon,
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 2e7584eeac295..c32fef99c7b55 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -6,14 +6,9 @@
     = link_button_to new_admin_group_path, variant: :confirm do
       = _('New group')
 
-.md:gl-flex.gl-min-w-0.gl-grow.row-content-block
-  = form_tag admin_groups_path, method: :get, class: 'js-search-form gl-w-full' do |f|
-    = hidden_field_tag :sort, @sort
-    .search-holder
-      .search-field-holder
-        = search_field_tag :name, params[:name].presence, class: "form-control search-text-input js-search-input", spellcheck: false, placeholder: 'Search by name', data: { testid: 'group-search-field' }
-        = sprite_icon('search', css_class: 'search-icon')
-      = render "shared/groups/dropdown", options_hash: admin_groups_sort_options_hash
+#js-admin-groups-filtered-search-and-sort
+  -# This element takes up space while Vue is rendering to avoid page jump
+  .gl-h-12
 
 - if @groups.any?
   %ul.content-list
diff --git a/app/views/shared/groups/_dropdown.html.haml b/app/views/shared/groups/_dropdown.html.haml
deleted file mode 100644
index 0b39f42165f37..0000000000000
--- a/app/views/shared/groups/_dropdown.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-- options_hash = local_assigns.fetch(:options_hash, groups_sort_options_hash)
-- groups_sort_options = options_hash.map { |value, title| { value: value, text: title, href: filter_groups_path(sort: value) } }
-
-%div{ data: { testid: 'group_sort_by_dropdown' } }
-  = gl_redirect_listbox_tag groups_sort_options, project_list_sort_by, data: { placement: 'right' }
diff --git a/doc/administration/admin_area.md b/doc/administration/admin_area.md
index f8e7f99eec3af..54cec6cd71fd9 100644
--- a/doc/administration/admin_area.md
+++ b/doc/administration/admin_area.md
@@ -239,8 +239,15 @@ To access the Groups page:
 For each group, the page displays their name, description, size, number of projects in the group,
 number of members, and whether the group is private, internal, or public. To edit a group, in the group's row, select **Edit**. To delete the group, in the group's row, select **Delete**.
 
-To change the sort order, select the sort dropdown list and select the desired order. The default
-sort order is by **Last created**.
+To change the sort order, select the sort dropdown list and choose the desired order.
+You can sort groups by:
+
+- Created date (default)
+- Updated date
+- Storage size
+
+The storage size option sorts groups by the total storage used, including Git repositories
+and Large File Storage (LFS) for all projects in the group. For more information, see [usage quotas](../user/storage_usage_quotas.md).
 
 To search for groups by name, enter your criteria in the search field. The group search is case
 insensitive, and applies partial matching.
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3b4cf3b7031e0..16d41e20db1c0 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -53368,6 +53368,9 @@ msgstr ""
 msgid "Storage nodes for new repositories"
 msgstr ""
 
+msgid "Storage size"
+msgstr ""
+
 msgid "Storage:"
 msgstr ""
 
diff --git a/qa/qa/page/admin/overview/groups/index.rb b/qa/qa/page/admin/overview/groups/index.rb
index 6157e7a5e4db1..85a727766c95d 100644
--- a/qa/qa/page/admin/overview/groups/index.rb
+++ b/qa/qa/page/admin/overview/groups/index.rb
@@ -6,8 +6,8 @@ module Admin
       module Overview
         module Groups
           class Index < QA::Page::Base
-            view 'app/views/admin/groups/index.html.haml' do
-              element 'group-search-field', required: true
+            view 'app/assets/javascripts/admin/groups/components/filtered_search_and_sort.vue' do
+              element 'admin-groups-filtered-search-and-sort', required: true
             end
 
             view 'app/views/admin/groups/_group.html.haml' do
@@ -16,7 +16,9 @@ class Index < QA::Page::Base
             end
 
             def search_group(group_name)
-              find_element('group-search-field').set(group_name).send_keys(:return)
+              within_element('admin-groups-filtered-search-and-sort') do
+                find_element('filtered-search-term-input').set(group_name).send_keys(:return)
+              end
             end
 
             def click_group(group_name)
diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb
index ad53e44052f6c..c7d71395c4cb9 100644
--- a/spec/controllers/admin/groups_controller_spec.rb
+++ b/spec/controllers/admin/groups_controller_spec.rb
@@ -25,37 +25,6 @@
       expect(assigns(:groups)).to match_array([group, group_2, group_3])
     end
 
-    it 'renders a correct list of sort by options' do
-      get :index
-
-      html_rendered = Nokogiri::HTML(response.body)
-      sort_options = Gitlab::Json.parse(html_rendered.css('[data-items]')[0]['data-items'])
-
-      expect(response).to render_template('shared/groups/_dropdown')
-
-      expect(sort_options.size).to eq(7)
-      expect(sort_options[0]['value']).to eq('name_asc')
-      expect(sort_options[0]['text']).to eq(s_('SortOptions|Name'))
-
-      expect(sort_options[1]['value']).to eq('name_desc')
-      expect(sort_options[1]['text']).to eq(s_('SortOptions|Name, descending'))
-
-      expect(sort_options[2]['value']).to eq('created_desc')
-      expect(sort_options[2]['text']).to eq(s_('SortOptions|Last created'))
-
-      expect(sort_options[3]['value']).to eq('created_asc')
-      expect(sort_options[3]['text']).to eq(s_('SortOptions|Oldest created'))
-
-      expect(sort_options[4]['value']).to eq('latest_activity_desc')
-      expect(sort_options[4]['text']).to eq(_('Updated date'))
-
-      expect(sort_options[5]['value']).to eq('latest_activity_asc')
-      expect(sort_options[5]['text']).to eq(s_('SortOptions|Oldest updated'))
-
-      expect(sort_options[6]['value']).to eq('storage_size_desc')
-      expect(sort_options[6]['text']).to eq(s_('SortOptions|Largest group'))
-    end
-
     context 'when a sort param is present' do
       it 'returns a sorted by name_asc result' do
         get :index, params: { sort: 'name_asc' }
@@ -119,19 +88,19 @@
   describe 'POST #create' do
     it 'creates group' do
       expect do
-        post :create, params: { group: {  path: 'test', name: 'test' } }
+        post :create, params: { group: { path: 'test', name: 'test' } }
       end.to change { Group.count }.by(1)
     end
 
     it 'creates namespace_settings for group' do
       expect do
-        post :create, params: { group: {  path: 'test', name: 'test' } }
+        post :create, params: { group: { path: 'test', name: 'test' } }
       end.to change { NamespaceSetting.count }.by(1)
     end
 
     it 'creates admin_note for group' do
       expect do
-        post :create, params: { group: {  path: 'test', name: 'test', admin_note_attributes: { note: 'test' } } }
+        post :create, params: { group: { path: 'test', name: 'test', admin_note_attributes: { note: 'test' } } }
       end.to change { Namespace::AdminNote.count }.by(1)
     end
 
diff --git a/spec/frontend/admin/groups/components/filtered_search_and_sort_spec.js b/spec/frontend/admin/groups/components/filtered_search_and_sort_spec.js
new file mode 100644
index 0000000000000..c3e7ff13b8e65
--- /dev/null
+++ b/spec/frontend/admin/groups/components/filtered_search_and_sort_spec.js
@@ -0,0 +1,125 @@
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import AdminGroupsFilteredSearchAndSort from '~/admin/groups/components/filtered_search_and_sort.vue';
+import FilteredSearchAndSort from '~/groups_projects/components/filtered_search_and_sort.vue';
+import {
+  FILTERED_SEARCH_TERM_KEY,
+  SORT_DIRECTION_ASC,
+  SORT_DIRECTION_DESC,
+  SORT_OPTION_CREATED_DATE,
+  SORT_OPTION_UPDATED_DATE,
+  SORT_OPTIONS,
+} from '~/admin/groups/constants';
+import { visitUrl } from '~/lib/utils/url_utility';
+
+jest.mock('~/lib/utils/url_utility', () => ({
+  ...jest.requireActual('~/lib/utils/url_utility'),
+  visitUrl: jest.fn(),
+}));
+
+describe('AdminGroupsFilteredSearchAndSort', () => {
+  let wrapper;
+
+  const createComponent = () => {
+    wrapper = shallowMountExtended(AdminGroupsFilteredSearchAndSort, {});
+  };
+
+  const findFilteredSearchAndSort = () => wrapper.findComponent(FilteredSearchAndSort);
+
+  it('renders FilteredSearchAndSort component with the correct initial props', () => {
+    createComponent();
+
+    expect(findFilteredSearchAndSort().props()).toMatchObject({
+      filteredSearchNamespace: 'admin-groups',
+      filteredSearchTokens: [],
+      filteredSearchTermKey: 'name',
+      filteredSearchRecentSearchesStorageKey: 'groups',
+      isAscending: false,
+      sortOptions: SORT_OPTIONS,
+      activeSortOption: SORT_OPTION_CREATED_DATE,
+      filteredSearchQuery: {},
+    });
+  });
+
+  describe('when the search bar is submitted', () => {
+    const searchTerm = 'test';
+
+    beforeEach(() => {
+      createComponent();
+
+      findFilteredSearchAndSort().vm.$emit('filter', {
+        [FILTERED_SEARCH_TERM_KEY]: searchTerm,
+      });
+    });
+
+    it('visits the URL with the correct query string', () => {
+      expect(visitUrl).toHaveBeenCalledWith(`?${FILTERED_SEARCH_TERM_KEY}=${searchTerm}`);
+    });
+  });
+
+  describe('when the sort item is changed', () => {
+    beforeEach(() => {
+      createComponent();
+
+      findFilteredSearchAndSort().vm.$emit('sort-by-change', SORT_OPTION_UPDATED_DATE.value);
+    });
+
+    it('visits the URL with the correct query string', () => {
+      expect(visitUrl).toHaveBeenCalledWith(
+        `?sort=${SORT_OPTION_UPDATED_DATE.value}_${SORT_DIRECTION_DESC}`,
+      );
+    });
+  });
+
+  describe('when the sort direction is changed', () => {
+    beforeEach(() => {
+      createComponent();
+
+      findFilteredSearchAndSort().vm.$emit('sort-direction-change', true);
+    });
+
+    it('visits the URL with the correct query string', () => {
+      expect(visitUrl).toHaveBeenCalledWith(
+        `?sort=${SORT_OPTION_CREATED_DATE.value}_${SORT_DIRECTION_ASC}`,
+      );
+    });
+  });
+
+  describe('when the search term is present and the sort item is changed', () => {
+    const searchTerm = 'group-name';
+
+    beforeEach(() => {
+      setWindowLocation(`?${FILTERED_SEARCH_TERM_KEY}=${searchTerm}`);
+
+      createComponent();
+
+      findFilteredSearchAndSort().vm.$emit('sort-direction-change', true);
+    });
+
+    it('visits the URL with the correct query string', () => {
+      expect(visitUrl).toHaveBeenCalledWith(
+        `?${FILTERED_SEARCH_TERM_KEY}=${searchTerm}&sort=${SORT_OPTION_CREATED_DATE.value}_${SORT_DIRECTION_ASC}`,
+      );
+    });
+  });
+
+  describe('when the sort item is present and the search term is changed', () => {
+    const searchTerm = 'group-name';
+
+    beforeEach(() => {
+      setWindowLocation(`?sort=${SORT_OPTION_CREATED_DATE.value}_${SORT_DIRECTION_ASC}`);
+
+      createComponent();
+
+      findFilteredSearchAndSort().vm.$emit('filter', {
+        [FILTERED_SEARCH_TERM_KEY]: searchTerm,
+      });
+    });
+
+    it('visits the URL with the correct query string', () => {
+      expect(visitUrl).toHaveBeenCalledWith(
+        `?${FILTERED_SEARCH_TERM_KEY}=${searchTerm}&sort=${SORT_OPTION_CREATED_DATE.value}_${SORT_DIRECTION_ASC}`,
+      );
+    });
+  });
+});
diff --git a/spec/helpers/sorting_helper_spec.rb b/spec/helpers/sorting_helper_spec.rb
index dccea889d55a2..a0ae1d1ca5fa4 100644
--- a/spec/helpers/sorting_helper_spec.rb
+++ b/spec/helpers/sorting_helper_spec.rb
@@ -142,23 +142,6 @@ def project_common_options
     end
   end
 
-  describe '#groups_sort_options_hash' do
-    let(:expected_options) do
-      {
-        sort_value_name => sort_title_name,
-        sort_value_name_desc => sort_title_name_desc,
-        sort_value_recently_created => sort_title_recently_created,
-        sort_value_oldest_created => sort_title_oldest_created,
-        sort_value_latest_activity => sort_title_recently_updated,
-        sort_value_oldest_activity => sort_title_oldest_updated
-      }
-    end
-
-    it 'returns a hash of available sorting options for the groups' do
-      expect(groups_sort_options_hash).to eq(expected_options)
-    end
-  end
-
   describe 'with `projects` controller' do
     before do
       stub_controller_path 'projects'
diff --git a/spec/views/shared/groups/_dropdown.html.haml_spec.rb b/spec/views/shared/groups/_dropdown.html.haml_spec.rb
deleted file mode 100644
index 8a09c0de383ad..0000000000000
--- a/spec/views/shared/groups/_dropdown.html.haml_spec.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'shared/groups/_dropdown.html.haml' do
-  describe 'render' do
-    describe 'when a sort option is not selected' do
-      before do
-        render 'shared/groups/dropdown'
-      end
-
-      it 'renders a default sort option' do
-        expect(rendered).to have_content 'Last created'
-      end
-
-      it 'renders correct sort by options' do
-        html_rendered = Nokogiri::HTML(rendered)
-        sort_options = Gitlab::Json.parse(html_rendered.css('[data-items]')[0]['data-items'])
-
-        expect(sort_options.size).to eq(6)
-        expect(sort_options[0]['value']).to eq('name_asc')
-        expect(sort_options[0]['text']).to eq(s_('SortOptions|Name'))
-
-        expect(sort_options[1]['value']).to eq('name_desc')
-        expect(sort_options[1]['text']).to eq(s_('SortOptions|Name, descending'))
-
-        expect(sort_options[2]['value']).to eq('created_desc')
-        expect(sort_options[2]['text']).to eq(s_('SortOptions|Last created'))
-
-        expect(sort_options[3]['value']).to eq('created_asc')
-        expect(sort_options[3]['text']).to eq(s_('SortOptions|Oldest created'))
-
-        expect(sort_options[4]['value']).to eq('latest_activity_desc')
-        expect(sort_options[4]['text']).to eq(_('Updated date'))
-
-        expect(sort_options[5]['value']).to eq('latest_activity_asc')
-        expect(sort_options[5]['text']).to eq(s_('SortOptions|Oldest updated'))
-      end
-    end
-
-    describe 'when a sort option is selected' do
-      before do
-        assign(:sort, 'name_desc')
-
-        render 'shared/groups/dropdown'
-      end
-
-      it 'renders the selected sort option' do
-        expect(rendered).to have_content 'Name, descending'
-      end
-    end
-  end
-end
-- 
GitLab