Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit aded34b

Browse files
jaqypbekovflowd
andauthored
Feature/components/pagination (#2951)
* refactor(navigation): created getPaginationPath utility * feat(common/pagination): implemented common version * feat(blog): added pagination * feat(blog): showed pagination the blog's home page * ref(common/pagination): usePaginationPages hook destroyed, logic simplifyed, tests updated * fix(blog): handle undefined category on the blog home page * ref(blog/pagination): moved to blog components, made more simple * ref(blog/pagination): types moved, styles updated Co-authored-by: Claudio Wunder <cwunder@gnome.org>
1 parent 9d5c632 commit aded34b

File tree

18 files changed

+954
-9
lines changed

18 files changed

+954
-9
lines changed

gatsby-node.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const createBlogPages = require('./util-node/createBlogPages');
1515
const createLearnPages = require('./util-node/createLearnPages');
1616
const createApiPages = require('./util-node/createApiPages');
1717
const generateRedirects = require('./util-node/generateRedirects');
18+
const getPaginationPath = require('./util-node/getPaginationPath');
1819
const redirects = require('./redirects');
1920
const nodeLocales = require('./locales');
2021
const { learnPath, apiPath, blogPath } = require('./pathPrefixes');
@@ -168,15 +169,11 @@ exports.createPages = async ({ graphql, actions }) => {
168169
total: paginatedPosts.length,
169170
});
170171

171-
const getBlogPagePath = index => {
172-
const categoryPath = node.name ? `${blogPath}${node.name}/` : blogPath;
173-
174-
return index === 0 ? categoryPath : `${categoryPath}page/${index + 1}/`;
175-
};
172+
const getBlogPagePath = getPaginationPath(blogPath, node.name);
176173

