Skip to content

Commit

Permalink
Paginate using the footer
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverguenther committed Jan 31, 2025
1 parent b6312c1 commit bcd7d95
Showing 10 changed files with 145 additions and 28 deletions.
22 changes: 22 additions & 0 deletions app/helpers/pagination_helper.rb
Original file line number Diff line number Diff line change
@@ -29,6 +29,10 @@
require "will_paginate"

module PaginationHelper
SHOW_MORE_DEFAULT_LIMIT = 5
SHOW_MORE_DEFAULT_INCREMENT = 20
SHOW_MORE_MAX_LIMIT = 1000

def pagination_links_full(paginator, options = {})
return unless paginator.total_entries > 0

@@ -151,6 +155,24 @@ def per_page_param(options = params)
end
end

##
# For "Show more" paginated links, we want to load an initial number of items (defaulting to 5)
# unless a higher number is provided. These values do not correspond to the per_page_options
def show_more_limit_param(options = params, initial_limit: SHOW_MORE_DEFAULT_LIMIT)
limit = options[:limit].to_i
if limit.zero?
initial_limit
else
[limit, SHOW_MORE_MAX_LIMIT].min
end
end

##
# Paginate an AR relation for the "show more" pagination functionality
def show_more_pagination(paginator, options = params)
paginator.paginate(page: 1, per_page: show_more_limit_param(options))
end

class LinkRenderer < ::WillPaginate::ActionView::LinkRenderer
def to_html
pagination.inject("") do |html, item|
17 changes: 17 additions & 0 deletions modules/meeting/app/components/meetings/table_component.rb
Original file line number Diff line number Diff line change
@@ -44,6 +44,23 @@ def sortable?
false
end

def paginated?
false
end

def has_footer?
model.is_a?(ActiveRecord::Relation) &&
(model.total_entries > model.size)
end

def footer
render Meetings::TableFooterComponent.new(
upcoming: options[:upcoming],
total: model.total_entries,
count: model.size
)
end

def has_actions?
true
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<%#-- 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.
++#%>

<%=
component_wrapper do
flex_layout(justify_content: :space_between) do |flex|
flex.with_column(classes: "ellipsis") do
render(Primer::BaseComponent.new(tag: :span, color: :muted)) do
concat render(Primer::Beta::Octicon.new(icon: :iterations, mr: 1, ml: 1))
concat render(Primer::Beta::Text.new(font_weight: :bold)) { label }
end
end
flex.with_column do
render(
Primer::Beta::Button.new(
scheme: :link,
size: :medium,
tag: :a,
href: url_for(limit: next_count, upcoming:),
data: {
keep_scroll_position_target: "triggerButton"
}
)
) do
I18n.t(:label_recurring_meeting_show_more)
end
end
end
end
%>
Original file line number Diff line number Diff line change
@@ -26,16 +26,31 @@
# See COPYRIGHT and LICENSE files for more details.
#++

module RecurringMeetings
class ShowComponent < ApplicationComponent
module Meetings
class TableFooterComponent < ApplicationComponent
include ApplicationHelper
include OpTurbo::Streamable
include OpPrimer::ComponentHelpers

def initialize(meeting:, project:)
attr_reader :total, :count, :upcoming

def initialize(count:, total:, upcoming:)
super

@meeting = meeting
@project = project
@count = count
@total = total
@upcoming = upcoming
end

def label
I18n.t(:label_meeting_more, count: total - count)
end

def next_count
[
total,
count + PaginationHelper::SHOW_MORE_DEFAULT_INCREMENT
].min
end
end
end
Empty file.
12 changes: 4 additions & 8 deletions modules/meeting/app/controllers/meetings_controller.rb
Original file line number Diff line number Diff line change
@@ -357,7 +357,7 @@ def load_meetings

# We group meetings into individual groups, but only for upcoming meetings
if params[:upcoming] == "false"
@meetings = @query.results.paginate(page: page_param, per_page: per_page_param)
@meetings = show_more_pagination(@query.results)
else
@grouped_meetings = group_meetings(@query.results)
end
@@ -366,13 +366,9 @@ def load_meetings
def group_meetings(all_meetings) # rubocop:disable Metrics/AbcSize
next_week = Time.current.next_occurring(Redmine::I18n.start_of_week)
groups = Hash.new { |h, k| h[k] = [] }
groups[:later] = all_meetings
.where(start_time: next_week..)
.order(start_time: :asc)
.paginate(page: page_param, per_page: per_page_param)

# If we're on the second page, only show the "later" group
return groups if page_param > 1
groups[:later] = show_more_pagination(all_meetings
.where(start_time: next_week..)
.order(start_time: :asc))

all_meetings
.where(start_time: ...next_week)
15 changes: 9 additions & 6 deletions modules/meeting/app/controllers/recurring_meetings_controller.rb
Original file line number Diff line number Diff line change
@@ -23,13 +23,15 @@ class RecurringMeetingsController < ApplicationController
menu_item :meetings

