Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(example/pager): Add Query Preservation to AVL Pager #3760

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
62 changes: 48 additions & 14 deletions examples/gno.land/p/demo/avl/pager/pager.gno
Original file line number Diff line number Diff line change
Expand Up @@ -126,58 +126,92 @@ func (p *Pager) GetPageByPath(rawURL string) (*Page, error) {
}

// Picker generates the Markdown UI for the page Picker
func (p *Page) Picker() string {
func (p *Page) Picker(path string) string {
pageNumber := p.PageNumber
pageNumber = max(pageNumber, 1)

if p.TotalPages <= 1 {
return ""
}

u, _ := url.Parse(path)
query := u.Query()

baseQuery := ""
for key, values := range query {
if key != p.Pager.PageQueryParam {
for _, value := range values {
if baseQuery != "" {
baseQuery += "&"
}
baseQuery += key + "=" + url.QueryEscape(value)
}
}
}

md := ""

if p.HasPrev {
// Always show the first page link
md += ufmt.Sprintf("[%d](?%s=%d) | ", 1, p.Pager.PageQueryParam, 1)

// Before
if baseQuery != "" {
md += ufmt.Sprintf("[%d](?%s=%d&%s) | ", 1, p.Pager.PageQueryParam, 1, baseQuery)
} else {
md += ufmt.Sprintf("[%d](?%s=%d) | ", 1, p.Pager.PageQueryParam, 1)
}

if p.PageNumber > 4 {
md += "… | "
}

if p.PageNumber > 3 {
md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-2, p.Pager.PageQueryParam, p.PageNumber-2)
if baseQuery != "" {
md += ufmt.Sprintf("[%d](?%s=%d&%s) | ", p.PageNumber-2, p.Pager.PageQueryParam, p.PageNumber-2, baseQuery)
} else {
md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-2, p.Pager.PageQueryParam, p.PageNumber-2)
}
}

if p.PageNumber > 2 {
md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-1, p.Pager.PageQueryParam, p.PageNumber-1)
if baseQuery != "" {
md += ufmt.Sprintf("[%d](?%s=%d&%s) | ", p.PageNumber-1, p.Pager.PageQueryParam, p.PageNumber-1, baseQuery)
} else {
md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber-1, p.Pager.PageQueryParam, p.PageNumber-1)
}
}
}

if p.PageNumber > 0 && p.PageNumber <= p.TotalPages {
// Current page
md += ufmt.Sprintf("**%d**", p.PageNumber)
} else {
md += ufmt.Sprintf("_%d_", p.PageNumber)
}

if p.HasNext {
md += " | "

if p.PageNumber < p.TotalPages-1 {
md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+1, p.Pager.PageQueryParam, p.PageNumber+1)
if baseQuery != "" {
md += ufmt.Sprintf(" | [%d](?%s=%d&%s)", p.PageNumber+1, p.Pager.PageQueryParam, p.PageNumber+1, baseQuery)
} else {
md += ufmt.Sprintf(" | [%d](?%s=%d)", p.PageNumber+1, p.Pager.PageQueryParam, p.PageNumber+1)
}
}

if p.PageNumber < p.TotalPages-2 {
md += ufmt.Sprintf("[%d](?%s=%d) | ", p.PageNumber+2, p.Pager.PageQueryParam, p.PageNumber+2)
if baseQuery != "" {
md += ufmt.Sprintf(" | [%d](?%s=%d&%s)", p.PageNumber+2, p.Pager.PageQueryParam, p.PageNumber+2, baseQuery)
} else {
md += ufmt.Sprintf(" | [%d](?%s=%d)", p.PageNumber+2, p.Pager.PageQueryParam, p.PageNumber+2)
}
}

if p.PageNumber < p.TotalPages-3 {
md += " | "
md += " | "
}

// Always show the last page link
md += ufmt.Sprintf("[%d](?%s=%d)", p.TotalPages, p.Pager.PageQueryParam, p.TotalPages)
if baseQuery != "" {
md += ufmt.Sprintf(" | [%d](?%s=%d&%s)", p.TotalPages, p.Pager.PageQueryParam, p.TotalPages, baseQuery)
} else {
md += ufmt.Sprintf(" | [%d](?%s=%d)", p.TotalPages, p.Pager.PageQueryParam, p.TotalPages)
}
}

