fix(pagination): use toLocaleString for default text formatting (#2161)

This commit is contained in:
Eric Liu 2025-04-26 13:39:26 -07:00 committed by GitHub
commit cdf5659fa0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 85 additions and 33 deletions

View file

@ -2681,25 +2681,25 @@ None.
### Props ### Props
| Prop name | Required | Kind | Reactive | Type | Default value | Description | | Prop name | Required | Kind | Reactive | Type | Default value | Description |
| :-------------------- | :------- | :--------------- | :------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | :-------------------- | :------- | :--------------- | :------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| pageSize | No | <code>let</code> | Yes | <code>number</code> | <code>10</code> | Specify the number of items to display in a page | | pageSize | No | <code>let</code> | Yes | <code>number</code> | <code>10</code> | Specify the number of items to display in a page |
| page | No | <code>let</code> | Yes | <code>number</code> | <code>1</code> | Specify the current page index | | page | No | <code>let</code> | Yes | <code>number</code> | <code>1</code> | Specify the current page index |
| totalItems | No | <code>let</code> | No | <code>number</code> | <code>0</code> | Specify the total number of items | | totalItems | No | <code>let</code> | No | <code>number</code> | <code>0</code> | Specify the total number of items |
| pageWindow | No | <code>let</code> | No | <code>number</code> | <code>1000</code> | If `totalItems` is a large number, it can affect the<br />rendering performance of this component since its value<br />is used to calculate the number of pages in the native<br />select dropdown. This value creates a small window of<br />pages rendered around the current page. By default,<br />a maximum of 1000 select items are rendered. | | pageWindow | No | <code>let</code> | No | <code>number</code> | <code>1000</code> | If `totalItems` is a large number, it can affect the<br />rendering performance of this component since its value<br />is used to calculate the number of pages in the native<br />select dropdown. This value creates a small window of<br />pages rendered around the current page. By default,<br />a maximum of 1000 select items are rendered. |
| disabled | No | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to disable the pagination | | disabled | No | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to disable the pagination |
| forwardText | No | <code>let</code> | No | <code>string</code> | <code>"Next page"</code> | Specify the forward button text | | forwardText | No | <code>let</code> | No | <code>string</code> | <code>"Next page"</code> | Specify the forward button text |
| backwardText | No | <code>let</code> | No | <code>string</code> | <code>"Previous page"</code> | Specify the backward button text | | backwardText | No | <code>let</code> | No | <code>string</code> | <code>"Previous page"</code> | Specify the backward button text |
| itemsPerPageText | No | <code>let</code> | No | <code>string</code> | <code>"Items per page:"</code> | Specify the items per page text | | itemsPerPageText | No | <code>let</code> | No | <code>string</code> | <code>"Items per page:"</code> | Specify the items per page text |
| itemText | No | <code>let</code> | No | <code>(min: number, max: number) => string</code> | <code>(min, max) => \`${min}${max} item${max === 1 ? "" : "s"}\`</code> | Override the item text | | itemText | No | <code>let</code> | No | <code>(min: number, max: number) => string</code> | <code>(min, max) => \`${min.toLocaleString()}${max.toLocaleString()} item${max === 1 ? "" : "s"}\`</code> | Override the item text |
| itemRangeText | No | <code>let</code> | No | <code>(min: number, max: number, total: number) => string</code> | <code>(min, max, total) => \`${min}${max} of ${total} item${max === 1 ? "" : "s"}\`</code> | Override the item range text | | itemRangeText | No | <code>let</code> | No | <code>(min: number, max: number, total: number) => string</code> | <code>(min, max, total) => \`${min.toLocaleString()}${max.toLocaleString()} of ${total.toLocaleString()} item${max === 1 ? "" : "s"}\`</code> | Override the item range text |
| pageInputDisabled | No | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to disable the page input | | pageInputDisabled | No | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to disable the page input |
| pageSizeInputDisabled | No | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to disable the page size input | | pageSizeInputDisabled | No | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` to disable the page size input |
| pageSizes | No | <code>let</code> | No | <code>ReadonlyArray<number></code> | <code>[10]</code> | Specify the available page sizes | | pageSizes | No | <code>let</code> | No | <code>ReadonlyArray<number></code> | <code>[10]</code> | Specify the available page sizes |
| pagesUnknown | No | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` if the number of pages is unknown | | pagesUnknown | No | <code>let</code> | No | <code>boolean</code> | <code>false</code> | Set to `true` if the number of pages is unknown |
| pageText | No | <code>let</code> | No | <code>(page: number) => string</code> | <code>(page) => \`page ${page}\`</code> | Override the page text | | pageText | No | <code>let</code> | No | <code>(page: number) => string</code> | <code>(page) => \`page ${page.toLocaleString()}\`</code> | Override the page text |
| pageRangeText | No | <code>let</code> | No | <code>(current: number, total: number) => string</code> | <code>(current, total) => \`of ${total} page${total === 1 ? "" : "s"}\`</code> | Override the page range text | | pageRangeText | No | <code>let</code> | No | <code>(current: number, total: number) => string</code> | <code>(current, total) => \`of ${total.toLocaleString()} page${total === 1 ? "" : "s"}\`</code> | Override the page range text |
| id | No | <code>let</code> | No | <code>string</code> | <code>"ccs-" + Math.random().toString(36)</code> | Set an id for the top-level element | | id | No | <code>let</code> | No | <code>string</code> | <code>"ccs-" + Math.random().toString(36)</code> | Set an id for the top-level element |
### Slots ### Slots

View file

@ -10113,7 +10113,7 @@
"kind": "let", "kind": "let",
"description": "Override the item text", "description": "Override the item text",
"type": "(min: number, max: number) => string", "type": "(min: number, max: number) => string",
"value": "(min, max) =>\n `${min}${max} item${max === 1 ? \"\" : \"s\"}`", "value": "(min, max) =>\n `${min.toLocaleString()}${max.toLocaleString()} item${max === 1 ? \"\" : \"s\"}`",
"isFunction": true, "isFunction": true,
"isFunctionDeclaration": false, "isFunctionDeclaration": false,
"isRequired": false, "isRequired": false,
@ -10125,7 +10125,7 @@
"kind": "let", "kind": "let",
"description": "Override the item range text", "description": "Override the item range text",
"type": "(min: number, max: number, total: number) => string", "type": "(min: number, max: number, total: number) => string",
"value": "(\n min,\n max,\n total,\n) =>\n `${min}${max} of ${total} item${max === 1 ? \"\" : \"s\"}`", "value": "(\n min,\n max,\n total,\n) =>\n `${min.toLocaleString()}${max.toLocaleString()} of ${total.toLocaleString()} item${max === 1 ? \"\" : \"s\"}`",
"isFunction": true, "isFunction": true,
"isFunctionDeclaration": false, "isFunctionDeclaration": false,
"isRequired": false, "isRequired": false,
@ -10197,7 +10197,7 @@
"kind": "let", "kind": "let",
"description": "Override the page text", "description": "Override the page text",
"type": "(page: number) => string", "type": "(page: number) => string",
"value": "(page) => `page ${page}`", "value": "(page) => `page ${page.toLocaleString()}`",
"isFunction": true, "isFunction": true,
"isFunctionDeclaration": false, "isFunctionDeclaration": false,
"isRequired": false, "isRequired": false,
@ -10209,7 +10209,7 @@
"kind": "let", "kind": "let",
"description": "Override the page range text", "description": "Override the page range text",
"type": "(current: number, total: number) => string", "type": "(current: number, total: number) => string",
"value": "(\n current,\n total,\n) =>\n `of ${total} page${total === 1 ? \"\" : \"s\"}`", "value": "(\n current,\n total,\n) =>\n `of ${total.toLocaleString()} page${total === 1 ? \"\" : \"s\"}`",
"isFunction": true, "isFunction": true,
"isFunctionDeclaration": false, "isFunctionDeclaration": false,
"isRequired": false, "isRequired": false,

View file

@ -39,14 +39,14 @@
* @type {(min: number, max: number) => string} * @type {(min: number, max: number) => string}
*/ */
export let itemText = (min, max) => export let itemText = (min, max) =>
`${min}${max} item${max === 1 ? "" : "s"}`; `${min.toLocaleString()}${max.toLocaleString()} item${max === 1 ? "" : "s"}`;
/** /**
* Override the item range text * Override the item range text
* @type {(min: number, max: number, total: number) => string} * @type {(min: number, max: number, total: number) => string}
*/ */
export let itemRangeText = (min, max, total) => export let itemRangeText = (min, max, total) =>
`${min}${max} of ${total} item${max === 1 ? "" : "s"}`; `${min.toLocaleString()}${max.toLocaleString()} of ${total.toLocaleString()} item${max === 1 ? "" : "s"}`;
/** Set to `true` to disable the page input */ /** Set to `true` to disable the page input */
export let pageInputDisabled = false; export let pageInputDisabled = false;
@ -70,14 +70,14 @@
* Override the page text * Override the page text
* @type {(page: number) => string} * @type {(page: number) => string}
*/ */
export let pageText = (page) => `page ${page}`; export let pageText = (page) => `page ${page.toLocaleString()}`;
/** /**
* Override the page range text * Override the page range text
* @type {(current: number, total: number) => string} * @type {(current: number, total: number) => string}
*/ */
export let pageRangeText = (current, total) => export let pageRangeText = (current, total) =>
`of ${total} page${total === 1 ? "" : "s"}`; `of ${total.toLocaleString()} page${total === 1 ? "" : "s"}`;
/** Set an id for the top-level element */ /** Set an id for the top-level element */
export let id = "ccs-" + Math.random().toString(36); export let id = "ccs-" + Math.random().toString(36);

View file

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Pagination } from "carbon-components-svelte"; import { Pagination } from "carbon-components-svelte";
import type { ComponentProps } from "svelte";
export let page = 1; export let page = 1;
export let totalItems = 0; export let totalItems = 0;
@ -10,9 +11,14 @@
export let pageInputDisabled = false; export let pageInputDisabled = false;
export let pageSizeInputDisabled = false; export let pageSizeInputDisabled = false;
export let pageSize = 10; export let pageSize = 10;
export let pageSizes: ReadonlyArray<number> = [10]; export let pageSizes: ComponentProps<Pagination>["pageSizes"] = [10];
export let pageWindow: undefined | number = undefined; export let pageWindow: ComponentProps<Pagination>["pageWindow"] = undefined;
export let pagesUnknown = false; export let pagesUnknown = false;
export let pageText: ComponentProps<Pagination>["pageText"] = undefined;
export let pageRangeText: ComponentProps<Pagination>["pageRangeText"] =
undefined;
export let itemRangeText: ComponentProps<Pagination>["itemRangeText"] =
undefined;
</script> </script>
<Pagination <Pagination
@ -28,6 +34,9 @@
bind:pageSize bind:pageSize
{pageSizes} {pageSizes}
{pagesUnknown} {pagesUnknown}
{pageText}
{pageRangeText}
{itemRangeText}
on:change={(e) => { on:change={(e) => {
console.log("change", e.detail); console.log("change", e.detail);
}} }}

View file

@ -214,4 +214,47 @@ describe("Pagination", () => {
const pageNumbers = screen.getByLabelText(/Page number, of 10000 pages/); const pageNumbers = screen.getByLabelText(/Page number, of 10000 pages/);
expect(pageNumbers).toHaveLength(100 + 1); expect(pageNumbers).toHaveLength(100 + 1);
}); });
it("formats larger numbers using `toLocaleString`", () => {
render(Pagination, {
props: { totalItems: 100_000 },
});
expect(screen.getByText(/110 of 100,000 items/)).toBeInTheDocument();
expect(screen.getByText(/of 10,000 pages/)).toBeInTheDocument();
});
it("handles custom page text", () => {
render(Pagination, {
props: {
pagesUnknown: true,
totalItems: 100_000,
pageText: (page) => `Current page ${page}`,
},
});
expect(screen.getByText(/Current page 1/)).toBeInTheDocument();
});
it("handles custom page range text", () => {
render(Pagination, {
props: {
totalItems: 100_000,
pageRangeText: (current, total) => `${current} of ${total}`,
},
});
expect(screen.getByText(/1 of 10000/)).toBeInTheDocument();
});
it("handles custom item range text", () => {
render(Pagination, {
props: {
totalItems: 100_000,
itemRangeText: (min, max, total) => `${min}${max} of ${total}`,
},
});
expect(screen.getByText(/110 of 100000/)).toBeInTheDocument();
});
}); });

