Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/src/pages/components/Pagination.svx
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ For example, with a total of 9 items and page sizes of `[5, 10, 15]`, the page s

## Unknown pages

Set `pagesUnknown` to `true` when the total number of pages is unknown. This renders the item range text without factoring in the total number of pages.
Set `pagesUnknown` to `true` when the total number of pages is unknown (for example, paging through a cursor-based API). The item range text is rendered without a total, and the forward button is not constrained by `totalItems`.

<Pagination pagesUnknown />
Because the component cannot know when the last page is reached, the consumer is responsible for the page bounds. Bind to `page` and toggle `forwardButtonDisabled` when there is no more data to load.

<FileSource src="/framed/Pagination/PaginationUnknown" />

## Page window

Expand Down
13 changes: 13 additions & 0 deletions docs/src/pages/framed/Pagination/PaginationUnknown.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script>
import { Pagination } from "carbon-components-svelte";

// Simulate a cursor-based API where the total is unknown.
const LAST_PAGE = 5;

let page = 1;
let pageSize = 10;

$: forwardButtonDisabled = page >= LAST_PAGE;
</script>

<Pagination pagesUnknown bind:page bind:pageSize {forwardButtonDisabled} />
33 changes: 26 additions & 7 deletions src/Pagination/Pagination.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,22 @@
/** Set to `true` if the number of pages is unknown */
export let pagesUnknown = false;

/**
* Override the disabled state of the forward (next page) button.
* Intended for use with `pagesUnknown` (controlled), where the consumer
* knows when there is no more data to load.
* @type {boolean | undefined}
*/
export let forwardButtonDisabled = undefined;

/**
* Override the disabled state of the backward (previous page) button.
* Intended for use with `pagesUnknown` (controlled), where the consumer
* manages page bounds.
* @type {boolean | undefined}
*/
export let backButtonDisabled = undefined;

/**
* Override the page text.
* @type {(page: number) => string}
Expand Down Expand Up @@ -150,7 +166,7 @@
}

$: totalPages = Math.max(Math.ceil(totalItems / pageSize), 1);
$: if (page > totalPages) page = totalPages;
$: if (!pagesUnknown && page > totalPages) page = totalPages;
$: if (prevPage !== page || prevPageSize !== pageSize) {
dispatch("update", { pageSize, page });
prevPage = page;
Expand All @@ -160,8 +176,11 @@
$: effectivePageSizes = dynamicPageSizes
? getFilteredPageSizes(pageSizes, totalItems)
: pageSizes;
$: backButtonDisabled = disabled || page === 1;
$: forwardButtonDisabled = disabled || page === totalPages;
$: internalBackButtonDisabled =
backButtonDisabled ?? (disabled || page === 1);
$: internalForwardButtonDisabled =
forwardButtonDisabled ??
(disabled || (!pagesUnknown && page === totalPages));
</script>

<div {id} class:bx--pagination={true} {...$$restProps}>
Expand Down Expand Up @@ -233,8 +252,8 @@
tooltipPosition="top"
icon={CaretLeft}
iconDescription={backwardText}
disabled={backButtonDisabled}
class="bx--pagination__button bx--pagination__button--backward {backButtonDisabled
disabled={internalBackButtonDisabled}
class="bx--pagination__button bx--pagination__button--backward {internalBackButtonDisabled
? 'bx--pagination__button--no-index'
: ''}"
on:click={() => {
Expand All @@ -249,8 +268,8 @@
tooltipPosition="top"
icon={CaretRight}
iconDescription={forwardText}
disabled={forwardButtonDisabled}
class="bx--pagination__button bx--pagination__button--forward {forwardButtonDisabled
disabled={internalForwardButtonDisabled}
class="bx--pagination__button bx--pagination__button--forward {internalForwardButtonDisabled
? 'bx--pagination__button--no-index'
: ''}"
on:click={() => {
Expand Down
6 changes: 6 additions & 0 deletions tests/Pagination/Pagination.test.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
undefined;
export let id: ComponentProps<Pagination>["id"] = undefined;
export let customClass = "";
export let forwardButtonDisabled: ComponentProps<Pagination>["forwardButtonDisabled"] =
undefined;
export let backButtonDisabled: ComponentProps<Pagination>["backButtonDisabled"] =
undefined;
</script>

<Pagination
Expand All @@ -42,6 +46,8 @@
{pageRangeText}
{itemRangeText}
{id}
{forwardButtonDisabled}
{backButtonDisabled}
class={customClass}
on:change={(e) => {
console.log("change", e.detail);
Expand Down
53 changes: 53 additions & 0 deletions tests/Pagination/Pagination.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,59 @@ describe("Pagination", () => {
expect(screen.getByText("page 1")).toBeInTheDocument();
});

// When `pagesUnknown` is true, the total number of pages is unknown, so the
// forward button should remain enabled at page 1 — otherwise the user can
// never advance past the first page.
it("should not disable the forward button at page 1 when pagesUnknown is true", () => {
render(Pagination, {
props: { pagesUnknown: true, page: 1 },
});

const nextButton = screen.getByRole("button", { name: "Next page" });
const prevButton = screen.getByRole("button", { name: "Previous page" });

expect(nextButton).not.toBeDisabled();
expect(prevButton).toBeDisabled();
});

it("should advance the page when pagesUnknown is true", async () => {
const consoleLog = vi.spyOn(console, "log");
render(Pagination, {
props: { pagesUnknown: true, page: 1 },
});

await user.click(screen.getByRole("button", { name: "Next page" }));

expect(consoleLog).toHaveBeenCalledWith("next", { page: 2 });
expect(screen.getByText("page 2")).toBeInTheDocument();
});

it("should allow overriding forwardButtonDisabled", () => {
render(Pagination, {
props: {
pagesUnknown: true,
page: 1,
forwardButtonDisabled: true,
},
});

expect(screen.getByRole("button", { name: "Next page" })).toBeDisabled();
});

it("should allow overriding backButtonDisabled", () => {
render(Pagination, {
props: {
totalItems: 102,
page: 2,
backButtonDisabled: true,
},
});

expect(
screen.getByRole("button", { name: "Previous page" }),
).toBeDisabled();
});

it("should update when page or pageSize changes", async () => {
const consoleLog = vi.spyOn(console, "log");
render(Pagination, {
Expand Down
Loading