Skip to content

Commit

Permalink
LWS-313: Yet another pagination bugfix (#1057)
Browse files Browse the repository at this point in the history
* LWS-313: Pagination predicate bug
* Fix sequence reactivity issue
* Fix failing pagination test
* update changelog
  • Loading branch information
jesperengstrom authored Jun 12, 2024
1 parent 7100b96 commit 7bd6e80
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 53 deletions.
132 changes: 80 additions & 52 deletions lxl-web/src/lib/components/find/Pagination.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,93 +5,121 @@
import BiChevronLeft from '~icons/bi/chevron-left';
export let data: SearchResult;
$: ({ first, last, next, totalItems, itemsPerPage, itemOffset, maxItems } = data);
$: ({ first, last, next, previous, totalItems, itemsPerPage, itemOffset, maxItems } = data);
$: showPagination = data.items.length > 0 && totalItems > itemsPerPage;
$: currentPage = Math.floor(itemOffset / itemsPerPage) + 1;
$: isFirstPage = currentPage === 1;
$: lastItem = totalItems > maxItems ? maxItems : totalItems;
$: lastPage = Math.ceil(lastItem / itemsPerPage);
$: isLastPage = currentPage === lastPage;
// How many pages to display in a sequence (excl first & last)
let sequenceSize = 3;
const numberOfPages = 7;
$: sequenceSize = numberOfPages > lastPage ? lastPage : numberOfPages;
$: if (sequenceSize > lastPage) {
sequenceSize = lastPage;
}
$: sequenceStart = (() => {
if (currentPage + (sequenceSize - 1) >= lastPage) {
return lastPage - (sequenceSize - 1);
} else {
return currentPage;
$: pageSequence = (() => {
let pages = [];
let halfSequence = Math.floor(sequenceSize / 2);
// exclude first & last pages from sequence
let sequenceStart = currentPage - halfSequence < 2 ? 2 : currentPage - halfSequence;
let sequenceEnd =
currentPage + halfSequence > lastPage - 1 ? lastPage - 1 : currentPage + halfSequence;
if (sequenceStart > sequenceEnd) {
sequenceEnd = sequenceStart;
}
// add remaining pages to beginning or end
const remainder = sequenceSize - (sequenceEnd - sequenceStart + 1);
if (remainder && sequenceStart > 2) {
sequenceStart = sequenceStart - remainder < 2 ? 2 : sequenceStart - remainder;
}
if (remainder && sequenceEnd < lastPage - 1) {
sequenceEnd = sequenceEnd + remainder > lastPage - 1 ? lastPage - 1 : sequenceEnd + remainder;
}
for (let i = sequenceStart; i <= sequenceEnd; i++) {
pages.push({ page: i, link: getOffsetLink(itemsPerPage * (i - 1)) });
}
return pages;
})();
$: pageSequence = [...Array(sequenceSize)].map((el, i) => sequenceStart + i);
$: sequenceEnd = pageSequence[pageSequence.length - 1];
function getOffsetLink(offset: number) {
let o = offset < 0 ? 0 : offset;
const params = new URLSearchParams($page.url.searchParams.toString());
params.set('_offset', o.toString());
return `${$page.url.pathname}?${params.toString()}`;
return `${first['@id']}&_offset=${o}`;
}
</script>

{#if data.items.length > 0 && totalItems > itemsPerPage}
<nav aria-label="paginering" data-testid="pagination">
<ul class="my-4 flex justify-center gap-2">
<!-- prev and first -->
{#if !isFirstPage || itemOffset > 0}
{#if showPagination}
<nav aria-label={$page.data.t('search.pagination')} data-testid="pagination">
<ul class="flex justify-center overflow-hidden page-padding">
<!-- prev -->
{#if previous}
<li>
<a
class="button-ghost"
href={getOffsetLink(itemOffset - itemsPerPage)}
href={previous['@id']}
aria-label={$page.data.t('search.previous')}
><BiChevronLeft aria-hidden="true" class="text-icon" /></a
>
</li>
{#if sequenceStart > 1}
<li>
<a aria-label="{$page.data.t('search.page')} 1" class="button-ghost" href={first['@id']}
>1</a
>
</li>
{/if}
{/if}
{#if sequenceStart > 2}
<li class="flex items-end text-3-cond-bold"><span>...</span></li>
<!-- first -->
<li>
<a
aria-label="{$page.data.t('search.page')} 1"
class={currentPage === 1 ? 'button-primary' : 'button-ghost'}
href={first['@id']}>1</a
>
</li>
{#if pageSequence[0].page > 2}
<li class="hidden items-end text-3-cond-bold sm:flex"><span>...</span></li>
{/if}
<!-- page sequence -->
{#each pageSequence as p}
<li>
<a
class={p === currentPage ? 'button-primary' : 'button-ghost hidden sm:flex'}
href={getOffsetLink(itemsPerPage * (p - 1))}
aria-label="{$page.data.t('search.page')} {p}"
aria-current={p === currentPage ? 'page' : null}
>{p.toLocaleString($page.data.locale)}</a
>
</li>
{/each}
{#if lastPage - sequenceEnd > 1}
<li class="flex items-end text-3-cond-bold"><span>...</span></li>
{/if}
<!-- last and next -->
{#if !isLastPage}
{#if sequenceEnd !== lastPage}
{#if p.page !== 1 && p.page !== lastPage}
<li>
<a
aria-label="{$page.data.t('search.page')} {lastPage}"
class="button-ghost"
href={last['@id']}>{lastPage.toLocaleString($page.data.locale)}</a
class={p.page === currentPage
? 'button-primary !mx-4 sm:!mx-0.5'
: 'button-ghost hidden sm:flex'}
href={p.link}
aria-label="{$page.data.t('search.page')} {p}"
aria-current={p.page === currentPage ? 'page' : null}
>{p.page.toLocaleString($page.data.locale)}</a
>
</li>
{/if}
{/each}
{#if lastPage - pageSequence[pageSequence.length - 1].page > 1}
<li class="hidden items-end text-3-cond-bold sm:flex"><span>...</span></li>
{/if}
<!-- last -->
<li>
<a
aria-label="{$page.data.t('search.page')} {lastPage}"
class={currentPage === lastPage ? 'button-primary' : 'button-ghost'}
href={last['@id']}>{lastPage.toLocaleString($page.data.locale)}</a
>
</li>
<!-- next -->
{#if next}
<li>
<a class="button-ghost" href={next?.['@id']} aria-label={$page.data.t('search.next')}
<a class="button-ghost" href={next['@id']} aria-label={$page.data.t('search.next')}
><BiChevronRight aria-hidden="true" class="text-icon" /></a
>
</li>
{/if}
</ul>
</nav>
{/if}

<style lang="postcss">
nav li > * {
@apply mx-0.5;
}
nav li > a {
@apply min-h-11 min-w-11 !px-2;
}
</style>
1 change: 1 addition & 0 deletions lxl-web/src/lib/i18n/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export default {
type: 'Type',
related: 'related',
relatedOne: 'related',
pagination: 'pagination',
previous: 'Previous page',
page: 'Page',
next: 'Next page',
Expand Down
1 change: 1 addition & 0 deletions lxl-web/src/lib/i18n/locales/sv.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export default {
type: 'Typ',
related: 'relaterade',
relatedOne: 'relaterad',
pagination: 'paginering',
previous: 'Föregående sida',
page: 'Sida',
next: 'Nästa sida',
Expand Down
1 change: 1 addition & 0 deletions lxl-web/src/lib/types/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface SearchResult {
first: Link;
last: Link;
next?: Link;
previous?: Link;
items: SearchResultItem[];
facetGroups: FacetGroup[];
predicates: MultiSelectFacet[];
Expand Down
1 change: 1 addition & 0 deletions lxl-web/src/lib/utils/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export async function asResult(
const translate = await getTranslator(locale);
return {
...('next' in view && { next: replacePath(view.next as Link, usePath) }),
...('previous' in view && { previous: replacePath(view.previous as Link, usePath) }),
itemOffset: view.itemOffset,
itemsPerPage: view.itemsPerPage,
totalItems: view.totalItems,
Expand Down
1 change: 1 addition & 0 deletions lxl-web/src/routes/(app)/[[lang=lang]]/help/en.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Here we will continuously provide information about newly added features and pla

- Improve help text
- Selectable text in search results
- Improved pagination

### 2024-05-29

Expand Down
1 change: 1 addition & 0 deletions lxl-web/src/routes/(app)/[[lang=lang]]/help/sv.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Här kommer vi kontinuerligt berätta om nytillkomna funktioner och planerad utv

- Förbättra hjälptext
- Markerbar text i sökträffarna
- Förbättrad paginering

### 2024-05-29

Expand Down
2 changes: 1 addition & 1 deletion lxl-web/tests/find.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,5 @@ test('can paginate to next and previous', async ({ page }) => {
await page.getByTestId('pagination').getByLabel('Nästa sida').click();
await expect(page).toHaveURL(/_offset=10/);
await page.getByTestId('pagination').getByLabel('Föregående sida').click();
await expect(page).toHaveURL(/_offset=0/);
await expect(page).not.toHaveURL(/_offset=/);
});

0 comments on commit 7bd6e80

Please sign in to comment.