return md
Expand Down
93 changes: 75 additions & 18 deletions examples/gno.land/p/demo/avl/pager/pager_test.gno
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please write some tests that test specifically for the case you've handled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit: b05cd58

Original file line number Diff line number Diff line change
Expand Up @@ -129,17 +129,19 @@ func TestPage_Picker(t *testing.T) {
tests := []struct {
pageNumber int
pageSize int
path string
expected string
}{
{1, 2, "**1** | [2](?page=2) | [3](?page=3)"},
{2, 2, "[1](?page=1) | **2** | [3](?page=3)"},
{3, 2, "[1](?page=1) | [2](?page=2) | **3**"},
{1, 2, "/test", "**1** | [2](?page=2) | [3](?page=3)"},
{2, 2, "/test", "[1](?page=1) | **2** | [3](?page=3)"},
{3, 2, "/test", "[1](?page=1) | [2](?page=2) | **3**"},
{1, 2, "/test?foo=bar", "**1** | [2](?page=2&foo=bar) | [3](?page=3&foo=bar)"},
}

for _, tt := range tests {
page := pager.GetPageWithSize(tt.pageNumber, tt.pageSize)

ui := page.Picker()
ui := page.Picker(tt.path)
uassert.Equal(t, tt.expected, ui)
}
}
Expand All @@ -158,27 +160,25 @@ func TestPager_UI_WithManyPages(t *testing.T) {
tests := []struct {
pageNumber int
pageSize int
path string
expected string
}{
// XXX: -1
// XXX: 0
{1, 10, "**1** | [2](?page=2) | [3](?page=3) | … | [10](?page=10)"},
{2, 10, "[1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [10](?page=10)"},
{3, 10, "[1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | … | [10](?page=10)"},
{4, 10, "[1](?page=1) | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) | … | [10](?page=10)"},
{5, 10, "[1](?page=1) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [10](?page=10)"},
{6, 10, "[1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** | [7](?page=7) | [8](?page=8) | … | [10](?page=10)"},
{7, 10, "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"},
{8, 10, "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"},
{9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"},
{10, 10, "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"},
// XXX: 11
{1, 10, "/test", "**1** | [2](?page=2) | [3](?page=3) | … | [10](?page=10)"},
{2, 10, "/test", "[1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [10](?page=10)"},
{3, 10, "/test", "[1](?page=1) | [2](?page=2) | **3** | [4](?page=4) | [5](?page=5) | … | [10](?page=10)"},
{4, 10, "/test", "[1](?page=1) | [2](?page=2) | [3](?page=3) | **4** | [5](?page=5) | [6](?page=6) | … | [10](?page=10)"},
{5, 10, "/test", "[1](?page=1) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [10](?page=10)"},
{6, 10, "/test", "[1](?page=1) | … | [4](?page=4) | [5](?page=5) | **6** | [7](?page=7) | [8](?page=8) | … | [10](?page=10)"},
{7, 10, "/test", "[1](?page=1) | … | [5](?page=5) | [6](?page=6) | **7** | [8](?page=8) | [9](?page=9) | [10](?page=10)"},
{8, 10, "/test", "[1](?page=1) | … | [6](?page=6) | [7](?page=7) | **8** | [9](?page=9) | [10](?page=10)"},
{9, 10, "/test", "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"},
{10, 10, "/test", "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"},
}

for _, tt := range tests {
page := pager.GetPageWithSize(tt.pageNumber, tt.pageSize)

ui := page.Picker()
ui := page.Picker(tt.path)
uassert.Equal(t, tt.expected, ui)
}
}
Expand Down Expand Up @@ -222,3 +222,60 @@ func TestPager_ParseQuery(t *testing.T) {
}
}
}

