Skip to content

Commit f45b8ed

Browse files
author
shleewhite
committed
feat: convert Pagination page to gts format
1 parent 4cddac0 commit f45b8ed

File tree

16 files changed

+1194
-813
lines changed

16 files changed

+1194
-813
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/**
2+
* Copyright (c) HashiCorp, Inc.
3+
* SPDX-License-Identifier: MPL-2.0
4+
*/
5+
import Component from '@glimmer/component';
6+
import { array, hash } from '@ember/helper';
7+
import { tracked } from '@glimmer/tracking';
8+
9+
import USERS from 'showcase/mocks/user-data';
10+
import type { User } from 'showcase/mocks/user-data';
11+
12+
import {
13+
HdsPaginationCompact,
14+
HdsTable,
15+
} from '@hashicorp/design-system-components/components';
16+
import type { HdsPaginationDirections } from '@hashicorp/design-system-components/components/hds/pagination/types';
17+
18+
const getCursorParts = (cursor: string | null, records: User[]) => {
19+
if (!cursor) {
20+
return { direction: 'next', cursorID: null, cursorIndex: -1 };
21+
}
22+
23+
const token = atob(cursor);
24+
const tokenParts = [...token.split('__')];
25+
const direction = tokenParts[0];
26+
const cursorID = tokenParts[1] ? parseInt(tokenParts[1]) : undefined;
27+
const cursorIndex = records.findIndex((element) => element.id === cursorID);
28+
29+
return { direction, cursorID, cursorIndex };
30+
};
31+
32+
const getNewPrevNextCursors = (
33+
cursor: string | null,
34+
pageSize: number,
35+
records: User[],
36+
) => {
37+
const { direction, cursorIndex } = getCursorParts(cursor, records);
38+
39+
let newPrevCursor;
40+
let newNextCursor;
41+
42+
const prevCursorIndex =
43+
direction === 'prev' ? cursorIndex - pageSize : cursorIndex;
44+
if (prevCursorIndex > 0) {
45+
const newPrevRecordId = records[prevCursorIndex]?.id;
46+
newPrevCursor = btoa(`prev__${newPrevRecordId}`);
47+
} else {
48+
newPrevCursor = null;
49+
}
50+
51+
const nextCursorIndex =
52+
direction === 'next' ? cursorIndex + pageSize : cursorIndex;
53+
if (nextCursorIndex < records.length) {
54+
const newNextRecordId = records[nextCursorIndex]?.id;
55+
newNextCursor = btoa(`next__${newNextRecordId}`);
56+
} else {
57+
newNextCursor = null;
58+
}
59+
60+
return {
61+
newPrevCursor,
62+
newNextCursor,
63+
};
64+
};
65+
66+
export default class CodeFragmentWithCompactAndEvents extends Component {
67+
@tracked currentCursor: string | null = btoa(`next__1`);
68+
@tracked pageSize = 5;
69+
70+
get paginatedData() {
71+
const { direction, cursorIndex } = getCursorParts(
72+
this.currentCursor,
73+
USERS,
74+
);
75+
76+
let start;
77+
let end;
78+
const pageSize = this.pageSize;
79+
if (direction === 'prev') {
80+
end = cursorIndex;
81+
start = cursorIndex - pageSize;
82+
} else {
83+
start = cursorIndex;
84+
end = cursorIndex + pageSize;
85+
}
86+
return USERS.slice(start, end);
87+
}
88+
89+
get newCursors() {
90+
const { newPrevCursor, newNextCursor } = getNewPrevNextCursors(
91+
this.currentCursor,
92+
this.pageSize,
93+
USERS,
94+
);
95+
return {
96+
newPrevCursor,
97+
newNextCursor,
98+
};
99+
}
100+
101+
get isPrevButtonDisabled() {
102+
const { newPrevCursor } = this.newCursors;
103+
return newPrevCursor === null;
104+
}
105+
106+
get isNextButtonDisabled() {
107+
const { newNextCursor } = this.newCursors;
108+
return newNextCursor === null;
109+
}
110+
111+
onPageChange = (page: HdsPaginationDirections) => {
112+
// get the next/prev cursors
113+
const { newPrevCursor, newNextCursor } = this.newCursors;
114+
// update the "current" cursor
115+
if (page === 'prev') {
116+
this.currentCursor = newPrevCursor;
117+
} else if (page === 'next') {
118+
this.currentCursor = newNextCursor;
119+
}
120+
};
121+
122+
<template>
123+
<div class="shw-component-pagination-table-demo">
124+
<HdsTable
125+
@model={{this.paginatedData}}
126+
@columns={{array
127+
(hash key="id" label="ID")
128+
(hash key="name" label="Name")
129+
(hash key="email" label="Email")
130+
(hash key="role" label="Role")
131+
}}
132+
>
133+
<:body as |B|>
134+
<B.Tr>
135+
<B.Td>{{B.data.id}}</B.Td>
136+
<B.Td>{{B.data.name}}</B.Td>
137+
<B.Td>{{B.data.email}}</B.Td>
138+
<B.Td>{{B.data.role}}</B.Td>
139+
</B.Tr>
140+
</:body>
141+
</HdsTable>
142+
<HdsPaginationCompact
143+
@isDisabledPrev={{this.isPrevButtonDisabled}}
144+
@isDisabledNext={{this.isNextButtonDisabled}}
145+
@onPageChange={{this.onPageChange}}
146+
/>
147+
</div>
148+
</template>
149+
}
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/**
2+
* Copyright (c) HashiCorp, Inc.
3+
* SPDX-License-Identifier: MPL-2.0
4+
*/
5+
import Component from '@glimmer/component';
6+
import { array, hash } from '@ember/helper';
7+
import { inject as service } from '@ember/service';
8+
import type RouterService from '@ember/routing/router-service';
9+
10+
import USERS from 'showcase/mocks/user-data';
11+
import type { User } from 'showcase/mocks/user-data';
12+
13+
import {
14+
HdsPaginationCompact,
15+
HdsTable,
16+
} from '@hashicorp/design-system-components/components';
17+
import type { HdsPaginationDirections } from '@hashicorp/design-system-components/components/hds/pagination/types';
18+
19+
const getCursorParts = (cursor: string | null, records: User[]) => {
20+
if (!cursor) {
21+
return { direction: 'next', cursorID: null, cursorIndex: -1 };
22+
}
23+
24+
const token = atob(cursor);
25+
const tokenParts = [...token.split('__')];
26+
const direction = tokenParts[0];
27+
const cursorID = tokenParts[1] ? parseInt(tokenParts[1]) : undefined;
28+
const cursorIndex = records.findIndex((element) => element.id === cursorID);
29+
30+
return { direction, cursorID, cursorIndex };
31+
};
32+
33+
const getNewPrevNextCursors = (
34+
cursor: string | null,
35+
pageSize: number,
36+
records: User[],
37+
) => {
38+
const { direction, cursorIndex } = getCursorParts(cursor, records);
39+
40+
let newPrevCursor;
41+
let newNextCursor;
42+
43+
const prevCursorIndex =
44+
direction === 'prev' ? cursorIndex - pageSize : cursorIndex;
45+
if (prevCursorIndex > 0) {
46+
const newPrevRecordId = records[prevCursorIndex]?.id;
47+
newPrevCursor = btoa(`prev__${newPrevRecordId}`);
48+
} else {
49+
newPrevCursor = null;
50+
}
51+
52+
const nextCursorIndex =
53+
direction === 'next' ? cursorIndex + pageSize : cursorIndex;
54+
if (nextCursorIndex < records.length) {
55+
const newNextRecordId = records[nextCursorIndex]?.id;
56+
newNextCursor = btoa(`next__${newNextRecordId}`);
57+
} else {
58+
newNextCursor = null;
59+
}
60+
61+
return {
62+
newPrevCursor,
63+
newNextCursor,
64+
};
65+
};
66+
67+
interface CodeFragmentWithCompactAndRoutingSignature {
68+
Args: {
69+
nextCursor: string | null;
70+
prevCursor: string | null;
71+
pageSize: number;
72+
};
73+
}
74+
75+
export default class CodeFragmentWithCompactAndRouting extends Component<CodeFragmentWithCompactAndRoutingSignature> {
76+
@service declare router: RouterService;
77+
78+
get paginatedData() {
79+
const { prevCursor, nextCursor, pageSize } = this.args;
80+
81+
let token = '';
82+
if (prevCursor) {
83+
token = prevCursor;
84+
} else if (nextCursor) {
85+
token = nextCursor;
86+
}
87+
88+
const { direction, cursorIndex } = getCursorParts(token, USERS);
89+
90+
let start;
91+
let end;
92+
93+
if (direction === 'prev') {
94+
end = cursorIndex;
95+
// we want to avoid having a negative `start` index for the `array.slide` method (it happens if the cursorIndex is smaller than the selected page size)
96+
start = Math.max(0, cursorIndex - pageSize);
97+
} else {
98+
start = cursorIndex;
99+
end = cursorIndex + pageSize;
100+
}
101+
102+
return USERS.slice(start, end);
103+
}
104+
105+
get newCursors() {
106+
const { prevCursor, nextCursor, pageSize } = this.args;
107+
108+
let cursor = '';
109+
// In cloud UI they use two distinct query params for the cursor depending if it's "prev" or "next"
110+
if (prevCursor) {
111+
cursor = prevCursor;
112+
} else if (nextCursor) {
113+
cursor = nextCursor;
114+
}
115+
return getNewPrevNextCursors(cursor, pageSize, USERS);
116+
}
117+
118+
get isPrevButtonDisabled() {
119+
const { newPrevCursor } = this.newCursors;
120+
return newPrevCursor === null;
121+
}
122+
123+
get isNextButtonDisabled() {
124+
const { newNextCursor } = this.newCursors;
125+
return newNextCursor === null;
126+
}
127+
128+
get demoRouteName() {
129+
// eg. 'components.pagination';
130+
const routeName = this.router.currentRouteName;
131+
return routeName ?? '';
132+
}
133+
134+
get demoQueryFunction() {
135+
const { newPrevCursor, newNextCursor } = this.newCursors;
136+
const currPrevCursor = this.args.prevCursor;
137+
const currNextCursor = this.args.nextCursor;
138+
139+
return (page: HdsPaginationDirections, pageSize?: number) => {
140+
// for the "compact" pagination when the user changes the page size and the `onPageSizeChange` function is invoked
141+
// the callback function returns a `null` value for the `page` argument so the consumer can decide how to handle the cursors acordingly
142+
143+
if (page === null) {
144+
return {
145+
prevCursorDemoCompact: currPrevCursor,
146+
nextCursorDemoCompact: currNextCursor,
147+
pageSizeDemoCompact: pageSize,
148+
};
149+
} else {
150+
return {
151+
prevCursorDemoCompact: page === 'prev' ? newPrevCursor : undefined,
152+
nextCursorDemoCompact: page === 'next' ? newNextCursor : undefined,
153+
pageSizeDemoCompact: pageSize,
154+
};
155+
}
156+
};
157+
}
158+
159+
onPageChange = (page: HdsPaginationDirections) => {
160+
console.log('genericHandlePageChange invoked with arguments:');
161+
console.log('page', page);
162+
console.log('pageSize', this.args.pageSize);
163+
};
164+
165+
onPageSizeChange = (pageSize: number) => {
166+
// there should be a better handling of how the "paginated" data list is computed and shown to the user to avoid some UX issues
167+
// for details see this thread: https://github.com/hashicorp/design-system/pull/1724#issuecomment-1768167782
168+
this.router.transitionTo(this.demoRouteName, {
169+
queryParams: {
170+
pageSizeDemoCompact: pageSize,
171+
},
172+
});
173+
};
174+
175+
<template>
176+
<div class="shw-component-pagination-table-demo">
177+
<HdsTable
178+
@model={{this.paginatedData}}
179+
@columns={{array
180+
(hash key="id" label="ID")
181+
(hash key="name" label="Name")
182+
(hash key="email" label="Email")
183+
(hash key="role" label="Role")
184+
}}
185+
>
186+
<:body as |B|>
187+
<B.Tr>
188+
<B.Td>{{B.data.id}}</B.Td>
189+
<B.Td>{{B.data.name}}</B.Td>
190+
<B.Td>{{B.data.email}}</B.Td>
191+
<B.Td>{{B.data.role}}</B.Td>
192+
</B.Tr>
193+
</:body>
194+
</HdsTable>
195+
<HdsPaginationCompact
196+
@queryFunction={{this.demoQueryFunction}}
197+
@showSizeSelector={{true}}
198+
@route={{this.demoRouteName}}
199+
@currentPageSize={{@pageSize}}
200+
@pageSizes={{array 5 10 30}}
201+
@isDisabledPrev={{this.isPrevButtonDisabled}}
202+
@isDisabledNext={{this.isNextButtonDisabled}}
203+
@onPageSizeChange={{this.onPageSizeChange}}
204+
@onPageChange={{this.onPageChange}}
205+
/>
206+
</div>
207+
</template>
208+
}

0 commit comments

Comments
 (0)