177174
paginatedPosts.forEach((currentPagePosts, index) => {
178175
createPage({
179-
path: getBlogPagePath(index),
176+
path: getBlogPagePath(index + 1),
180177
component: blogTemplate,
181178
context: {
182179
category: node.name ? node : null,
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React from 'react';
2+
import classnames from 'classnames';
3+
import { LocalizedLink as Link } from 'gatsby-theme-i18n';
4+
import { FormattedMessage } from 'react-intl';
5+
import styles from './index.module.scss';
6+
7+
interface Props {
8+
className: string;
9+
currentPage: number;
10+
hrefBuilder: (n: number) => string;
11+
onPageChange: (n: number) => void;
12+
pageCount: number;
13+
}
14+
15+
const PaginationNextPage = ({
16+
className,
17+
currentPage,
18+
hrefBuilder,
19+
onPageChange,
20+
pageCount,
21+
}: Props) => {
22+
const componentClassName = classnames(className, styles.next);
23+
const content = <FormattedMessage id="components.pagination.next" />;
24+
const disabled = currentPage === pageCount;
25+
26+
if (disabled) {
27+
return (
28+
<span aria-disabled="true" className={componentClassName}>
29+
{content}
30+
</span>
31+
);
32+
}
33+
34+
const nextPage = currentPage + 1;
35+
36+
return (
37+
<Link
38+
aria-label="Next Page"
39+
onClick={onPageChange(nextPage)}
40+
className={componentClassName}
41+
rel="next"
42+
to={hrefBuilder(nextPage)}
43+
>
44+
{content}
45+
</Link>
46+
);
47+
};
48+
49+
export default PaginationNextPage;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React, { MouseEvent } from 'react';
2+
import classnames from 'classnames';
3+
import { LocalizedLink as Link } from 'gatsby-theme-i18n';
4+
import { PageModel } from '../../../types/pagination';
5+
6+
interface Props {
7+
className: string;
8+
page: PageModel;
9+
hrefBuilder: (n: number) => string;
10+
onPageChange?: (e: MouseEvent) => void;
11+
}
12+
13+
const PaginationPage = ({
14+
className,
15+
page,
16+
hrefBuilder,
17+
onPageChange,
18+
}: Props) => {
19+
const content = String(page.num);
20+
const componentClassName = classnames(className, page?.className ?? '');
21+
22+
if (page.selected) {
23+
return (
24+
<em aria-current="page" className={componentClassName}>
25+
{content}
26+
</em>
27+
);
28+
}
29+
30+
return (
31+
<Link
32+
aria-label={`Page ${page.num}`}
33+
onClick={onPageChange}
34+
className={componentClassName}
35+
to={hrefBuilder(page.num)}
36+
>
37+
{content}
38+
</Link>
39+
);
40+
};
41+
42+
export default PaginationPage;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import React from 'react';
2+
import classnames from 'classnames';
3+
import { LocalizedLink as Link } from 'gatsby-theme-i18n';
4+
import { FormattedMessage } from 'react-intl';
5+
import styles from './index.module.scss';
6+
7+
interface Props {
8+
className: string;
9+
currentPage: number;
10+
hrefBuilder: (n: number) => string;
11+
onPageChange: (n: number) => void;
12+
}
13+
14+
const PaginationPreviousPage = ({
15+
className,
16+
currentPage,
17+
hrefBuilder,
18+
onPageChange,
19+
}: Props) => {
20+
const componentClassName = classnames(className, styles.prev);
21+
const content = <FormattedMessage id="components.pagination.previous" />;
22+
const disabled = currentPage === 1;
23+
24+
if (disabled) {
25+
return (
26+
<span aria-disabled="true" className={componentClassName}>
27+
{content}
28+
</span>
29+
);
30+
}
31+
32+
const previousPage = currentPage - 1;
33+
34+
return (
35+
<Link
36+
aria-label="Previous Page"
37+
onClick={onPageChange(previousPage)}
38+
className={componentClassName}
39+
rel="prev"
40+
to={hrefBuilder(previousPage)}
41+
>
42+
{content}
43+
</Link>
44+
);
45+
};
46+
47+
export default PaginationPreviousPage;
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React from 'react';
2+
import { render, screen } from '@testing-library/react';
3+
import userEvent from '@testing-library/user-event';
4+
import Pagination from '..';
5+
6+
describe('Pagination component', () => {
7+
it('renders correctly', () => {
8+
const { container } = render(<Pagination currentPage={1} pageCount={10} />);
9+
expect(container).toMatchSnapshot();
10+
});
11+
12+
it('renders with only next and prev link', () => {
13+
const { container } = render(
14+
<Pagination currentPage={1} pageCount={10} showPages={false} />
15+
);
16+
expect(container).toMatchSnapshot();
17+
});
18+
19+
it('renders with disabled next link', () => {
20+
const { container } = render(
21+
<Pagination currentPage={10} pageCount={10} />
22+
);
23+
expect(container).toMatchSnapshot();
24+
});
25+
26+
it('renders with wrapperClassName', () => {
27+
const { container } = render(
28+
<Pagination currentPage={1} pageCount={10} wrapperClassName="wrapper" />
29+
);
30+
expect(container).toMatchSnapshot();
31+
});
32+
33+
it('handles onPageChange', async () => {
34+
const onClick = jest.fn();
35+
render(
36+
<Pagination
37+
currentPage={1}
38+
pageCount={10}
39+
wrapperClassName="wrapper"
40+
onPageChange={onClick}
41+
/>
42+
);
43+
const linkItem: Element = screen.getAllByRole('link')[0] as Element;
44+
await userEvent.click(linkItem);
45+
expect(onClick).toHaveBeenCalled();
46+
});
47+
48+
it('handles hrefBuilder', () => {
49+
const hrefBuilder = jest.fn((page: number) => `/page/${page}`);
50+
const { container } = render(
51+
<Pagination
52+
currentPage={2}
53+
hrefBuilder={hrefBuilder}
54+
pageCount={10}
55+
wrapperClassName="wrapper"
56+
/>
57+
);
58+
59+
expect(hrefBuilder).toHaveBeenCalled();
60+
expect(container).toMatchSnapshot();
61+
});
62+
});

0 commit comments

Comments
 (0)