list: show only the top ten by default
Assisted-by: Claude Sonnet 5.5 via Claude Code
change smtpokmwsvpnrsurvqortnktzzlwlxtv
commit aa22d3144407235ea410073a3f5950c01f5b9378
author Alpha Chen <alpha@kejadlen.dev>
date
parent 1a2d4ce3
diff --git a/lib/rank_king/views/list/show.rb b/lib/rank_king/views/list/show.rb
index 5b57ff7..302b85b 100644
--- a/lib/rank_king/views/list/show.rb
+++ b/lib/rank_king/views/list/show.rb
@@ -8,6 +8,8 @@ module RankKing
   module Views
     module ListViews
       class Show < Phlex::HTML
+        VISIBLE = 10
+
         def initialize(list:, csrf:, axis: nil)
           @list = list
           @csrf = csrf
@@ -85,20 +87,15 @@ module RankKing
 
         def render_ranking(axis)
           ranked, unranked = axis.ranking
+          rows = ranked.each.with_index(1).map { |item, rank| [item, format("%02d", rank), false] } +
+                 unranked.map { |item| [item, "—", true] }
+          shown, hidden = rows.take(VISIBLE), rows.drop(VISIBLE)
 
-          ol(class: "card") do
-            ranked.each.with_index(1) do |item, rank|
-              li do
-                span(class: "type-label") { format("%02d", rank) }
-                plain item.name
-              end
-            end
-            unranked.each do |item|
-              li do
-                span(class: "type-label") { "—" }
-                plain item.name
-                span(class: "type-label") { "Unranked" }
-              end
+          render_rows(shown)
+          unless hidden.empty?
+            details do
+              summary { "Show #{hidden.size} more" }
+              render_rows(hidden, start: VISIBLE + 1)
             end
           end
 
@@ -106,6 +103,18 @@ module RankKing
             "Compare on #{axis.name}"
           end
         end
+
+        def render_rows(rows, start: nil)
+          ol(class: "card", start: start) do
+            rows.each do |item, label, unranked|
+              li do
+                span(class: "type-label") { label }
+                plain item.name
+                span(class: "type-label") { "Unranked" } if unranked
+              end
+            end
+          end
+        end
       end
     end
   end
diff --git a/public/css/app.css b/public/css/app.css
index e5c8867..4dcf274 100644
--- a/public/css/app.css
+++ b/public/css/app.css
@@ -174,6 +174,13 @@ main > header [popovertarget] { anchor-name: --rk-list-actions; }
   gap: var(--gl-space-s);
 }
 
+/* The rest of a long ranking, spaced like the tabpanel's other children. */
+[role="tabpanel"] details[open] {
+  display: flex;
+  flex-direction: column;
+  gap: var(--gl-space-s);
+}
+
 /* The rank sits in a fixed gutter; "Unranked" sits at the far end. */
 .card > li:has(> .type-label:first-child) { align-items: baseline; }
 
diff --git a/test/test_web.rb b/test/test_web.rb
index cbbf488..0d7a994 100644
--- a/test/test_web.rb
+++ b/test/test_web.rb
@@ -240,6 +240,34 @@ class TestWeb < Minitest::Test
     assert_includes body, "Compare on Taste"
   end
 
+  def test_show_list_folds_everything_past_ten_into_a_disclosure
+    list, axis = create_list_with_axis(items: (1..12).map { |n| "Item#{n}" }.join("\n"))
+    record_result(list, axis, winner: list.items.first, loser: list.items.last)
+
+    get "/lists/#{list.id}"
+    body = last_response.body[/role="tabpanel".*/m]
+    assert_includes body, "<summary>Show 2 more</summary>"
+    assert_includes body, '<ol class="card" start="11">'
+    visible, hidden = body.split("<details>")
+    assert_equal 10, visible.scan("<li>").size
+    assert_equal 2, hidden.scan("<li>").size
+    assert_includes hidden, "Unranked"
+  end
+
+  def test_show_list_folds_a_single_extra_item
+    list, = create_list_with_axis(items: (1..11).map { |n| "Item#{n}" }.join("\n"))
+
+    get "/lists/#{list.id}"
+    assert_includes last_response.body, "<summary>Show 1 more</summary>"
+  end
+
+  def test_show_list_has_no_disclosure_for_ten_or_fewer
+    list, = create_list_with_axis(items: (1..10).map { |n| "Item#{n}" }.join("\n"))
+
+    get "/lists/#{list.id}"
+    refute_includes last_response.body, "<details>"
+  end
+
   def test_show_list_selects_the_requested_axis
     create_list(name: "Foods", items: "Pizza\nSushi", axes: %w[Taste Value])
     list = RankKing::List.first