UI: HDS adoption replace <ListPagination> component (#23169)

* change currentPage to page to be consistent

* replace pagination in listview and always show pagination

* wip

* fix query param issue

* access identity aliases index

* leases done and dusted

* policies and secrets backend

* remove list Pagination

* changelog
This commit is contained in:
Angel Garbarino
2023-09-26 10:27:14 -06:00
committed by GitHub
parent 88ed074287
commit 6db476da41
25 changed files with 128 additions and 408 deletions

3
changelog/23169.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:improvement
ui: Implement Helios Design System pagination component
```

View File

@@ -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');

View File

@@ -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');

View File

@@ -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',

View File

@@ -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;

View File

@@ -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);
}),

View File

@@ -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;
}
}

View File

@@ -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';

View File

@@ -36,13 +36,16 @@
</div>
</LinkedBlock>
{{/each}}
{{#if (gt this.model.meta.lastPage 1)}}
<ListPagination
@page={{this.model.meta.currentPage}}
@lastPage={{this.model.meta.lastPage}}
@link="vault.cluster.access.identity.aliases.index"
/>
{{/if}}
<Hds::Pagination::Numbered
@currentPage={{this.model.meta.currentPage}}
@currentPageSize={{this.model.meta.pageSize}}
@route="vault.cluster.access.identity.aliases.index"
@showSizeSelector={{false}}
@totalItems={{this.model.meta.total}}
@queryFunction={{this.paginationQueryParams}}
/>
{{else}}
<EmptyState
@title="No {{this.identityType}} aliases yet"

View File

@@ -96,13 +96,16 @@
</div>
</LinkedBlock>
{{/each}}
{{#if (gt this.model.meta.lastPage 1)}}
<ListPagination
@page={{this.model.meta.currentPage}}
@lastPage={{this.model.meta.lastPage}}
@link="vault.cluster.access.identity.index"
/>
{{/if}}
<Hds::Pagination::Numbered
@currentPage={{this.model.meta.currentPage}}
@currentPageSize={{this.model.meta.pageSize}}
@route="vault.cluster.access.identity.index"
@showSizeSelector={{false}}
@totalItems={{this.model.meta.total}}
@queryFunction={{this.paginationQueryParams}}
/>
{{else}}
<EmptyState
@title="No {{pluralize this.identityType}} yet"

View File

@@ -102,11 +102,12 @@
{{else}}
<EmptyState @title={{this.emptyTitle}} />
{{/if}}
{{#if (gt this.model.meta.lastPage 1)}}
<ListPagination
@page={{this.model.meta.currentPage}}
@lastPage={{this.model.meta.lastPage}}
@link={{concat "vault.cluster.access.leases.list" (unless this.baseKey.id "-root")}}
@models={{compact (array (if this.baseKey.id this.baseKey.id))}}
/>
{{/if}}
<Hds::Pagination::Numbered
@currentPage={{this.model.meta.currentPage}}
@currentPageSize={{this.model.meta.pageSize}}
@route={{concat "vault.cluster.access.leases.list" (unless this.baseKey.id "-root")}}
@showSizeSelector={{false}}
@totalItems={{this.model.meta.total}}
@queryFunction={{this.paginationQueryParams}}
/>

View File

@@ -151,13 +151,16 @@
{{else}}
<EmptyState @title="No policies matching &quot;{{this.pageFilter}}&quot;" />
{{/each}}
{{#if (gt this.model.meta.lastPage 1)}}
<ListPagination
@page={{this.model.meta.currentPage}}
@lastPage={{this.model.meta.lastPage}}
@link="vault.cluster.policies.index"
/>
{{/if}}
<Hds::Pagination::Numbered
@currentPage={{this.model.meta.currentPage}}
@currentPageSize={{this.model.meta.pageSize}}
@route="vault.cluster.policies.index"
@showSizeSelector={{false}}
@totalItems={{this.model.meta.total}}
@queryFunction={{this.paginationQueryParams}}
/>
{{else}}
<EmptyState
@title="No {{uppercase this.policyType}} policies yet"

View File

@@ -94,14 +94,16 @@
{{/if}}
</div>
{{/each}}
{{#if (gt this.model.meta.lastPage 1)}}
<ListPagination
@page={{this.model.meta.currentPage}}
@lastPage={{this.model.meta.lastPage}}
@link={{concat "vault.cluster.secrets.backend.list" (unless this.baseKey.id "-root")}}
@models={{compact (array this.backend (if this.baseKey.id this.baseKey.id))}}
/>
{{/if}}
<Hds::Pagination::Numbered
@currentPage={{this.model.meta.currentPage}}
@currentPageSize={{this.model.meta.pageSize}}
@route={{concat "vault.cluster.secrets.backend.list" (unless this.baseKey.id "-root")}}
@showSizeSelector={{false}}
@totalItems={{this.model.meta.total}}
@queryFunction={{this.paginationQueryParams}}
/>
{{else}}
{{#if (eq this.baseKey.id "")}}
{{#if (and options.firstStep (not this.tab))}}

View File

@@ -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]);
}),
});

View File

@@ -10,12 +10,14 @@
{{else}}
{{yield}}
{{/each}}
{{#if this.showPagination}}
<ListPagination
@page={{@items.meta.currentPage}}
@lastPage={{@items.meta.lastPage}}
@link={{@paginationRouteName}}
data-test-list-view-pagination
{{#if @paginationRouteName}}
<Hds::Pagination::Numbered
@currentPage={{@items.meta.currentPage}}
@currentPageSize={{@items.meta.pageSize}}
@route={{@paginationRouteName}}
@showSizeSelector={{false}}
@totalItems={{@items.meta.total}}
@queryFunction={{this.paginationQueryParams}}
/>
{{/if}}
</div>

View File

@@ -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,
};
};
}
}

View File

@@ -1,94 +0,0 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}
<nav class="pagination is-centered" aria-label="pagination">
{{#if this.hasPrevious}}
<LinkTo @route={{this.link}} @models={{this.models}} @query={{hash page=(dec this.page)}} class="pagination-previous">
<Chevron @direction="left" />
<span class="pagination-previous-label">
Previous
</span>
</LinkTo>
{{else}}
<button type="button" disabled={{true}} class="pagination-previous is-invisible" aria-hidden={{true}}>
<Chevron @direction="left" />
<span class="pagination-previous-label">
Previous
</span>
</button>
{{/if}}
{{#if this.hasNext}}
<LinkTo @route={{this.link}} @models={{this.models}} @query={{hash page=(inc this.page)}} class="pagination-next">
<span class="pagination-next-label">
Next
</span>
<Chevron />
</LinkTo>
{{else}}
<button type="button" disabled={{true}} class="pagination-next is-invisible" aria-hidden={{true}}>
<span class="pagination-next-label">
Next
</span>
<Chevron />
</button>
{{/if}}
{{#if this.segmentLinks}}
<ul class="pagination-list">
<li>
<LinkTo
@route={{this.link}}
@models={{this.models}}
@query={{hash page=1}}
class={{concat (if (eq this.page 1) "is-current ") "pagination-link"}}
aria-label="Go to page 1"
>
1
</LinkTo>
</li>
<li><span class="pagination-ellipsis">&hellip;</span></li>
{{#each this.pageRange as |p|}}
<li>
<LinkTo
@route={{this.link}}
@models={{this.models}}
@query={{hash page=p}}
class={{concat (if (eq this.page 1) "is-current ") "pagination-link"}}
aria-label={{concat "Go to page " p}}
>
{{p}}
</LinkTo>
</li>
{{/each}}
<li><span class="pagination-ellipsis">&hellip;</span></li>
<li>
<LinkTo
@route={{this.link}}
@models={{this.models}}
@query={{hash page=this.lastPage}}
class={{concat (if (eq this.page this.lastPage) "is-current ") "pagination-link"}}
aria-label={{concat "Go to page " this.lastPage}}
>
{{this.lastPage}}
</LinkTo>
</li>
</ul>
{{else}}
<ul class="pagination-list">
{{#each this.pageRange as |p|}}
<li>
<LinkTo
@route={{this.link}}
@models={{this.models}}
@query={{hash page=p}}
class={{concat (if (eq this.page p) "is-current ") "pagination-link"}}
aria-label={{concat "Go to page " p}}
>
{{p}}
</LinkTo>
</li>
{{/each}}
</ul>
{{/if}}
</nav>

View File

@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'core/components/list-pagination';

View File

@@ -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,
};
};
}

View File

@@ -6,5 +6,5 @@
import Controller from '@ember/controller';
export default class KvListController extends Controller {
queryParams = ['pageFilter', 'currentPage'];
queryParams = ['pageFilter', 'page'];
}

View File

@@ -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);
}
}
}

View File

@@ -35,7 +35,7 @@ export default class LdapRolesPageComponent extends Component<Args> {
}
get paginationQueryParams() {
return (page: number) => ({ currentPage: page });
return (page: number) => ({ page });
}
@action

View File

@@ -6,5 +6,5 @@
import Controller from '@ember/controller';
export default class LdapRolesController extends Controller {
queryParams = ['pageFilter', 'currentPage'];
queryParams = ['pageFilter', 'page'];
}

View File

@@ -25,11 +25,11 @@ interface LdapRolesController extends Controller {
breadcrumbs: Array<Breadcrumb>;
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);
}
}
}

View File

@@ -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();
});
});