def index
@recurring_meetings =
results =
if @project
RecurringMeeting.visible.where(project_id: @project.id)
else
RecurringMeeting.visible
end

@recurring_meetings = show_more_pagination(results)

respond_to do |format|
format.html do
render :index, locals: { menu_name: project_or_global_menu }
@@ -51,11 +53,12 @@ def show # rubocop:disable Metrics/AbcSize
params[:count].to_i + 5
end

if @direction == "past"
@meetings = @recurring_meeting.scheduled_instances(upcoming: false).limit(@count)
else
@meetings, @planned_meetings = upcoming_meetings(count: @count)
end

if @direction == "past"

Check notice on line 57 in modules/meeting/app/controllers/recurring_meetings_controller.rb

GitHub Actions / rubocop

[rubocop] modules/meeting/app/controllers/recurring_meetings_controller.rb#L56-L57 <Layout/EmptyLines>

Extra blank line detected.
Raw output
modules/meeting/app/controllers/recurring_meetings_controller.rb:56:1: C: Layout/EmptyLines: Extra blank line detected.
@meetings = @recurring_meeting.scheduled_instances(upcoming: false).limit(@count)
else
@meetings, @planned_meetings =upcoming_meetings(count: @count)

Check notice on line 60 in modules/meeting/app/controllers/recurring_meetings_controller.rb

GitHub Actions / rubocop

[rubocop] modules/meeting/app/controllers/recurring_meetings_controller.rb#L60 <Layout/SpaceAroundOperators>

Surrounding space missing for operator `=`.
Raw output
modules/meeting/app/controllers/recurring_meetings_controller.rb:60:38: C: Layout/SpaceAroundOperators: Surrounding space missing for operator `=`.
end

Check notice on line 61 in modules/meeting/app/controllers/recurring_meetings_controller.rb

GitHub Actions / rubocop

[rubocop] modules/meeting/app/controllers/recurring_meetings_controller.rb#L57-L61 <Layout/IndentationConsistency>

Inconsistent indentation detected.
Raw output
modules/meeting/app/controllers/recurring_meetings_controller.rb:57:7: C: Layout/IndentationConsistency: Inconsistent indentation detected.

respond_to do |format|
format.html do
6 changes: 5 additions & 1 deletion modules/meeting/app/views/meetings/index.html.erb
Original file line number Diff line number Diff line change
@@ -36,5 +36,9 @@ See COPYRIGHT and LICENSE files for more details.
<% elsif @meetings.empty? -%>
<%= no_results_box %>
<% else %>
<%= render Meetings::TableComponent.new(rows: @meetings, current_project: @project) %>
<%= render Meetings::TableComponent.new(
rows: @meetings,
current_project: @project,
upcoming: params[:upcoming],
) %>
<% end -%>
3 changes: 3 additions & 0 deletions modules/meeting/config/locales/en.yml
Original file line number Diff line number Diff line change
@@ -157,6 +157,9 @@ en:
label_recurring_meeting_series_delete: "Delete meeting series"
label_recurring_meeting_series_end: "End meeting series"
label_recurring_meeting_series_end_now: "End series now"
label_meeting_more:
one: "There is one more meeting."
other: "There are %{count} more meetings."
label_my_meetings: "My meetings"
label_all_meetings: "All meetings"
label_upcoming_meetings: "Upcoming meetings"
17 changes: 9 additions & 8 deletions modules/meeting/spec/requests/meetings_index_spec.rb
Original file line number Diff line number Diff line change
@@ -237,27 +237,28 @@
end
end

describe "paginating options", with_settings: { per_page_options: "1,5" } do
context "when requesting the first page with per_page=1" do
let(:request) { get "/projects/#{project.id}/meetings?page=1&per_page=1" }
describe "paginating options" do
context "when requesting the first page with limit=1" do
let(:request) { get "/projects/#{project.id}/meetings?limit=1" }

it "shows a pagination" do
expect(subject).to have_http_status(:ok)

expect(page).to have_css(".op-pagination--pages .op-pagination--item_current", text: "1")
expect(page).to have_css("#meetings-table-footer-component")
expect(page).to have_text "There is one more meeting"
expect(page).to have_text "meeting on next monday"
expect(page).to have_no_text "meeting on next friday"
end
end

context "when requesting the second page with per_page=2" do
let(:request) { get "/projects/#{project.id}/meetings?page=2&per_page=1" }
context "when requesting the second page with limit=100" do
let(:request) { get "/projects/#{project.id}/meetings?limit=100" }

it "shows a pagination" do
expect(subject).to have_http_status(:ok)

expect(page).to have_css(".op-pagination--pages .op-pagination--item_current", text: "2")
expect(page).to have_no_text "meeting on next monday"
expect(page).to have_no_css("#meetings-table-footer-component")
expect(page).to have_text "meeting on next monday"
expect(page).to have_text "meeting on next friday"
end
end

0 comments on commit bcd7d95

Please sign in to comment.