func TestPage_PickerQueryParamPreservation(t *testing.T) {
tree := avl.NewTree()
for i := 1; i <= 6; i++ {
tree.Set(ufmt.Sprintf("key%d", i), i)
}

pager := NewPager(tree, 2, false)

tests := []struct {
name string
pageNumber int
path string
expected string
}{
{
name: "single query param",
pageNumber: 1,
path: "/test?foo=bar",
expected: "**1** | [2](?page=2&foo=bar) | [3](?page=3&foo=bar)",
},
{
name: "multiple query params",
pageNumber: 2,
path: "/test?foo=bar&baz=qux",
expected: "[1](?page=1&foo=bar&baz=qux) | **2** | [3](?page=3&foo=bar&baz=qux)",
},
{
name: "overwrite existing page param",
pageNumber: 1,
path: "/test?param1=value1&page=999&param2=value2",
expected: "**1** | [2](?page=2&param1=value1&param2=value2) | [3](?page=3&param1=value1&param2=value2)",
},
{
name: "empty query string",
pageNumber: 2,
path: "/test",
expected: "[1](?page=1) | **2** | [3](?page=3)",
},
{
name: "query string with only page param",
pageNumber: 2,
path: "/test?page=2",
expected: "[1](?page=1) | **2** | [3](?page=3)",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
page := pager.GetPageWithSize(tt.pageNumber, 2)
result := page.Picker(tt.path)
if result != tt.expected {
t.Errorf("\nwant: %s\ngot: %s", tt.expected, result)
}
})
}
}
2 changes: 1 addition & 1 deletion examples/gno.land/p/demo/avl/pager/z_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func main() {
for idx, item := range page.Items {
println(ufmt.Sprintf("- idx=%d key=%s value=%d", idx, item.Key, item.Value))
}
println(page.Picker())
println(page.Picker("/"))
println()
}
}
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/userbook/render.gno
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ func Render(path string) string {

out += "---\n\n"
out += "**Page " + strconv.Itoa(page.PageNumber) + " of " + strconv.Itoa(page.TotalPages) + "**\n\n"
out += page.Picker()
out += page.Picker(path)
return out
}
2 changes: 1 addition & 1 deletion examples/gno.land/r/demo/users/users.gno
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ func renderHome(path string) string {
doc += " * [" + user.Name + "](/r/demo/users:" + user.Name + ")\n"
}
doc += "\n"
doc += page.Picker()
doc += page.Picker(path)
return doc
}

Expand Down
4 changes: 2 additions & 2 deletions examples/gno.land/r/docs/avl_pager/avl_pager.gno
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ func Render(path string) string {
// Header and pagination info
result := "# Paginated Items\n"
result += "Page " + strconv.Itoa(page.PageNumber) + " of " + strconv.Itoa(page.TotalPages) + "\n\n"
result += page.Picker() + "\n\n"
result += page.Picker(path) + "\n\n"

// Display items on the current page
for _, item := range page.Items {
result += "- " + item.Key + ": " + item.Value.(string) + "\n"
}

result += "\n" + page.Picker() // Repeat page picker for ease of navigation
result += "\n" + page.Picker(path) // Repeat page picker for ease of navigation
return result
}
20 changes: 10 additions & 10 deletions examples/gno.land/r/docs/avl_pager/avl_pager_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import (
)

func TestRender(t *testing.T) {
// Test default Render output (first page)
output := Render("")
req := "?sort=name&order=asc"
output := Render(req)
expected := `# Paginated Items
Page 1 of 10

**1** | [2](?page=2) | [3](?page=3) | … | [10](?page=10)
**1** | [2](?page=2&sort=name&order=asc) | [3](?page=3&sort=name&order=asc) | … | [10](?page=10&sort=name&order=asc)

- Item1: Value of Item1
- Item10: Value of Item10
Expand All @@ -23,19 +23,19 @@ Page 1 of 10
- Item16: Value of Item16
- Item17: Value of Item17

**1** | [2](?page=2) | [3](?page=3) | … | [10](?page=10)`
**1** | [2](?page=2&sort=name&order=asc) | [3](?page=3&sort=name&order=asc) | … | [10](?page=10&sort=name&order=asc)`
if output != expected {
t.Errorf("Render(\"\") failed, got:\n%s", output)
t.Errorf("Render(%q) failed, got:\n%s\nwant:\n%s", req, output, expected)
}
}

func TestRender_page2(t *testing.T) {
// Test Render output for a custom page (page 2)
output := Render("?page=2&size=10")
req := "?page=2&size=10&sort=name&order=asc"
output := Render(req)
expected := `# Paginated Items
Page 2 of 10

[1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [10](?page=10)
[1](?page=1&size=10&sort=name&order=asc) | **2** | [3](?page=3&size=10&sort=name&order=asc) | [4](?page=4&size=10&sort=name&order=asc) | … | [10](?page=10&size=10&sort=name&order=asc)

- Item18: Value of Item18
- Item19: Value of Item19
Expand All @@ -48,8 +48,8 @@ Page 2 of 10
- Item25: Value of Item25
- Item26: Value of Item26

[1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [10](?page=10)`
[1](?page=1&size=10&sort=name&order=asc) | **2** | [3](?page=3&size=10&sort=name&order=asc) | [4](?page=4&size=10&sort=name&order=asc) | … | [10](?page=10&size=10&sort=name&order=asc)`
if output != expected {
t.Errorf("Render(\"\") failed, got:\n%s", output)
t.Errorf("Render(%q) failed, got:\n%s\nwant:\n%s", req, output, expected)
}
}
2 changes: 1 addition & 1 deletion examples/gno.land/r/docs/avl_pager_with_params/render.gno
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ No matter how you browse through the paginated list, the introductory text (this
}

// Insert pagination controls (previous/next links, etc.).
out += "\n" + page.Picker() + "\n\n"
out += "\n" + page.Picker(fullPath) + "\n\n"
out += "### [Go back to r/docs](/r/docs)"

return out
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/leon/config/render.gno
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func Render(path string) (out string) {
out += ufmt.Sprintf("- [%s](%s:%s)\n\n", item.Value.(Config).name, absPath, item.Key)
}

out += page.Picker()
out += page.Picker(path)
out += "\n\n"
out += "Page " + strconv.Itoa(page.PageNumber) + " of " + strconv.Itoa(page.TotalPages) + "\n\n"
}
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/leon/hof/render.gno
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (e Exhibition) Render(path string, dashboard bool) string {

out += "</div><!-- /columns-2 -->\n\n"

out += page.Picker()
out += page.Picker(path)

return out
}
Expand Down
2 changes: 1 addition & 1 deletion examples/gno.land/r/moul/present/present.gno
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func renderList(req *realmpath.Request) string {
}

out.WriteString(table.String())
out.WriteString(page.Picker()) // XXX: picker is not preserving the previous flags, should take "req" as argument.
out.WriteString(page.Picker(req.String()))
return out.String()
}

Expand Down
8 changes: 4 additions & 4 deletions examples/gno.land/r/moul/present/present_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func printRender(path string) {
// | 2024-01-15 | [title11](/r/moul/present:s11) | event1 | author2 | 4 |
// | 2024-01-10 | [title17](/r/moul/present:s17) | event3 | author2 | 4 |
// | 2024-01-05 | [title20](/r/moul/present:s20) | event3 | author4 | 4 |
// **1** | [2](?page=2)
// **1** | [2](?page=2&order=desc&sort=author)
//
// +-------------------------------
// | PATH: ?order=asc&sort=date
Expand All @@ -102,7 +102,7 @@ func printRender(path string) {
// | 2024-02-20 | [title18](/r/moul/present:s18) | event2 | author3 | 4 |
// | 2024-03-01 | [title14](/r/moul/present:s14) | event3 | author2 | 4 |
// | 2024-03-10 | [title19](/r/moul/present:s19) | event1 | author1 | 4 |
// **1** | [2](?page=2)
// **1** | [2](?page=2&order=desc&sort=author)
//
// +-------------------------------
// | PATH: ?order=asc&sort=title
Expand All @@ -120,7 +120,7 @@ func printRender(path string) {
// | 2024-01-10 | [title17](/r/moul/present:s17) | event3 | author2 | 4 |
// | 2024-02-20 | [title18](/r/moul/present:s18) | event2 | author3 | 4 |
// | 2024-03-10 | [title19](/r/moul/present:s19) | event1 | author1 | 4 |
// **1** | [2](?page=2)
// **1** | [2](?page=2&order=desc&sort=author)
//
// +-------------------------------
// | PATH: ?order=asc&sort=author
Expand Down Expand Up @@ -148,7 +148,7 @@ func printRender(path string) {
// | [Date](?order=desc&page=2&sort=date) | [Title](?order=desc&page=2&sort=title) | Event | [Author](?order=desc&page=2&sort=author) | Slides |
// | --- | --- | --- | --- | --- |
// | 2024-01-05 | [title20](/r/moul/present:s20) | event3 | author4 | 4 |
// [1](?page=1) | **2**
// [1](?page=1&order=desc&sort=author) | **2**
//
// +-------------------------------
// | PATH: s15
Expand Down
Loading