From 1024ffd5320df446c14983dab39218646a88157c Mon Sep 17 00:00:00 2001 From: Vault Automation Date: Tue, 21 Apr 2026 12:27:32 -0400 Subject: [PATCH] UI: Fix Namespace url and content on page synchronization with pagination (#14095) (#14140) * adding handlers to keep track of current page and page size, adds them to url * pr comments Co-authored-by: Dan Rivera --- ui/app/components/page/namespaces.hbs | 9 +++++++- ui/app/components/page/namespaces.ts | 15 ++++++++++--- .../vault/cluster/access/namespaces/index.js | 11 +++++++++- .../vault/cluster/access/namespaces/index.js | 5 +++++ .../vault/cluster/access/namespaces/index.hbs | 7 +++++- ui/lib/core/addon/components/list-table.ts | 22 +++++++++++++++++-- 6 files changed, 61 insertions(+), 8 deletions(-) diff --git a/ui/app/components/page/namespaces.hbs b/ui/app/components/page/namespaces.hbs index 700fce725a..b562e1ed8b 100644 --- a/ui/app/components/page/namespaces.hbs +++ b/ui/app/components/page/namespaces.hbs @@ -82,7 +82,14 @@ {{#if @model.namespaces.length}} - + <:popupMenu as |rowData|> { @action handleSearch(evt: HTMLElementEvent) { evt.preventDefault(); - this.args.onFilterChange(this.query); + this.args.onFilterChange({ pageFilter: this.query }); } @action @@ -187,8 +190,14 @@ export default class PageNamespacesComponent extends Component { this.shouldRenderIntroModal = true; } - @action handlePageChange() { - this.args.onRefresh(); + // handles page change but keeps pageFilters if there are any + @action handlePageChange(page: number) { + this.args.onPageChange({ page: page, pageFilter: this.query, pageSize: this.args.model.pageSize }); + } + + // handles page size change but keeps any current pageFilters + @action handlePageSizeChange(pageSize: number) { + this.args.onPageChange({ page: 1, pageFilter: this.query, pageSize: pageSize }); } @action diff --git a/ui/app/controllers/vault/cluster/access/namespaces/index.js b/ui/app/controllers/vault/cluster/access/namespaces/index.js index 20ff5f9606..d6f850101f 100644 --- a/ui/app/controllers/vault/cluster/access/namespaces/index.js +++ b/ui/app/controllers/vault/cluster/access/namespaces/index.js @@ -27,7 +27,16 @@ export default class ManageNamespacesController extends Controller { @action navigate(pageFilter) { const route = 'vault.cluster.access.namespaces.index'; - const args = [route, { queryParams: { page: 1, pageFilter: pageFilter || null } }]; + const args = [ + route, + { + queryParams: { + page: pageFilter.page || 1, + pageFilter: pageFilter.pageFilter || null, + pageSize: pageFilter.pageSize || null, + }, + }, + ]; this.router.transitionTo(...args); } diff --git a/ui/app/routes/vault/cluster/access/namespaces/index.js b/ui/app/routes/vault/cluster/access/namespaces/index.js index d302f59dc2..43c9ee015d 100644 --- a/ui/app/routes/vault/cluster/access/namespaces/index.js +++ b/ui/app/routes/vault/cluster/access/namespaces/index.js @@ -23,6 +23,9 @@ export default class NamespaceListRoute extends Route { page: { refreshModel: true, }, + pageSize: { + refreshModel: true, + }, }; beforeModel() { @@ -56,6 +59,8 @@ export default class NamespaceListRoute extends Route { const { pageFilter } = params; return hash({ namespaces: this.fetchNamespaces(params), + page: Number(params?.page) || 1, + pageSize: Number(params?.pageSize) || 10, pageFilter, }); } diff --git a/ui/app/templates/vault/cluster/access/namespaces/index.hbs b/ui/app/templates/vault/cluster/access/namespaces/index.hbs index a8998b9d9d..6706e7de20 100644 --- a/ui/app/templates/vault/cluster/access/namespaces/index.hbs +++ b/ui/app/templates/vault/cluster/access/namespaces/index.hbs @@ -3,4 +3,9 @@ SPDX-License-Identifier: BUSL-1.1 }} - \ No newline at end of file + \ No newline at end of file diff --git a/ui/lib/core/addon/components/list-table.ts b/ui/lib/core/addon/components/list-table.ts index 3369ff952e..ea8b41b293 100644 --- a/ui/lib/core/addon/components/list-table.ts +++ b/ui/lib/core/addon/components/list-table.ts @@ -55,15 +55,26 @@ interface Args { data: Array; columns: TableColumn[]; selectionKeyField?: string; + page?: number; // optional page number to set current page, needed to keep pagination sync with url query param + pageSize?: number; // optional page size, needed to keep pagination sync with url query param & keep page size onSelectionChange?: OnSelectionChange; + onPageChange?: CallableFunction; + onPageSizeChange?: CallableFunction; } export default class ListTable extends Component { - @tracked currentPage = 1; - @tracked pageSize = 10; + @tracked currentPage; + @tracked pageSize; // WORKAROUND to manually re-render Hds::Pagination::Numbered to force update @currentPage @tracked renderPagination = true; + constructor(owner: unknown, args: Args) { + super(owner, args); + + this.currentPage = args.page || 1; + this.pageSize = args.pageSize || 10; + } + get paginatedTableData() { const paginated = paginate(this.args.data, { page: this.currentPage, @@ -76,8 +87,15 @@ export default class ListTable extends Component { async handlePaginationChange(action: 'currentPage' | 'pageSize', value: number) { if (action === 'pageSize') { await this.resetPagination(); + // external callback to handle page size changes and bubble up to parent component + this.args.onPageSizeChange?.(value); } this[action] = value; + + // external callback to handle current page changes and bubble up to parent component + if (action === 'currentPage') { + this.args.onPageChange?.(value); + } } @action