Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ab579b4
feat: implement pagination - jump to page
Jan 5, 2026
09ecdf6
feat: add input inline label and popover controlled visibility
Jan 5, 2026
7109f94
fix: update demo pages to use ref-based error handling
Jan 6, 2026
b22f6c5
fix: Fixes drag handle UAP buttons to never render outside viewport (…
pan-kot Jan 6, 2026
94ac26c
chore: switch build-tools to transition branch (#4138)
amanabiy Jan 6, 2026
6dbd763
chore: Bump qs and express (#4145)
dependabot[bot] Jan 7, 2026
3950362
fix: wrap scrollToIndex in requestAnimationFrame (#4134)
mxschll Jan 7, 2026
1bfe630
chore: update build-tools dependency to use main branch (#4162)
amanabiy Jan 7, 2026
90a4fea
fix: Fixes UAP buttons forced padding (#4171)
pan-kot Jan 7, 2026
def84b9
fix: Fixes treeview offset for better items alignment (#4163)
pan-kot Jan 8, 2026
a1881da
feat: add post-copy event handlers to CopyToClipboard (#4040) (#4118)
TrevorBurnham Jan 8, 2026
99cac32
fix: Fix sticky state listener logic to listen size change (#4174)
NathanZlion Jan 8, 2026
c44ab5e
chore: modify snapshot update instructions (#4176)
mxschll Jan 8, 2026
55b4ef0
fix: Restore focus to tutorial panel on dismiss (#4167)
NathanZlion Jan 8, 2026
e04be93
chore: Add extra dev page to test popover trigger focus ring (#4175)
jperals Jan 9, 2026
7fd9e6f
feat: add pagination - jump to page, update i18n, snapshots
Jan 10, 2026
83c5e3d
Merge branch 'main' into new-jump-to-page-branch
ngynmt Jan 10, 2026
88e81fb
Merge branch 'main' into new-jump-to-page-branch
gethinwebster Jan 14, 2026
96ecbbc
Merge branch 'main' into new-jump-to-page-branch
ngynmt Jan 14, 2026
3c6aca2
Remove unrelated i18n changes
Jan 15, 2026
58e9033
Remove unrelated i18n changes
Jan 15, 2026
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
7 changes: 5 additions & 2 deletions pages/pagination/permutations.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
import React from 'react';

import I18nProvider from '~components/i18n';
import messages from '~components/i18n/messages/all.en';
import Pagination, { PaginationProps } from '~components/pagination';

import createPermutations from '../utils/permutations';
Expand All @@ -26,16 +28,17 @@ const permutations = createPermutations<PaginationProps>([
pagesCount: [15],
openEnd: [true, false],
ariaLabels: [paginationLabels],
jumpToPage: [undefined, { loading: false }, { loading: true }],
},
]);

export default function PaginationPermutations() {
return (
<>
<I18nProvider messages={[messages]} locale="en">
<h1>Pagination permutations</h1>
<ScreenshotArea>
<PermutationsView permutations={permutations} render={permutation => <Pagination {...permutation} />} />
</ScreenshotArea>
</>
</I18nProvider>
);
}
109 changes: 109 additions & 0 deletions pages/table/jump-to-page-closed.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useState } from 'react';

import { CollectionPreferences } from '~components';
import I18nProvider from '~components/i18n';
import messages from '~components/i18n/messages/all.en';
import Pagination from '~components/pagination';
import Table from '~components/table';

import { generateItems, Instance } from './generate-data';

const allItems = generateItems(100);
const PAGE_SIZE = 10;

function JumpToPageClosedContent() {
const [currentPageIndex, setCurrentPageIndex] = useState(1);

const totalPages = Math.ceil(allItems.length / PAGE_SIZE);
const startIndex = (currentPageIndex - 1) * PAGE_SIZE;
const endIndex = startIndex + PAGE_SIZE;
const currentItems = allItems.slice(startIndex, endIndex);

return (
<Table
header={<h1>Jump to Page - Closed Pagination (100 items, 10 pages)</h1>}
columnDefinitions={[
{ header: 'ID', cell: (item: Instance) => item.id },
{ header: 'State', cell: (item: Instance) => item.state },
{ header: 'Type', cell: (item: Instance) => item.type },
{ header: 'DNS Name', cell: (item: Instance) => item.dnsName || '-' },
]}
preferences={
<CollectionPreferences
title="Preferences"
confirmLabel="Confirm"
cancelLabel="Cancel"
preferences={{
pageSize: 10,
contentDisplay: [
{ id: 'variable', visible: true },
{ id: 'value', visible: true },
{ id: 'type', visible: true },
{ id: 'description', visible: true },
],
}}
pageSizePreference={{
title: 'Page size',
options: [
{ value: 10, label: '10 resources' },
{ value: 20, label: '20 resources' },
],
}}
wrapLinesPreference={{}}
stripedRowsPreference={{}}
contentDensityPreference={{}}
contentDisplayPreference={{
options: [
{
id: 'variable',
label: 'Variable name',
alwaysVisible: true,
},
{ id: 'value', label: 'Text value' },
{ id: 'type', label: 'Type' },
{ id: 'description', label: 'Description' },
],
}}
stickyColumnsPreference={{
firstColumns: {
title: 'Stick first column(s)',
description: 'Keep the first column(s) visible while horizontally scrolling the table content.',
options: [
{ label: 'None', value: 0 },
{ label: 'First column', value: 1 },
{ label: 'First two columns', value: 2 },
],
},
lastColumns: {
title: 'Stick last column',
description: 'Keep the last column visible while horizontally scrolling the table content.',
options: [
{ label: 'None', value: 0 },
{ label: 'Last column', value: 1 },
],
},
}}
/>
}
items={currentItems}
pagination={
<Pagination
currentPageIndex={currentPageIndex}
pagesCount={totalPages}
onChange={({ detail }) => setCurrentPageIndex(detail.currentPageIndex)}
jumpToPage={{}}
/>
}
/>
);
}

export default function JumpToPageClosedExample() {
return (
<I18nProvider messages={[messages]} locale="en">
<JumpToPageClosedContent />
</I18nProvider>
);
}
149 changes: 149 additions & 0 deletions pages/table/jump-to-page-open-end.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useRef, useState } from 'react';

import I18nProvider from '~components/i18n';
import messages from '~components/i18n/messages/all.en';
import Pagination, { PaginationProps } from '~components/pagination';
import Table from '~components/table';

import { generateItems, Instance } from './generate-data';

const PAGE_SIZE = 10;
const TOTAL_ITEMS = 100; // Simulated server-side total

function JumpToPageOpenEndContent() {
const [currentPageIndex, setCurrentPageIndex] = useState(1);
const [loadedPages, setLoadedPages] = useState<Record<number, Instance[]>>({ 1: generateItems(10) });
const [jumpToPageIsLoading, setJumpToPageIsLoading] = useState(false);
const [maxKnownPage, setMaxKnownPage] = useState(1);
const [openEnd, setOpenEnd] = useState(true);
const jumpToPageRef = useRef<PaginationProps.Ref>(null);

const currentItems = loadedPages[currentPageIndex] || [];

const loadPage = (pageIndex: number) => {
return new Promise<Instance[]>((resolve, reject) => {
setTimeout(() => {
const totalPages = Math.ceil(TOTAL_ITEMS / PAGE_SIZE);
if (pageIndex > totalPages) {
reject({
message: `Page ${pageIndex} does not exist. Maximum page is ${totalPages}.`,
maxPage: totalPages,
});
} else {
const startIndex = (pageIndex - 1) * PAGE_SIZE;
resolve(generateItems(10).map((item, i) => ({ ...item, id: `${startIndex + i + 1}` })));
}
}, 500);
});
};

return (
<Table
header={
<div>
<h1>Jump to Page - Open End Pagination (100 items total, lazy loaded)</h1>
<p>
Current: Page {currentPageIndex}, Max Known: {maxKnownPage}, Mode: {openEnd ? 'Open-End' : 'Closed'}
</p>
</div>
}
columnDefinitions={[
{ header: 'ID', cell: (item: Instance) => item.id },
{ header: 'State', cell: (item: Instance) => item.state },
{ header: 'Type', cell: (item: Instance) => item.type },
{ header: 'DNS Name', cell: (item: Instance) => item.dnsName || '-' },
]}
items={currentItems}
pagination={
<Pagination
ref={jumpToPageRef}
currentPageIndex={currentPageIndex}
pagesCount={maxKnownPage}
openEnd={openEnd}
onChange={({ detail }) => {
const requestedPage = detail.currentPageIndex;
// If page already loaded, just navigate
if (loadedPages[requestedPage]) {
setCurrentPageIndex(requestedPage);
return;
}
// Otherwise, load the page
setJumpToPageIsLoading(true);
loadPage(requestedPage)
.then(items => {
setLoadedPages(prev => ({ ...prev, [requestedPage]: items }));
setCurrentPageIndex(requestedPage);
setMaxKnownPage(Math.max(maxKnownPage, requestedPage));
setJumpToPageIsLoading(false);
})
.catch((error: { message: string; maxPage?: number }) => {
const newMaxPage = error.maxPage || maxKnownPage;
setMaxKnownPage(newMaxPage);
setOpenEnd(false);
jumpToPageRef.current?.setError(true);
// Load all pages from current to max
const pagesToLoad = [];
for (let i = 1; i <= newMaxPage; i++) {
if (!loadedPages[i]) {
pagesToLoad.push(loadPage(i).then(items => ({ page: i, items })));
}
}

Promise.all(pagesToLoad).then(results => {
setLoadedPages(prev => {
const updated = { ...prev };
results.forEach(({ page, items }) => {
updated[page] = items;
});
return updated;
});
setCurrentPageIndex(newMaxPage);
setJumpToPageIsLoading(false);
});
});
}}
onNextPageClick={({ detail }) => {
// If page already loaded, just navigate
if (loadedPages[detail.requestedPageIndex]) {
setCurrentPageIndex(detail.requestedPageIndex);
return;
}
// Load the next page
setJumpToPageIsLoading(true);
loadPage(detail.requestedPageIndex)
.then(items => {
setLoadedPages(prev => ({ ...prev, [detail.requestedPageIndex]: items }));
setCurrentPageIndex(detail.requestedPageIndex);
setMaxKnownPage(Math.max(maxKnownPage, detail.requestedPageIndex));
setJumpToPageIsLoading(false);
})
.catch((error: { message: string; maxPage?: number }) => {
// Discovered the end - switch to closed pagination and stay on current page
if (error.maxPage) {
setMaxKnownPage(error.maxPage);
setOpenEnd(false);
}
// Reset to current page (undo the navigation that already happened)
setCurrentPageIndex(currentPageIndex);
jumpToPageRef.current?.setError(true);
setJumpToPageIsLoading(false);
});
}}
jumpToPage={{
loading: jumpToPageIsLoading,
}}
/>
}
/>
);
}

export default function JumpToPageOpenEndExample() {
return (
<I18nProvider messages={[messages]} locale="en">
<JumpToPageOpenEndContent />
</I18nProvider>
);
}
Loading
Loading