Skip to content

Commit b874111

Browse files
authored
refactor(component): update type names and improve code readability (#319)
* refactor(component): update type names and improve code readibility * test(component): add stories for Pagination component
1 parent a7ddbcf commit b874111

File tree

5 files changed

+206
-92
lines changed

5 files changed

+206
-92
lines changed

apps/website/src/routes/docs/tailwind/(components)/pagination/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { Pagination } from '@qwik-ui/tailwind';
33
import { Toggle } from '@qwik-ui/tailwind';
44

55
export default component$(() => {
6-
const page = useSignal(50);
7-
const pages = useSignal(100);
6+
const page = useSignal(5);
7+
const pages = useSignal(10);
88

99
const showFirstButton = useSignal(true);
1010
const showLastButton = useSignal(true);

nx

Whitespace-only changes.
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { Meta, StoryObj } from 'storybook-framework-qwik';
2+
import { Pagination, PaginationProps } from './pagination';
3+
4+
import { userEvent, within } from '@storybook/testing-library';
5+
import { expect } from '@storybook/jest';
6+
7+
const meta: Meta<PaginationProps> = {
8+
component: Pagination,
9+
};
10+
11+
type Story = StoryObj<PaginationProps>;
12+
13+
export default meta;
14+
15+
export const Default: Story = {
16+
args: {
17+
pages: 10,
18+
page: 5,
19+
},
20+
render: (args) => (
21+
<>
22+
<Pagination
23+
pages={args.pages}
24+
page={args.page}
25+
onPaging$={(newValue: number) => {
26+
console.log('value', newValue);
27+
}}
28+
showFirstButton={args.showFirstButton}
29+
showLastButton={args.showLastButton}
30+
hideNextButton={args.hideNextButton}
31+
hidePrevButton={args.hidePrevButton}
32+
siblingCount={args.siblingCount}
33+
boundaryCount={args.boundaryCount}
34+
/>
35+
</>
36+
),
37+
play: ({ canvasElement, args }) => {
38+
const canvas = within(canvasElement);
39+
const values = ['1', '4', '5', '6', '10'];
40+
values.forEach((value) => {
41+
expect(canvas.getByText(value)).toBeVisible();
42+
});
43+
expect(canvas.queryByText('first')).toBeNull();
44+
expect(canvas.queryByText('last')).toBeNull();
45+
expect(canvas.getByText('prev')).toBeVisible();
46+
expect(canvas.getByText('next')).toBeVisible();
47+
},
48+
};
49+
50+
export const AllButtons: Story = {
51+
args: {
52+
pages: 10,
53+
page: 5,
54+
showFirstButton: true,
55+
showLastButton: true,
56+
hideNextButton: false,
57+
hidePrevButton: false,
58+
siblingCount: 1,
59+
boundaryCount: 1,
60+
},
61+
render: (args) => (
62+
<>
63+
<Pagination
64+
pages={args.pages}
65+
page={args.page}
66+
onPaging$={(newValue: number) => {
67+
console.log('value', newValue);
68+
}}
69+
showFirstButton={args.showFirstButton}
70+
showLastButton={args.showLastButton}
71+
hideNextButton={args.hideNextButton}
72+
hidePrevButton={args.hidePrevButton}
73+
siblingCount={args.siblingCount}
74+
boundaryCount={args.boundaryCount}
75+
/>
76+
</>
77+
),
78+
play: ({ canvasElement, args }) => {
79+
const canvas = within(canvasElement);
80+
const values = ['1', '4', '5', '6', '10'];
81+
values.forEach((value) => {
82+
expect(canvas.getByText(value)).toBeVisible();
83+
});
84+
expect(canvas.getByText('first')).toBeVisible();
85+
expect(canvas.getByText('last')).toBeVisible();
86+
expect(canvas.getByText('prev')).toBeVisible();
87+
expect(canvas.getByText('next')).toBeVisible();
88+
},
89+
};
90+
91+
export const HideButtons: Story = {
92+
args: {
93+
pages: 10,
94+
page: 5,
95+
showFirstButton: false,
96+
showLastButton: false,
97+
hideNextButton: true,
98+
hidePrevButton: true,
99+
siblingCount: 1,
100+
boundaryCount: 1,
101+
},
102+
render: (args) => (
103+
<>
104+
<Pagination
105+
pages={args.pages}
106+
page={args.page}
107+
onPaging$={(newValue: number) => {
108+
console.log('value', newValue);
109+
}}
110+
showFirstButton={args.showFirstButton}
111+
showLastButton={args.showLastButton}
112+
hideNextButton={args.hideNextButton}
113+
hidePrevButton={args.hidePrevButton}
114+
siblingCount={args.siblingCount}
115+
boundaryCount={args.boundaryCount}
116+
/>
117+
</>
118+
),
119+
play: ({ canvasElement, args }) => {
120+
const canvas = within(canvasElement);
121+
const values = ['1', '4', '5', '6', '10'];
122+
values.forEach((value) => {
123+
expect(canvas.getByText(value)).toBeVisible();
124+
});
125+
expect(canvas.queryByText('first')).toBeNull();
126+
expect(canvas.queryByText('last')).toBeNull();
127+
expect(canvas.queryByText('prev')).toBeNull();
128+
expect(canvas.queryByText('next')).toBeNull();
129+
},
130+
};

packages/kit-headless/src/components/pagination/pagination.tsx

Lines changed: 63 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -17,92 +17,75 @@ import { Button as HeadlessButton } from '@qwik-ui/primitives';
1717
* size: 'sm' | 'md' | 'lg'
1818
* paginationRange (see https://mui.com/material-ui/react-pagination/#pagination-ranges)
1919
*
20-
* EVENTS
21-
* onPageChange
22-
*
2320
*/
24-
export interface IPaginationProps extends IGetPaginationItemsOptions {
21+
export interface PaginationProps extends PaginationOptions {
2522
pages: number;
2623
page: number;
2724
onPaging$: PropFunction<(index: number) => void>;
28-
RenderItem?: Component<IRenderPaginationItemProps>;
25+
RenderItem?: Component<PaginationButtonProps>;
2926
RenderDivider?: Component<object>;
3027
}
3128

32-
export interface IRenderPaginationItemProps {
29+
export interface PaginationOptions {
30+
boundaryCount?: number;
31+
siblingCount?: number;
32+
hidePrevButton?: boolean;
33+
hideNextButton?: boolean;
34+
showFirstButton?: boolean;
35+
showLastButton?: boolean;
36+
activeClass?: string;
37+
defaultClass?: string;
38+
labels?: PaginationButtonLabels;
39+
}
40+
41+
export interface PaginationButtonProps {
3342
onClick$: PropFunction<() => void>;
3443
disabled?: boolean;
3544
'aria-label': string;
3645
'aria-current'?: boolean;
37-
value: TPaginationItemValue;
3846
key?: string | number;
3947
activeClass?: string;
4048
defaultClass?: string;
41-
labels?: TPaginationLabels;
49+
value: PaginationButtonValue;
50+
labels?: PaginationButtonLabels;
4251
}
4352

44-
export type TPaginationLabels = {
45-
first?: string;
46-
last?: string;
47-
next?: string;
48-
prev?: string;
49-
};
50-
51-
export type TPaginationItemValue =
53+
export type PaginationButtonValue =
5254
| 'prev'
5355
| 'next'
54-
| number
55-
| 'start-ellipsis'
56-
| 'end-ellipsis'
5756
| 'first'
5857
| 'last'
58+
// | 'divider'
59+
// | 'start-ellipsis'
60+
// | 'end-ellipsis'
61+
| number
5962
| string;
6063

64+
export interface PaginationButtonLabels {
65+
first?: string;
66+
last?: string;
67+
next?: string;
68+
prev?: string;
69+
}
70+
6171
const range = (start: number, end: number) => {
6272
const length = end - start + 1;
6373
return Array.from({ length }, (_, i) => start + i);
6474
};
6575

66-
export type TPaginationItem =
67-
| 'first'
68-
| 'last'
69-
| 'prev'
70-
| 'next'
71-
| 'divider'
72-
| number
73-
| string;
74-
75-
export interface IGetPaginationItems {
76-
page: number;
77-
count: number;
78-
options: IGetPaginationItemsOptions;
79-
}
80-
81-
export interface IGetPaginationItemsOptions {
82-
boundaryCount?: number;
83-
siblingCount?: number;
84-
hidePrevButton?: boolean;
85-
hideNextButton?: boolean;
86-
showFirstButton?: boolean;
87-
showLastButton?: boolean;
88-
activeClass?: string;
89-
defaultClass?: string;
90-
labels?: TPaginationLabels;
91-
}
92-
93-
export const getPaginationItems = (
94-
page: IGetPaginationItems['page'],
95-
count: IGetPaginationItems['count'],
96-
labels: TPaginationLabels | undefined,
76+
export const getPaginationButtons = (
77+
page: number,
78+
count: number,
79+
labels: PaginationButtonLabels | undefined,
9780
{
9881
boundaryCount = 1,
9982
siblingCount = 1,
10083
hidePrevButton,
10184
hideNextButton,
10285
showFirstButton,
10386
showLastButton,
104-
}: IGetPaginationItems['options']
105-
): TPaginationItem[] => {
87+
}: PaginationOptions
88+
): PaginationButtonValue[] => {
10689
const startPages = range(1, Math.min(boundaryCount, count));
10790
const endPages = range(
10891
Math.max(count - boundaryCount + 1, boundaryCount + 1),
@@ -152,14 +135,14 @@ export const getPaginationItems = (
152135
return items;
153136
};
154137

155-
export const RenderPaginationItem = component$(
138+
export const PaginationButton = component$(
156139
({
157140
'aria-label': ariaLabel,
158141
disabled,
159142
onClick$,
160143
key,
161144
value,
162-
}: IRenderPaginationItemProps) => {
145+
}: PaginationButtonProps) => {
163146
return (
164147
<HeadlessButton
165148
onClick$={onClick$}
@@ -188,7 +171,7 @@ export const PaginationDivider = component$(() => {
188171
*/
189172
export const Pagination = component$(
190173
({
191-
RenderItem = RenderPaginationItem,
174+
RenderItem = PaginationButton,
192175
RenderDivider = PaginationDivider,
193176
onPaging$,
194177
page,
@@ -197,17 +180,37 @@ export const Pagination = component$(
197180
defaultClass,
198181
labels,
199182
...rest
200-
}: IPaginationProps) => {
201-
const pagi = getPaginationItems(page, pages, labels, rest);
202-
183+
}: PaginationProps) => {
203184
const _onPaging$ = $((page: number) => {
204185
if (page < 1 || page > pages) return;
205186
onPaging$(page);
206187
});
207188

189+
const itemClickHandler = $((item: PaginationButtonValue) =>
190+
_onPaging$(
191+
(() => {
192+
switch (item) {
193+
case 'first':
194+
return 1;
195+
case 'prev':
196+
return page - 1;
197+
case 'next':
198+
return page + 1;
199+
case 'last':
200+
return pages;
201+
default:
202+
if (typeof item === 'number') return item;
203+
return page;
204+
}
205+
})()
206+
)
207+
);
208+
209+
const items = getPaginationButtons(page, pages, labels, rest);
210+
208211
return (
209212
<>
210-
{pagi.map((item, i) => {
213+
{items.map((item, i) => {
211214
return (
212215
<>
213216
{item === 'divider' ? (
@@ -218,25 +221,7 @@ export const Pagination = component$(
218221
defaultClass={defaultClass}
219222
key={i}
220223
labels={labels}
221-
onClick$={() =>
222-
_onPaging$(
223-
(() => {
224-
switch (item) {
225-
case 'first':
226-
return 1;
227-
case 'prev':
228-
return page - 1;
229-
case 'next':
230-
return page + 1;
231-
case 'last':
232-
return pages;
233-
default:
234-
if (typeof item === 'number') return item;
235-
return page;
236-
}
237-
})()
238-
)
239-
}
224+
onClick$={() => itemClickHandler(item)}
240225
disabled={
241226
(['prev', 'first'].includes(item.toString()) &&
242227
page === 1) ||

0 commit comments

Comments
 (0)