diff --git a/app/src/components/Files.js b/app/src/components/Files.js index 2b8bd23a..499ea9ab 100644 --- a/app/src/components/Files.js +++ b/app/src/components/Files.js @@ -333,9 +333,9 @@ export function FilesView( ) { const {view} = useSearchView(); - const paginator =
+ const paginator =
-
; + ; let body; if (view === 'list') { diff --git a/app/src/components/MapSearchView.js b/app/src/components/MapSearchView.js index e0ec66b8..106cd66b 100644 --- a/app/src/components/MapSearchView.js +++ b/app/src/components/MapSearchView.js @@ -124,7 +124,7 @@ export function MapSearchView() { )} -
+
; diff --git a/app/src/components/Zim.js b/app/src/components/Zim.js index b7e18f7a..83fc0316 100644 --- a/app/src/components/Zim.js +++ b/app/src/components/Zim.js @@ -223,9 +223,9 @@ const ZimAccordionItem = ({value, active, data, searchStr, activeTags}) => { } } - const paginator =
+ const paginator =
-
; +
; const label = ; diff --git a/app/src/components/ui/ui-layout.cy.js b/app/src/components/ui/ui-layout.cy.js index be7348df..13db0e9a 100644 --- a/app/src/components/ui/ui-layout.cy.js +++ b/app/src/components/ui/ui-layout.cy.js @@ -1,7 +1,7 @@ import React from 'react'; import { ActionInput, Button, Card, Header, Icon, IconButton, IconStack, Loading, MultiSelect, Panel, PathInput, - Message, Placeholder, Progress, SearchBox, Statistic, StatisticGroup, Status, TabBar, Table, + Message, Pagination, Placeholder, Progress, SearchBox, Statistic, StatisticGroup, Status, TabBar, Table, tabClassName, TextInput, } from './index'; import {MemoryRouter} from 'react-router'; @@ -2020,3 +2020,54 @@ describe('the search clear button matches the field it clears', () => { }); }); }); + +describe('pagination sits in the middle of its container', () => { + /* + * Three call sites wrap the pager in `
` or `text-align: center` and it still sat + * hard against the left edge. Both of those centre INLINE content, and Mantine's + * Pagination root is a flex container that fills its parent's width -- so the buttons + * were laid out at `flex-start` inside a box that was already the full width, and no + * amount of centring outside it could move them. + * + * Measured as the gap on each side rather than by reading `justify-content`, so the + * claim is about where the control actually lands. + */ + it('leaves the same gap either side', () => { + cy.mountUI(
+ {}}/> +
); + + cy.get('.wrolpi-pagination').should(($pager) => { + const container = $pager[0].getBoundingClientRect(); + const buttons = [...$pager[0].querySelectorAll('button')] + .map(button => button.getBoundingClientRect()); + expect(buttons.length, 'the pager rendered its controls').to.be.greaterThan(3); + + const left = Math.min(...buttons.map(b => b.left)) - container.left; + const right = container.right - Math.max(...buttons.map(b => b.right)); + // The premise: the container really is wider than the strip, so there is slack + // to distribute. Without this the equality holds trivially at 0 and 0. + expect(left + right, 'the container is wider than the strip').to.be.greaterThan(40); + expect(left, 'gap left vs gap right').to.be.closeTo(right, 1); + }); + }); + + it('does not push its first page off the left edge when space is tight', () => { + /* + * The risk centring introduces. Overflow in a `flex-start` row spills to the right, + * where it can at least be scrolled to; centred, it spills BOTH ways and the first + * page number is unreachable. 240px is narrower than any real layout gives it. + */ + cy.mountUI(
+ {}} showFirstAndLast/> +
); + + cy.get('.wrolpi-pagination').should(($pager) => { + const container = $pager[0].getBoundingClientRect(); + const buttons = [...$pager[0].querySelectorAll('button')] + .map(button => button.getBoundingClientRect()); + expect(Math.min(...buttons.map(b => b.left)), 'nothing hangs off the left') + .to.be.at.least(container.left - 0.5); + }); + }); +}); diff --git a/app/src/components/ui/ui.css b/app/src/components/ui/ui.css index d9115edd..f398c087 100644 --- a/app/src/components/ui/ui.css +++ b/app/src/components/ui/ui.css @@ -190,6 +190,28 @@ html[data-theme="amber"] .wrolpi-theme-option-active { /* ------------------------------------------------------------ Pagination */ +/* + * The pager is centred here rather than by whoever renders it. Three call sites wrapped it + * in `
` or `text-align: center` and it still sat hard against the left edge. + * + * Mantine builds a Pagination as a BLOCK root -- full width, so those wrappers had nothing + * to centre -- holding a flex Group that carries the controls and lays them out at + * `flex-start`. `justify-content` on the root therefore does nothing at all; it has to + * reach the Group. + * + * Setting `--group-justify` on the root does not work either: Group defaults that prop and + * writes the variable INLINE on its own element, where an inherited value cannot reach it. + * The property itself is fair game, though -- Mantine only ever reads the variable from a + * hashed class -- so this rule at (0,2,0) is what actually lands. + * + * The Group already wraps by default (`--group-wrap` falls back to `wrap`), which matters + * once the row is centred: centred overflow spills off BOTH edges, and the first page would + * be unreachable rather than merely off-screen to the right. + */ +.wrolpi-pagination .mantine-Group-root { + justify-content: center; +} + .wrolpi-pagination button { background: var(--panel); color: var(--text);