Commit dd4f91c9 authored by Nick Kipling's avatar Nick Kipling Committed by Robert Speicher

Adding sort to the projects package list page

Updated the UI to include sort dropdown
Added reorder options to the packages list
Updated sorting helper with package options
Added tests to test that sorting works
Updated pot file
Added changelog
parent f502d4e2
---
title: Adds sorting of packages at the project level
merge_request: 15448
author:
type: added
...@@ -12,6 +12,6 @@ module PackagesAccess ...@@ -12,6 +12,6 @@ module PackagesAccess
def verify_packages_enabled! def verify_packages_enabled!
render_404 unless Gitlab.config.packages.enabled && render_404 unless Gitlab.config.packages.enabled &&
project.feature_available?(:packages) project.feature_available?(:packages)
end end
end end
...@@ -4,11 +4,15 @@ module Projects ...@@ -4,11 +4,15 @@ module Projects
module Packages module Packages
class PackagesController < ApplicationController class PackagesController < ApplicationController
include PackagesAccess include PackagesAccess
include SortingHelper
before_action :authorize_destroy_package!, only: [:destroy] before_action :authorize_destroy_package!, only: [:destroy]
def index def index
@packages = project.packages.has_version.page(params[:page]) @packages = project.packages
.has_version
.sort_by_attribute(@sort = params[:sort] || 'created_desc')
.page(params[:page])
end end
def show def show
......
# frozen_string_literal: true
module EE
module PackagesHelper
def package_sort_path(options = {})
"#{request.path}?#{options.to_param}"
end
end
end
...@@ -64,6 +64,43 @@ module EE ...@@ -64,6 +64,43 @@ module EE
end end
end end
def packages_sort_options_hash
{
sort_value_recently_created => sort_title_created_date,
sort_value_oldest_created => sort_title_created_date,
sort_value_name => sort_title_name,
sort_value_name_desc => sort_title_name,
sort_value_version_desc => sort_title_version,
sort_value_version_asc => sort_title_version,
sort_value_type_desc => sort_value_type_desc,
sort_value_type_asc => sort_title_type
}
end
def packages_reverse_sort_order_hash
{
sort_value_recently_created => sort_value_oldest_created,
sort_value_oldest_created => sort_value_recently_created,
sort_value_name => sort_value_name_desc,
sort_value_name_desc => sort_value_name,
sort_value_version_desc => sort_value_version_asc,
sort_value_version_asc => sort_value_version_desc,
sort_value_type_desc => sort_value_type_asc,
sort_value_type_asc => sort_value_type_desc
}
end
def packages_sort_option_title(sort_value)
packages_sort_options_hash[sort_value]
end
def packages_sort_direction_button(sort_value)
reverse_sort = packages_reverse_sort_order_hash[sort_value]
url = package_sort_path(sort: reverse_sort)
sort_direction_button(url, reverse_sort, sort_value)
end
# Creates a button with the opposite ordering for the current field in UI. # Creates a button with the opposite ordering for the current field in UI.
def sort_order_button(sort) def sort_order_button(sort)
opposite_sorting_param = epics_ordering_options_hash[sort] || epics_ordering_options_hash.key(sort) opposite_sorting_param = epics_ordering_options_hash[sort] || epics_ordering_options_hash.key(sort)
...@@ -95,6 +132,14 @@ module EE ...@@ -95,6 +132,14 @@ module EE
s_('SortOptions|Weight') s_('SortOptions|Weight')
end end
def sort_title_version
s_('SortOptions|Version')
end
def sort_title_type
s_('SortOptions|Type')
end
def sort_value_start_date def sort_value_start_date
'start_date_asc' 'start_date_asc'
end end
...@@ -118,5 +163,21 @@ module EE ...@@ -118,5 +163,21 @@ module EE
def sort_value_weight def sort_value_weight
'weight' 'weight'
end end
def sort_value_version_asc
'version_asc'
end
def sort_value_version_desc
'version_desc'
end
def sort_value_type_asc
'type_asc'
end
def sort_value_type_desc
'type_desc'
end
end end
end end
...@@ -24,6 +24,16 @@ class Packages::Package < ApplicationRecord ...@@ -24,6 +24,16 @@ class Packages::Package < ApplicationRecord
scope :preload_files, -> { preload(:package_files) } scope :preload_files, -> { preload(:package_files) }
scope :last_of_each_version, -> { where(id: all.select('MAX(id) AS id').group(:version)) } scope :last_of_each_version, -> { where(id: all.select('MAX(id) AS id').group(:version)) }
# Sorting
scope :order_created, -> { reorder('created_at ASC') }
scope :order_created_desc, -> { reorder('created_at DESC') }
scope :order_name, -> { reorder('name ASC') }
scope :order_name_desc, -> { reorder('name DESC') }
scope :order_version, -> { reorder('version ASC') }
scope :order_version_desc, -> { reorder('version DESC') }
scope :order_type, -> { reorder('package_type ASC') }
scope :order_type_desc, -> { reorder('package_type DESC') }
def self.for_projects(projects) def self.for_projects(projects)
return none unless projects.any? return none unless projects.any?
...@@ -40,6 +50,20 @@ class Packages::Package < ApplicationRecord ...@@ -40,6 +50,20 @@ class Packages::Package < ApplicationRecord
.where(packages_package_files: { file_name: file_name }).last! .where(packages_package_files: { file_name: file_name }).last!
end end
def self.sort_by_attribute(method)
case method.to_s
when 'created_asc' then order_created
when 'name_asc' then order_name
when 'name_desc' then order_name_desc
when 'version_asc' then order_version
when 'version_desc' then order_version_desc
when 'type_asc' then order_type
when 'type_desc' then order_type_desc
else
order_created_desc
end
end
private private
def valid_npm_package_name def valid_npm_package_name
......
- sort_value = @sort
- sort_title = packages_sort_option_title(sort_value)
- page_title _("Packages") - page_title _("Packages")
- can_destroy_package = can?(current_user, :destroy_package, @project) - can_destroy_package = can?(current_user, :destroy_package, @project)
- if @packages.any? - if @packages.any?
.d-flex.justify-content-end
.dropdown.inline.prepend-top-default.append-bottom-default.package-sort-dropdown
.btn-group{ role: 'group' }
.btn-group{ role: 'group' }
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
= sort_title
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
%li
= sortable_item(sort_title_created_date, package_sort_path(sort: sort_value_recently_created), sort_title)
= sortable_item(sort_title_name, package_sort_path(sort: sort_value_name_desc), sort_title)
= sortable_item(sort_title_version, package_sort_path(sort: sort_value_version_desc), sort_title)
= sortable_item(sort_title_type, package_sort_path(sort: sort_value_type_desc), sort_title)
= packages_sort_direction_button(sort_value)
.table-holder .table-holder
.gl-responsive-table-row.table-row-header{ role: 'row' } .gl-responsive-table-row.table-row-header.bg-secondary-50.px-2.border-top{ role: 'row' }
.table-section.section-30{ role: 'rowheader' } .table-section.section-30{ role: 'rowheader' }
= _('Name') = _('Name')
.table-section.section-20{ role: 'rowheader' } .table-section.section-20{ role: 'rowheader' }
...@@ -14,7 +31,7 @@ ...@@ -14,7 +31,7 @@
= _('Created') = _('Created')
.table-section.section-10{ role: 'rowheader' } .table-section.section-10{ role: 'rowheader' }
- @packages.each do |package| - @packages.each do |package|
.gl-responsive-table-row .gl-responsive-table-row.package-row.px-2
.table-section.section-30 .table-section.section-30
.table-mobile-header{ role: "rowheader" }= _("Name") .table-mobile-header{ role: "rowheader" }= _("Name")
.table-mobile-content.flex-truncate-parent .table-mobile-content.flex-truncate-parent
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
describe 'Packages' do describe 'Packages' do
include SortingHelper
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
...@@ -83,7 +85,86 @@ describe 'Packages' do ...@@ -83,7 +85,86 @@ describe 'Packages' do
end end
end end
context 'sorting when there are packages' do
let!(:aaa_package) do
create(
:maven_package,
name: 'aaa/company/app/my-app',
version: '1.0-SNAPSHOT',
project: project)
end
let!(:bbb_package) do
create(
:maven_package,
name: 'bbb/company/app/my-app',
version: '1.1-SNAPSHOT',
project: project)
end
it 'sorts by created date descending' do
visit project_packages_path(project, sort: sort_value_created_date)
expect(first_package).to include(bbb_package.name)
expect(last_package).to include(aaa_package.name)
end
it 'sorts by created date ascending' do
visit project_packages_path(project, sort: sort_value_oldest_created)
expect(first_package).to include(aaa_package.name)
expect(last_package).to include(bbb_package.name)
end
it 'sorts by name descending' do
visit project_packages_path(project, sort: sort_value_name_desc)
expect(first_package).to include(bbb_package.name)
expect(last_package).to include(aaa_package.name)
end
it 'sorts by name ascending' do
visit project_packages_path(project, sort: sort_value_name)
expect(first_package).to include(aaa_package.name)
expect(last_package).to include(bbb_package.name)
end
it 'sorts by version descending' do
visit project_packages_path(project, sort: sort_value_version_desc)
expect(first_package).to include(bbb_package.name)
expect(last_package).to include(aaa_package.name)
end
it 'sorts by version ascending' do
visit project_packages_path(project, sort: sort_value_version_asc)
expect(first_package).to include(aaa_package.name)
expect(last_package).to include(bbb_package.name)
end
end
context 'sorting different types of packages' do
let!(:maven_package) { create(:maven_package, project: project) }
let!(:npm_package) { create(:npm_package, project: project) }
it 'sorts by type descending' do
visit project_packages_path(project, sort: sort_value_type_desc)
expect(first_package).to include(npm_package.name)
expect(last_package).to include(maven_package.name)
end
it 'sorts by type ascending' do
visit project_packages_path(project, sort: sort_value_type_asc)
expect(first_package).to include(maven_package.name)
expect(last_package).to include(npm_package.name)
end
end
def visit_project_packages def visit_project_packages
visit project_packages_path(project) visit project_packages_path(project)
end end
def first_package
page.all('.table-holder .package-row').first.text
end
def last_package
page.all('.table-holder .package-row').last.text
end
end end
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Packages::Package, type: :model do RSpec.describe Packages::Package, type: :model do
include SortingHelper
describe 'relationships' do describe 'relationships' do
it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:package_files).dependent(:destroy) } it { is_expected.to have_many(:package_files).dependent(:destroy) }
......
...@@ -14938,6 +14938,12 @@ msgstr "" ...@@ -14938,6 +14938,12 @@ msgstr ""
msgid "SortOptions|Start soon" msgid "SortOptions|Start soon"
msgstr "" msgstr ""
msgid "SortOptions|Type"
msgstr ""
msgid "SortOptions|Version"
msgstr ""
msgid "SortOptions|Weight" msgid "SortOptions|Weight"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment