Files
RenewCMS/features/media_pagination_screen.feature
T

186 lines
12 KiB
Gherkin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Feature: Media page pagination
As a verified owner or admin with many images
I want the Media library split into pages with a page-size menu and shareable URLs
So that I can browse a large library quickly and come back to the same page
Background:
Given /media is the verified Media route (features/media_browse.feature), per docs/design/routes.md "Pagination in the URL (Articles, Media)" and docs/design/screens/media.md states 44-52
And the Paginator and PageSizeSelect are the existing components used by Articles (AppPaginator.vue); no new component is created
And the page requests GET /v1/media/?q=Q&filter=F&sort=S&variants=V&page=P&size=Z and reads {items, total, page, size, counts} (features/media_pagination_api.feature); q and variants are sent only when set; page and size are always sent explicitly; the tab counts come from counts and do not change while searching
And page and size live in the URL: ?page=&size=, next to the existing q, filter, sort, variants and view; the defaults (page 1, size 25) are omitted from the URL
And an invalid or missing size falls back to the stored size (features/media_page_size_memory.feature) or 25, and an invalid page (not an integer >= 1) falls back to 1, both silently replaced in the URL with history.replace and never shown as an error
And a change to search, tab, sort, view, the variants filter or the page size resets page to 1 (and drops it from the URL); search keeps its 120 ms debounce
And the URL changes when a response succeeds, not when the request is sent
And the summary reads "Showing {from}–{to} of {total}" with locale-formatted numbers, from total (the filtered count), under the grid or list
And the Paginator has First, Previous, page numbers with ellipses marking skipped ranges, Next, Last, and the page-size select "5 per page", "25 per page", "50 per page", "100 per page" (it opens upward)
And the current page uses --rc-sand fill, --rc-on-sand text and a --ui-primary border and has aria-current="page"; disabled controls are at 40 % opacity; buttons are 32 px (44 px on mobile)
And on mobile the paginator is Previous, "Page N of M" and Next
And the paginator is hidden when total fits on one page; the summary and the page-size select stay
And the paginator sits under the grid in grid view and under the table (md and up) or card list (below md) in list view; its exact placement is taken from the board frame docs/design/source/MediaFrame.dc.html, not from components.md prose
And keyboard: Tab reaches the page-size select, first, previous, each page, next, last; Enter or Space activates; ArrowDown opens the page-size list, Enter selects, Esc closes; every control shows a 2 px --ui-primary focus outline with a 2 px offset
And the Duplicates tab (?filter=duplicates) is not paginated: it loads the whole duplicate set with size=100, requesting page 1, 2, ... until the loaded count reaches total, groups it by sha256 as today, shows no paginator, summary or page-size select, and drops page and size from the URL
And "Select all" covers the images on the current page only and reads "{n} images" for that page; changing page, size, search, tab, sort or view clears the selection
And the silent background refresh after a delete, upload or merge reloads the current page with no skeleton; when the current page has become empty and is past the last page, the list steps back to the last page and replaces the URL
And the first load and a change of search, tab or sort keep the existing skeleton after 150 ms and the "Couldn't load your images" alert (media.md states 4 and 42); the page-change error below applies only when images are already shown
And the fixture below is total 312, size 25 (13 pages) unless stated, with the board's own sample data
Scenario: 44 First page
Given GET /v1/media/?filter=all&sort=newest&page=1&size=25 resolves 200 with 25 items, total 312 and counts {all 312, unused 2, duplicates 2}
When the user navigates to /media
Then the summary reads "Showing 1–25 of 312"
And First and Previous are disabled, page 1 is current, Next and Last are enabled
And the URL has no page or size
Scenario: 45 Middle page
Given GET ...&page=7&size=25 resolves 200 with 25 items and total 312
When the user navigates to /media?page=7
Then the summary reads "Showing 151–175 of 312"
And page 7 is current with --rc-sand / --rc-on-sand and aria-current="page"
And ellipses mark the skipped ranges and every control is enabled
Scenario: 46 Last page
Given GET ...&page=13&size=25 resolves 200 with 12 items and total 312
When the user navigates to /media?page=13
Then the summary reads "Showing 301–312 of 312"
And Next and Last are disabled
Scenario: Single page
Given GET /v1/media/ resolves 200 with 12 items, total 12 and counts {all 12, unused 2, duplicates 2}
When the user navigates to /media
Then the summary reads "Showing 1–12 of 12" and the page-size select stays
And the paginator (First ... Last) is not rendered
Scenario: 47 List view, desktop and mobile
Given page 2 of 13 is shown with ?view=list
Then at md width or more the paginator sits under the table with the page numbers
And below md it sits under the card list as Previous, "Page 2 of 13", Next with 44 px controls and no page numbers
And in grid view on mobile the paginator is the same Previous, "Page 2 of 13", Next
Scenario: 48 Loading next page
Given the first page has loaded and GET ...&page=2 has not resolved
When the user activates Next
Then skeleton cards keep the grid height and the list is aria-busy="true"
And every paginator control is disabled and the summary reads "Loading page 2…"
When the request resolves 200
Then the images and summary of page 2 appear and the URL becomes /media?page=2
Scenario Outline: 49 Error loading a page keeps the previous images
Given page 6 is shown
When the user activates Next and GET ...&page=7 <failure>
Then the page-6 images stay, the URL stays /media?page=6 and the controls are re-enabled
And a notice reads "Couldn't load page 7. Showing page 6." with a "Retry" action
When the user activates "Retry"
Then GET ...&page=7 is sent again and, on 200, page 7 replaces the images, the URL becomes ?page=7 and the notice goes
Examples:
| failure |
| resolves with a 500 response |
| never reaches the server |
Scenario: 50 Page out of range redirects to the last page with a notice
Given GET ...&page=99 resolves 200 with items [] and total 312
When the user navigates to /media?page=99
Then the URL is replaced (no history entry) by /media?page=13 and GET ...&page=13 is sent
And the notice reads "That page doesn't exist. Showing the last page."
And the last-page state (46) is shown
Scenario: Out of range with total 0 shows the empty state, no redirect
Given GET ...&page=5 resolves 200 with items [] and total 0 and counts.all 0
When the user navigates to /media?page=5
Then the "No images yet" empty state shows and the URL is /media
Scenario: Out of range after a search with total 0 shows no results
Given GET ...&q=zzzz&page=5 resolves 200 with items [], total 0 and counts.all 312
When the user navigates to /media?q=zzzz&page=5
Then the no-results state (media.md state 6) shows and the URL is /media?q=zzzz
Scenario Outline: Search, tab, sort, view and variants changes reset to page 1
Given the user is on /media?page=7
When the user <action> and the request resolves
Then GET ... carries page=1
And on success the URL has no page
And the tab counts are unchanged by a search
Examples:
| action |
| types "wall" in the search field and the debounce elapses |
| selects the "Unused" tab |
| picks "Largest" in the sort menu |
| activates the list icon in the grid toggle |
| applies the "With variants" filter |
Scenario Outline: 51 Page size menu
Given the user is on /media?page=3
When the user opens the page-size select (it opens upward) and chooses "<label>"
Then GET ...&page=1&size=<size> is sent and, on success, the URL is <url>
Examples:
| label | size | url |
| 5 per page | 5 | /media?size=5 |
| 25 per page | 25 | /media |
| 50 per page | 50 | /media?size=50 |
| 100 per page | 100 | /media?size=100 |
Scenario Outline: Invalid URL values fall back silently
When the user navigates to /media?<query>
Then GET ... is sent with page=1 and size=25 and the URL is replaced by /media with no history entry
And no error or notice is shown
Examples:
| query |
| size=10&page=abc |
| page=0 |
| page=-2 |
| page=1.5 |
| size=0 |
| size=200 |
Scenario: Pagination is kept across the other controls
Given the URL is /media?size=50&sort=largest&filter=unused
When the user activates Next
Then every request carries size=50, sort=largest and filter=unused and the URL keeps them
Scenario: 52 Focus-visible
When the user tabs to Next
Then Next shows a 2 px --ui-primary outline with a 2 px offset
And the same ring shows on every paginator control and on the page-size select
Scenario: Keyboard, the page-size list
When the user focuses the page-size select and presses ArrowDown
Then the list opens; Enter selects the highlighted size; Esc closes it and returns focus to the select
Scenario: Select all covers the current page only
Given page 2 of 13 is shown with 25 images
Then the "Select all" row reads "Select all" and "25 images"
When the user activates "Select all"
Then 25 images are selected and the bulk bar announces "25 selected"
When the user activates Next and page 3 loads
Then no image is selected and the bulk bar is gone
Scenario: The Duplicates tab loads the whole set and shows no paginator
Given 130 images are duplicates, GET ...&filter=duplicates&sort=newest&page=1&size=100 resolves 200 with 100 items and total 130
And GET ...&filter=duplicates&page=2&size=100 resolves 200 with the remaining 30 items
When the user navigates to /media?filter=duplicates&page=4&size=5
Then page 1 and page 2 are requested with size=100, page 2 only because 100 < 130
And the groups are built from the 130 images, with no paginator, summary or page-size select
And the URL is replaced by /media?filter=duplicates
Scenario: Leaving the Duplicates tab returns to the paginated list
Given the Duplicates tab is shown
When the user selects the "All" tab
Then GET ...&filter=all&page=1&size=25 is sent and the paginator is shown
Scenario: Deleting the last image of the last page
Given the user is on the last page with a single image and deletes it (204)
Then the list reloads the previous page in the background and the URL is replaced by it
And the tab counts are recomputed from the new response
Scenario: A background refresh keeps the current page
Given page 7 is shown and the user renames, uploads or merges
Then the list is requested again in the background for page 7 with no skeleton
Scenario: Hash lookups do not touch the page
When the detail panel or the upload duplicate check looks up copies of an image
Then GET /v1/media/?hash=H&page=1&size=100 is sent, the URL does not change and the paginator is unaffected