View file

@ -53,13 +53,13 @@ type $Props = {
/** /**
* Override the item text * Override the item text
* @default (min, max) => `${min}${max} item${max === 1 ? "" : "s"}` * @default (min, max) => `${min.toLocaleString()}${max.toLocaleString()} item${max === 1 ? "" : "s"}`
*/ */
itemText?: (min: number, max: number) => string; itemText?: (min: number, max: number) => string;
/** /**
* Override the item range text * Override the item range text
* @default (min, max, total) => `${min}${max} of ${total} item${max === 1 ? "" : "s"}` * @default (min, max, total) => `${min.toLocaleString()}${max.toLocaleString()} of ${total.toLocaleString()} item${max === 1 ? "" : "s"}`
*/ */
itemRangeText?: (min: number, max: number, total: number) => string; itemRangeText?: (min: number, max: number, total: number) => string;
@ -95,13 +95,13 @@ type $Props = {
/** /**
* Override the page text * Override the page text
* @default (page) => `page ${page}` * @default (page) => `page ${page.toLocaleString()}`
*/ */
pageText?: (page: number) => string; pageText?: (page: number) => string;
/** /**
* Override the page range text * Override the page range text
* @default (current, total) => `of ${total} page${total === 1 ? "" : "s"}` * @default (current, total) => `of ${total.toLocaleString()} page${total === 1 ? "" : "s"}`
*/ */
pageRangeText?: (current: number, total: number) => string; pageRangeText?: (current: number, total: number) => string;