[#67969] Limit number of items in subitems widget

Allows for a configurable limit of items to be displayed. If a project
has more subitems than can be displayed, a "View all subitems" link is
displayed.

This commit also fixes ordering by creation date.
This commit is contained in:
Alexander Brandon Coles
2025-10-09 15:58:07 +01:00
parent 09f01a5db6
commit dd9415778f
6 changed files with 151 additions and 9 deletions
+32
View File
@@ -0,0 +1,32 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module FinderMethods
end
+38
View File
@@ -0,0 +1,38 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module FinderMethods
module WithMore
def first_with_more(limit)
result = self.limit(limit + 1)
[result.first(limit), result.size > limit]
end
end
end
@@ -30,12 +30,12 @@ See COPYRIGHT and LICENSE files for more details.
<%=
widget_wrapper do |container|
if children.empty?
if has_no_subitems?
container.with_row do
render(Primer::Beta::Text.new(color: :subtle)) { I18n.t("overviews.widgets.subitems.no_visible_children") }
end
else
children.find_each do |child|
displayed_subitems.each do |child|
container.with_row do
flex_layout do |row|
row.with_column do
@@ -60,6 +60,12 @@ See COPYRIGHT and LICENSE files for more details.
end
end
end
if has_more_subitems?
container.with_row do
render(Primer::Beta::Link.new(href: view_all_subitems_path)) { I18n.t("overviews.widgets.subitems.view_all_subitems") }
end
end
end
end
%>
@@ -32,22 +32,55 @@ module Overviews
module Widgets
class SubitemsComponent < Grids::WidgetComponent
include OpPrimer::ComponentHelpers
include Rails.application.routes.url_helpers
SUBITEMS_LIMIT = 10
private_constant :SUBITEMS_LIMIT
param :project
delegate :description, to: :project
option :limit, default: -> { SUBITEMS_LIMIT }
def title
I18n.t("overviews.widgets.subitems.in_this_#{project.workspace_type}")
end
def children
@children ||= project.children.visible
def displayed_subitems
subitems_with_more.first
end
def has_more_subitems?
subitems_with_more.last
end
def has_no_subitems?
displayed_subitems.empty?
end
def wrapper_arguments
{ full_width: true }
end
private
def subitems_with_more
@subitems_with_more ||= project.children
.visible(current_user)
.unscope(:order)
.newest
.extending(FinderMethods::WithMore)
.first_with_more(limit)
end
def view_all_subitems_path
@view_all_subitems_path ||= projects_path(::API::Decorators::QueryParamsRepresenter.new(project_query).to_h)
end
def project_query
ProjectQuery.new
.where("active", "=", "t")
.where("parent_id", "=", project.id)
end
end
end
end
+1
View File
@@ -10,6 +10,7 @@ en:
in_this_program: "In this program"
in_this_project: "In this project"
no_visible_children: "There are no visible children."
view_all_subitems: "View all subitems"
members:
title: "Latest members"
view_all_members: "View all members"
@@ -31,17 +31,20 @@
require "rails_helper"
RSpec.describe Overviews::Widgets::SubitemsComponent, type: :component do
include Rails.application.routes.url_helpers
def render_component(...)
render_inline(described_class.new(...))
end
let(:project) { build_stubbed(:project) }
let(:user) { build_stubbed(:user) }
let(:params) { {} }
current_user { user }
subject(:rendered_component) do
render_component(project)
render_component(project, current_user:, **params)
end
context "with no children" do
@@ -57,9 +60,38 @@ RSpec.describe Overviews::Widgets::SubitemsComponent, type: :component do
context "when visible to user" do
let(:user) { create(:admin) }
it "renders the children" do
expect(rendered_component).to have_list "In this project" do |list|
expect(list).to have_list_item count: 3, text: /My Project No. \d+/
context "and a limit greater than the number of all subitems (default: 10)" do
it "renders all subitems, without a 'view all' item", :aggregate_failures do
expect(rendered_component).to have_list "In this project" do |list|
expect(list).to have_list_item count: 3, text: /My Project No. \d+/
expect(list).to have_no_list_item text: "View all subitems"
end
end
it "does not render 'view all' link" do
expect(rendered_component).to have_no_link "View all subitems"
end
end
context "and a limit less than the number of all subitems" do
let(:params) { { limit: 2 } }
it "renders specified subitems, along with a 'view all' item", :aggregate_failures do
expect(rendered_component).to have_list "In this project" do |list|
expect(list).to have_list_item count: 2, text: /My Project No. \d+/
expect(list).to have_list_item text: "View all subitems"
end
end
it "renders 'view all' link to projects with parent filter", :aggregate_failures do
expect(rendered_component).to have_link "View all subitems" do |link|
uri = Addressable::URI.parse(link[:href])
expect(uri.path).to eq projects_path
expect(uri.query_values["filters"]).to be_json_eql %{[
{"active":{"operator":"=","values":["t"]}},
{"parent":{"operator":"=","values":["#{project.id}"]}}
]}
end
end
end
end