mirror of
https://github.com/we-promise/sure.git
synced 2026-09-07 15:44:21 +00:00
* fix(pagination): stop the pager wrapping on narrow screens
The page-number strip put the last page on a second line on a phone. The
container was a plain block, so the inline-flex links flowed as inline
content and wrapped like words. Measured on a 760-page list: the strip needs
~198px, and a 360px viewport leaves ~158px once the chevrons and the
per-page select are taken out.
The failure was not even consistent — at 402px the row wrapped, at 360px the
same markup overflowed its parent instead, because a block box inside a flex
item resolves its width differently at each size.
Make the strip a nowrap flex row, and drop the pages that aren't useful on a
phone. Small screens keep the first page, the last page, the current page and
the gaps ("1 … 5 … 760", ~148px); the full series returns from `sm` up. Short
series are left alone — Pagy only emits those when the collection has that
few pages, so hiding there would render a 3-page pager as "1 3".
`sm:` is a viewport breakpoint and the pager also lives in narrow columns on
wide screens (the account activity feed with both sidebars open), so the row
keeps `overflow-x-auto` as a backstop. At 320px, where even the reduced set
is wider than the space, it scrolls rather than wrapping.
Shared by twelve call sites, so this covers transactions, imports, rules,
account activity, statements, merchants, exports and the debug log.
* fix(pagination): mark the pages the mobile pager drops
Hiding the middle page links without saying so made the survivors read as
adjacent. On a 760-page collection sitting on page 3, Pagy emits
[1, 2, "3", 4, 5, :gap, 760] and a phone rendered "1 3 … 760" — page 2 gone
with nothing to show for it. A gapless 6-page series was worse: "1 6".
Each collapsed run now renders its own mobile-only ellipsis, unless Pagy
already put a gap beside that run, which would otherwise read as "… …".
[1, 2, "3", 4, 5, :gap, 760] -> 1 … 3 … 760
["1", 2, 3, 4, 5, 6] -> 1 … 6
["1", 2, 3, 4, 5, :gap, 760] -> 1 … 760 (Pagy's gap already covers it)
[1, :gap, 3, 4, "5", 6, 7, …] -> 1 … 5 … 760
The helper now returns a per-slot plan rather than a class, since the view
needs to know where a run starts, not just whether a slot is hidden.
Tests carry a property check — no two page numbers may survive side by side
unless they are consecutive — with a guard that fails if every pair happens to
be separated by an ellipsis, which would make the property vacuous. It was, on
the first pass.
85 lines
3.2 KiB
Ruby
85 lines
3.2 KiB
Ruby
require "test_helper"
|
|
|
|
class PaginationHelperTest < ActionView::TestCase
|
|
include PaginationHelper
|
|
|
|
# Renders what a phone would show: kept slots, plus a "…" wherever a run of
|
|
# pages is collapsed. Pagy marks the current page by making it a String.
|
|
def mobile_render(series)
|
|
plan = pagination_mobile_plan(series)
|
|
series.each_with_index.filter_map do |item, i|
|
|
case plan[i]
|
|
when :visible then item == :gap ? "…" : item.to_s
|
|
when :collapsed then "…"
|
|
end
|
|
end.join(" ")
|
|
end
|
|
|
|
test "collapses the middle of a long series behind an ellipsis" do
|
|
assert_equal "1 … 5 … 760", mobile_render([ 1, :gap, 3, 4, "5", 6, 7, :gap, 760 ])
|
|
end
|
|
|
|
test "keeps the first and last page of a long series" do
|
|
plan = pagination_mobile_plan([ "1", 2, 3, 4, 5, :gap, 760 ])
|
|
|
|
assert_equal :visible, plan.first, "first page must stay"
|
|
assert_equal :visible, plan.last, "last page must stay"
|
|
end
|
|
|
|
# A short series only happens when the collection has that few pages, so the
|
|
# numbers are 1-2 digits and the row fits. Hiding here would render "1 3".
|
|
test "leaves a short series untouched" do
|
|
assert_equal "1 2 3", mobile_render([ "1", 2, 3 ])
|
|
end
|
|
|
|
# Regression: dropping pages without standing an ellipsis in their place made
|
|
# the survivors look adjacent — "1 3 … 760" hid page 2 with no sign of it.
|
|
test "a collapsed run away from any gap gets its own ellipsis" do
|
|
assert_equal "1 … 3 … 760", mobile_render([ 1, 2, "3", 4, 5, :gap, 760 ])
|
|
end
|
|
|
|
test "a gapless series still marks the pages it drops" do
|
|
assert_equal "1 … 6", mobile_render([ "1", 2, 3, 4, 5, 6 ])
|
|
end
|
|
|
|
# Pagy's own gap already stands for the skipped pages; adding another beside
|
|
# it would render "… …".
|
|
test "does not double up on an ellipsis Pagy already emitted" do
|
|
assert_equal "1 … 760", mobile_render([ "1", 2, 3, 4, 5, :gap, 760 ])
|
|
end
|
|
|
|
test "never hides a gap or the current page" do
|
|
plan = pagination_mobile_plan([ 1, :gap, 3, 4, "5", 6, 7, :gap, 760 ])
|
|
|
|
assert_equal :visible, plan[1], ":gap must stay"
|
|
assert_equal :visible, plan[4], "current page must stay"
|
|
assert_equal :visible, plan[7], ":gap must stay"
|
|
end
|
|
|
|
# Whatever is collapsed, a phone must never be shown two numbers that are not
|
|
# actually consecutive without a "…" between them.
|
|
test "no surviving pair of pages reads as adjacent unless it is" do
|
|
checked = 0
|
|
|
|
[
|
|
[ 1, 2, "3", 4, 5, :gap, 760 ],
|
|
[ "1", 2, 3, 4, 5, 6 ],
|
|
[ 1, :gap, 3, 4, "5", 6, 7, :gap, 760 ],
|
|
[ 1, 2, 3, 4, "5", 6, 7 ],
|
|
[ 1, :gap, 756, 757, 758, 759, "760" ],
|
|
[ 1, "2", 3, 4, 5, 6 ] # current next to the first page: 1 and 2 do survive side by side
|
|
].each do |series|
|
|
tokens = mobile_render(series).split(" ")
|
|
tokens.each_cons(2) do |a, b|
|
|
next if a == "…" || b == "…"
|
|
checked += 1
|
|
assert_equal a.to_i + 1, b.to_i,
|
|
"#{series.inspect} rendered #{tokens.join(' ')}: #{a} and #{b} are not consecutive"
|
|
end
|
|
end
|
|
|
|
assert_operator checked, :>, 0,
|
|
"every pair was separated by an ellipsis, so this asserted nothing — add a series where two numbers survive side by side"
|
|
end
|
|
end
|