diff --git a/changelog/23169.txt b/changelog/23169.txt new file mode 100644 index 000000000000..4f7d266dfb6f --- /dev/null +++ b/changelog/23169.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Implement Helios Design System pagination component +``` \ No newline at end of file diff --git a/ui/app/controllers/vault/cluster/access/identity/aliases/index.js b/ui/app/controllers/vault/cluster/access/identity/aliases/index.js index 3109f51492a7..7f69a355106c 100644 --- a/ui/app/controllers/vault/cluster/access/identity/aliases/index.js +++ b/ui/app/controllers/vault/cluster/access/identity/aliases/index.js @@ -7,6 +7,15 @@ import Controller from '@ember/controller'; import ListController from 'core/mixins/list-controller'; export default Controller.extend(ListController, { + // callback from HDS pagination to set the queryParams page + get paginationQueryParams() { + return (page) => { + return { + page, + }; + }; + }, + actions: { onDelete() { this.send('reload'); diff --git a/ui/app/controllers/vault/cluster/access/identity/index.js b/ui/app/controllers/vault/cluster/access/identity/index.js index 0df094e600c3..db9da7a80abd 100644 --- a/ui/app/controllers/vault/cluster/access/identity/index.js +++ b/ui/app/controllers/vault/cluster/access/identity/index.js @@ -10,6 +10,15 @@ import ListController from 'core/mixins/list-controller'; export default Controller.extend(ListController, { flashMessages: service(), + // callback from HDS pagination to set the queryParams page + get paginationQueryParams() { + return (page) => { + return { + page, + }; + }; + }, + actions: { delete(model) { const type = model.get('identityType'); diff --git a/ui/app/controllers/vault/cluster/access/leases/list.js b/ui/app/controllers/vault/cluster/access/leases/list.js index 434674f4ba3b..fe79717ac097 100644 --- a/ui/app/controllers/vault/cluster/access/leases/list.js +++ b/ui/app/controllers/vault/cluster/access/leases/list.js @@ -14,6 +14,15 @@ export default Controller.extend(ListController, { store: service(), clusterController: controller('vault.cluster'), + // callback from HDS pagination to set the queryParams page + get paginationQueryParams() { + return (page) => { + return { + page, + }; + }; + }, + backendCrumb: computed('clusterController.model.name', function () { return { label: 'leases', diff --git a/ui/app/controllers/vault/cluster/policies/index.js b/ui/app/controllers/vault/cluster/policies/index.js index a04575cab1e7..4d41580889af 100644 --- a/ui/app/controllers/vault/cluster/policies/index.js +++ b/ui/app/controllers/vault/cluster/policies/index.js @@ -24,6 +24,15 @@ export default Controller.extend({ // set via the route `loading` action isLoading: false, + // callback from HDS pagination to set the queryParams page + get paginationQueryParams() { + return (page) => { + return { + page, + }; + }; + }, + filterMatchesKey: computed('filter', 'model', 'model.[]', function () { var filter = this.filter; var content = this.model; diff --git a/ui/app/controllers/vault/cluster/secrets/backend/list.js b/ui/app/controllers/vault/cluster/secrets/backend/list.js index c437ab09b15b..3f63bd1806ab 100644 --- a/ui/app/controllers/vault/cluster/secrets/backend/list.js +++ b/ui/app/controllers/vault/cluster/secrets/backend/list.js @@ -18,6 +18,15 @@ export default Controller.extend(ListController, BackendCrumbMixin, WithNavToNea tab: '', + // callback from HDS pagination to set the queryParams page + get paginationQueryParams() { + return (page) => { + return { + page, + }; + }; + }, + filterIsFolder: computed('filter', function () { return !!keyIsFolder(this.filter); }), diff --git a/ui/app/styles/components/list-pagination.scss b/ui/app/styles/components/list-pagination.scss deleted file mode 100644 index 5269c4cb63cd..000000000000 --- a/ui/app/styles/components/list-pagination.scss +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -// This file combines Bulma CSS with our own CSS that previously overrode Bulma. In the future we should adopt the HDS pagination. - -.pagination-previous[disabled], -.pagination-next[disabled], -.pagination-link[disabled], -.pagination-ellipsis[disabled] { - cursor: not-allowed; -} - -.pagination-previous, -.pagination-next, -.pagination-link, -.pagination-ellipsis, -.tabs { - user-select: none; -} - -.pagination-previous, -.pagination-next, -.pagination-link, -.pagination-ellipsis { - align-items: center; - box-shadow: none; - display: inline-flex; - font-size: $size-6; - justify-content: flex-start; - line-height: 1.5; - margin: $size-11; - padding-bottom: calc(0.5em - 1px); - padding-left: calc(0.75em - 1px); - padding-right: calc(0.75em - 1px); - padding-top: calc(0.5em - 1px); - position: relative; - vertical-align: top; -} - -.pagination-link.is-current { - color: $white; -} - -.pagination-list { - flex-grow: 1; - flex-shrink: 1; - justify-content: flex-start; - order: 1; -} - -.list-pagination { - @extend .has-slim-padding; - position: relative; - top: 1px; - background-color: $grey-lightest; - margin-bottom: $size-4; - - a { - text-decoration: none; - height: 1.5rem; - min-width: 1.5rem; - border: none; - } - a.pagination-link { - width: 3ch; - } - a:not(.is-current):hover { - text-decoration: underline; - color: $blue; - } - a.is-current { - background-color: $grey; - } - .pagination { - justify-content: center; - } - - .pagination-list { - flex-grow: 0; - flex-wrap: wrap; - - li { - list-style: none; - } - } - .pagination, - .pagination-list { - align-items: center; - display: flex; - justify-content: center; - text-align: center; - } - .pagination-ellipsis { - margin: 0; - padding-left: 0; - padding-right: 0; - } -} - -.list-pagination .pagination-previous, -.list-pagination .pagination-next { - @extend .button; - @extend .is-primary; - @extend .is-outlined; - @extend .is-compact; - background: $white; - border-color: $blue-500; - color: $blue-500; - max-width: 8rem; - - @include until($mobile) { - max-width: 2rem; - padding-left: 0; - padding-right: 0; - } - - .pagination-next-label, - .pagination-previous-label { - @include until($mobile) { - display: none; - } - } - - .icon { - height: 1em; - width: 1em; - vertical-align: middle; - - &:last-child:not(:first-child), - &:first-child:not(:last-child) { - margin: -0.1em 0 0; - } - } - - .button .icon { - margin: 0; - } -} - -.pagination-previous { - order: 1; -} - -.pagination-next { - order: 3; -} - -.pagination.is-centered { - &.pagination-previous { - order: 1; - } - - &.pagination-list { - justify-content: center; - order: 2; - } - - &.pagination-next { - order: 3; - } -} - -.pagination.is-right { - &.pagination-previous { - order: 1; - } - - &.pagination-next { - order: 2; - } - - &.pagination-list { - justify-content: flex-end; - order: 3; - } -} - -// responsive css -@media screen and (max-width: 768px) { - .pagination { - flex-wrap: wrap; - } - .pagination-previous, - .pagination-next { - flex-grow: 1; - flex-shrink: 1; - } - .pagination-list li { - flex-grow: 1; - flex-shrink: 1; - } -} - -.list-pagination .pagination-next { - @include until($mobile) { - order: 3; - } -} diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index 48abeb15d1a0..2d506f4f97d9 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -77,7 +77,6 @@ @import './components/license-banners'; @import './components/linked-block'; @import './components/list-item-row'; -@import './components/list-pagination'; @import './components/loader'; @import './components/login-form'; @import './components/masked-input'; diff --git a/ui/app/templates/vault/cluster/access/identity/aliases/index.hbs b/ui/app/templates/vault/cluster/access/identity/aliases/index.hbs index 7b20c761f06b..3df1212e27cf 100644 --- a/ui/app/templates/vault/cluster/access/identity/aliases/index.hbs +++ b/ui/app/templates/vault/cluster/access/identity/aliases/index.hbs @@ -36,13 +36,16 @@ {{/each}} - {{#if (gt this.model.meta.lastPage 1)}} - - {{/if}} + + + {{else}} {{/each}} - {{#if (gt this.model.meta.lastPage 1)}} - - {{/if}} + + + {{else}} {{/if}} -{{#if (gt this.model.meta.lastPage 1)}} - -{{/if}} \ No newline at end of file + + \ No newline at end of file diff --git a/ui/app/templates/vault/cluster/policies/index.hbs b/ui/app/templates/vault/cluster/policies/index.hbs index 5a2c752e49ac..e5b02ef0836b 100644 --- a/ui/app/templates/vault/cluster/policies/index.hbs +++ b/ui/app/templates/vault/cluster/policies/index.hbs @@ -151,13 +151,16 @@ {{else}} {{/each}} - {{#if (gt this.model.meta.lastPage 1)}} - - {{/if}} + + + {{else}} {{/each}} - {{#if (gt this.model.meta.lastPage 1)}} - - {{/if}} + + + {{else}} {{#if (eq this.baseKey.id "")}} {{#if (and options.firstStep (not this.tab))}} diff --git a/ui/lib/core/addon/components/list-pagination.js b/ui/lib/core/addon/components/list-pagination.js deleted file mode 100644 index 28f947f9683d..000000000000 --- a/ui/lib/core/addon/components/list-pagination.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { gt } from '@ember/object/computed'; -import Component from '@ember/component'; -import { computed } from '@ember/object'; -import { range } from 'ember-composable-helpers/helpers/range'; -import { A } from '@ember/array'; -import layout from '../templates/components/list-pagination'; - -// In non-dev mode, the pagination defaults to the config/environment variable. Set to 100. - -export default Component.extend({ - layout, - classNames: ['box', 'is-shadowless', 'list-pagination'], - page: null, - lastPage: null, - link: null, - models: A(), - // number of links to show on each side of page - spread: 2, - hasNext: computed('page', 'lastPage', function () { - return this.page < this.lastPage; - }), - hasPrevious: gt('page', 1), - - segmentLinks: gt('lastPage', 10), - - pageRange: computed('lastPage', 'page', 'spread', function () { - const { spread, page, lastPage } = this; - - let lower = Math.max(2, page - spread); - const upper = Math.min(lastPage - 1, lower + spread * 2); - // we're closer to lastPage than the spread - if (upper - lower < 5) { - lower = upper - 4; - } - if (lastPage <= 10) { - return range([1, lastPage, true]); - } - return range([lower, upper, true]); - }), -}); diff --git a/ui/lib/core/addon/components/list-view.hbs b/ui/lib/core/addon/components/list-view.hbs index cdf1596315d8..fa6006d7ed3a 100644 --- a/ui/lib/core/addon/components/list-view.hbs +++ b/ui/lib/core/addon/components/list-view.hbs @@ -10,12 +10,14 @@ {{else}} {{yield}} {{/each}} - {{#if this.showPagination}} - {{/if}} diff --git a/ui/lib/core/addon/components/list-view.js b/ui/lib/core/addon/components/list-view.js index 9a37d2603bcc..ac7ceceb3f20 100644 --- a/ui/lib/core/addon/components/list-view.js +++ b/ui/lib/core/addon/components/list-view.js @@ -40,11 +40,6 @@ export default class ListView extends Component { return this.args.itemNoun || 'item'; } - get showPagination() { - const meta = this.args.items.meta; - return this.args.paginationRouteName && meta && meta.lastPage > 1 && meta.total > 0; - } - get emptyTitle() { const items = pluralize(this.itemNoun); return `No ${items} yet`; @@ -54,4 +49,13 @@ export default class ListView extends Component { const items = pluralize(this.itemNoun); return `Your ${items} will be listed here. Add your first ${this.itemNoun} to get started.`; } + + // callback from HDS pagination to set the queryParams page + get paginationQueryParams() { + return (page) => { + return { + page, + }; + }; + } } diff --git a/ui/lib/core/addon/templates/components/list-pagination.hbs b/ui/lib/core/addon/templates/components/list-pagination.hbs deleted file mode 100644 index df0acd8f71e5..000000000000 --- a/ui/lib/core/addon/templates/components/list-pagination.hbs +++ /dev/null @@ -1,94 +0,0 @@ -{{! - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: BUSL-1.1 -~}} - - \ No newline at end of file diff --git a/ui/lib/core/app/components/list-pagination.js b/ui/lib/core/app/components/list-pagination.js deleted file mode 100644 index 80a3c70599f9..000000000000 --- a/ui/lib/core/app/components/list-pagination.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -export { default } from 'core/components/list-pagination'; diff --git a/ui/lib/kv/addon/components/page/list.js b/ui/lib/kv/addon/components/page/list.js index 8054796df8cf..8afc11cf5da1 100644 --- a/ui/lib/kv/addon/components/page/list.js +++ b/ui/lib/kv/addon/components/page/list.js @@ -42,11 +42,11 @@ export default class KvListPageComponent extends Component { return pathIsDirectory(path) ? 'View list' : 'View secret'; } - // callback from HDS pagination to set the queryParams currentPage + // callback from HDS pagination to set the queryParams page get paginationQueryParams() { return (page) => { return { - currentPage: page, + page, }; }; } diff --git a/ui/lib/kv/addon/controllers/list.js b/ui/lib/kv/addon/controllers/list.js index d1c7e3daa535..00349e2e07f8 100644 --- a/ui/lib/kv/addon/controllers/list.js +++ b/ui/lib/kv/addon/controllers/list.js @@ -6,5 +6,5 @@ import Controller from '@ember/controller'; export default class KvListController extends Controller { - queryParams = ['pageFilter', 'currentPage']; + queryParams = ['pageFilter', 'page']; } diff --git a/ui/lib/kv/addon/routes/list-directory.js b/ui/lib/kv/addon/routes/list-directory.js index 297a5e982196..4c77f44f8428 100644 --- a/ui/lib/kv/addon/routes/list-directory.js +++ b/ui/lib/kv/addon/routes/list-directory.js @@ -19,7 +19,7 @@ export default class KvSecretsListRoute extends Route { pageFilter: { refreshModel: true, }, - currentPage: { + page: { refreshModel: true, }, }; @@ -29,7 +29,7 @@ export default class KvSecretsListRoute extends Route { .lazyPaginatedQuery('kv/metadata', { backend, responsePath: 'data.keys', - page: Number(params.currentPage) || 1, + page: Number(params.page) || 1, size: Number(params.currentPageSize), pageFilter: params.pageFilter, pathToSecret, @@ -90,7 +90,7 @@ export default class KvSecretsListRoute extends Route { resetController(controller, isExiting) { if (isExiting) { controller.set('pageFilter', null); - controller.set('currentPage', null); + controller.set('page', null); } } } diff --git a/ui/lib/ldap/addon/components/page/roles.ts b/ui/lib/ldap/addon/components/page/roles.ts index 13a01e1fc5a9..9ec7b0c628c3 100644 --- a/ui/lib/ldap/addon/components/page/roles.ts +++ b/ui/lib/ldap/addon/components/page/roles.ts @@ -35,7 +35,7 @@ export default class LdapRolesPageComponent extends Component { } get paginationQueryParams() { - return (page: number) => ({ currentPage: page }); + return (page: number) => ({ page }); } @action diff --git a/ui/lib/ldap/addon/controllers/roles/index.ts b/ui/lib/ldap/addon/controllers/roles/index.ts index c4903bea59eb..a8eec50870f6 100644 --- a/ui/lib/ldap/addon/controllers/roles/index.ts +++ b/ui/lib/ldap/addon/controllers/roles/index.ts @@ -6,5 +6,5 @@ import Controller from '@ember/controller'; export default class LdapRolesController extends Controller { - queryParams = ['pageFilter', 'currentPage']; + queryParams = ['pageFilter', 'page']; } diff --git a/ui/lib/ldap/addon/routes/roles/index.ts b/ui/lib/ldap/addon/routes/roles/index.ts index 1a7596a327a3..678ccbbc2ed2 100644 --- a/ui/lib/ldap/addon/routes/roles/index.ts +++ b/ui/lib/ldap/addon/routes/roles/index.ts @@ -25,11 +25,11 @@ interface LdapRolesController extends Controller { breadcrumbs: Array; model: LdapRolesRouteModel; pageFilter: string | undefined; - currentPage: number | undefined; + page: number | undefined; } interface LdapRolesRouteParams { - currentPage?: string; + page?: string; pageFilter: string; } @@ -44,7 +44,7 @@ export default class LdapRolesRoute extends Route { pageFilter: { refreshModel: true, }, - currentPage: { + page: { refreshModel: true, }, }; @@ -58,7 +58,7 @@ export default class LdapRolesRoute extends Route { 'ldap/role', { backend: backendModel.id, - page: Number(params.currentPage) || 1, + page: Number(params.page) || 1, pageFilter: params.pageFilter, responsePath: 'data.keys', }, @@ -83,7 +83,7 @@ export default class LdapRolesRoute extends Route { resetController(controller: LdapRolesController, isExiting: boolean) { if (isExiting) { controller.set('pageFilter', undefined); - controller.set('currentPage', undefined); + controller.set('page', undefined); } } } diff --git a/ui/tests/acceptance/access/namespaces/index-test.js b/ui/tests/acceptance/access/namespaces/index-test.js index ef3f1952f633..0a64769e2606 100644 --- a/ui/tests/acceptance/access/namespaces/index-test.js +++ b/ui/tests/acceptance/access/namespaces/index-test.js @@ -35,6 +35,6 @@ module('Acceptance | Enterprise | /access/namespaces', function (hooks) { // Default page size is 15 assert.strictEqual(store.peekAll('namespace').length, 15, 'Store has 15 namespaces records'); assert.dom('.list-item-row').exists({ count: 15 }); - assert.dom('[data-test-list-view-pagination]').exists(); + assert.dom('.hds-pagination').exists(); }); });