aboutsummaryrefslogtreecommitdiffstats
path: root/src/pages
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-11-13 17:45:59 +0100
committerArmand Philippot <git@armandphilippot.com>2023-11-13 17:45:59 +0100
commit56878f647ea0f1066fa3e222d7aa0d83057f496d (patch)
tree26f673a062741414bfa7db5d37990936ce115f49 /src/pages
parent599b70cd2390d08ce26ee44174b3f39c6587110c (diff)
refactor(components): rewrite PostsList component
* remove NoResults component and move logic to Search page * add a usePostsList hook * remove Pagination from PostsList (it is only used if javascript is disabled and not on every posts list) * replace `byYear` prop with `sortByYear` * replace `loadMore` prop with `onLoadMore` * remove `showLoadMoreBtn` (we can use `loadMore` prop instead to determine if we need to display the button) * replace `titleLevel` prop with `headingLvl` * add `firstNewResult` prop to handle focus on the new results when loading more article (we should not focus a useless span but the item directly)
Diffstat (limited to 'src/pages')
-rw-r--r--src/pages/blog/index.tsx101
-rw-r--r--src/pages/blog/page/[number].tsx71
-rw-r--r--src/pages/recherche/index.tsx69
-rw-r--r--src/pages/sujet/[slug].tsx10
-rw-r--r--src/pages/thematique/[slug].tsx9
5 files changed, 215 insertions, 45 deletions
diff --git a/src/pages/blog/index.tsx b/src/pages/blog/index.tsx
index d74124e..678b75a 100644
--- a/src/pages/blog/index.tsx
+++ b/src/pages/blog/index.tsx
@@ -3,6 +3,7 @@ import type { GetStaticProps } from 'next';
import Head from 'next/head';
import { useRouter } from 'next/router';
import Script from 'next/script';
+import { useCallback, useRef } from 'react';
import { useIntl } from 'react-intl';
import {
getLayout,
@@ -12,6 +13,9 @@ import {
Notice,
PageLayout,
PostsList,
+ Pagination,
+ type RenderPaginationLink,
+ type RenderPaginationItemAriaLabel,
} from '../../components';
import {
getArticles,
@@ -21,6 +25,7 @@ import {
getTotalThematics,
getTotalTopics,
} from '../../services/graphql';
+import styles from '../../styles/pages/blog.module.scss';
import type {
EdgesResponse,
NextPageWithLayout,
@@ -34,12 +39,16 @@ import {
getBlogSchema,
getLinksListItems,
getPageLinkFromRawData,
- getPostsList,
getSchemaJson,
getWebPageSchema,
} from '../../utils/helpers';
import { loadTranslation, type Messages } from '../../utils/helpers/server';
-import { useBreadcrumb, usePagination, useSettings } from '../../utils/hooks';
+import {
+ useBreadcrumb,
+ useIsMounted,
+ usePostsList,
+ useSettings,
+} from '../../utils/hooks';
type BlogPageProps = {
articles: EdgesResponse<RawArticle>;
@@ -68,7 +77,8 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({
title,
url: ROUTES.BLOG,
});
-
+ const postsListRef = useRef<HTMLDivElement>(null);
+ const isMounted = useIsMounted(postsListRef);
const { blog, website } = useSettings();
const { asPath } = useRouter();
const page = {
@@ -105,14 +115,15 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({
const schemaJsonLd = getSchemaJson([webpageSchema, blogSchema]);
const {
- data,
error,
+ firstNewResultIndex,
isLoading,
isLoadingMore,
isRefreshing,
hasNextPage,
loadMore,
- } = usePagination<RawArticle>({
+ posts,
+ } = usePostsList({
fallback: [articles],
fetcher: getArticles,
perPage: blog.postsPerPage,
@@ -129,7 +140,54 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({
description: 'BlogPage: topics list widget title',
id: '2D9tB5',
});
- const postsListBaseUrl = `${ROUTES.BLOG}/page/`;
+ const renderPaginationLink: RenderPaginationLink = useCallback(
+ (pageNum) => `${ROUTES.BLOG}/page/${pageNum}`,
+ []
+ );
+ const renderPaginationLabel: RenderPaginationItemAriaLabel = useCallback(
+ ({ kind, pageNumber: number, isCurrentPage }) => {
+ switch (kind) {
+ case 'backward':
+ return intl.formatMessage(
+ {
+ defaultMessage: 'Go to previous page, page {number}',
+ description: 'BlogPage: previous page label',
+ id: 'faO6BQ',
+ },
+ { number }
+ );
+ case 'forward':
+ return intl.formatMessage(
+ {
+ defaultMessage: 'Go to next page, page {number}',
+ description: 'BlogPage: next page label',
+ id: 'oq3BzP',
+ },
+ { number }
+ );
+ case 'number':
+ default:
+ return isCurrentPage
+ ? intl.formatMessage(
+ {
+ defaultMessage: 'Current page, page {number}',
+ description: 'BlogPage: current page label',
+ id: 'JL6G22',
+ },
+ { number }
+ )
+ : intl.formatMessage(
+ {
+ defaultMessage: 'Go to page {number}',
+ description: 'BlogPage: page number label',
+ id: 'IVczxR',
+ },
+ { number }
+ );
+ }
+ },
+ [intl]
+ );
const headerMeta: MetaItemData[] = totalArticles
? [
@@ -153,6 +211,12 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({
]
: [];
+ const paginationAriaLabel = intl.formatMessage({
+ defaultMessage: 'Pagination',
+ description: 'BlogPage: pagination accessible name',
+ id: 'AXe1Iz',
+ });
+
return (
<>
<Head>
@@ -206,17 +270,28 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({
/>,
]}
>
- {data ? (
+ {posts ? (
<PostsList
- baseUrl={postsListBaseUrl}
- byYear={true}
+ className={styles.list}
+ firstNewResult={firstNewResultIndex}
isLoading={isLoading || isLoadingMore || isRefreshing}
- loadMore={loadMore}
- posts={getPostsList(data)}
- showLoadMoreBtn={hasNextPage}
- total={totalArticles}
+ onLoadMore={hasNextPage && isMounted ? loadMore : undefined}
+ posts={posts}
+ ref={postsListRef}
+ sortByYear
+ total={isMounted ? totalArticles : undefined}
/>
) : null}
+ {isMounted ? null : (
+ <Pagination
+ aria-label={paginationAriaLabel}
+ current={1}
+ isCentered
+ renderItemAriaLabel={renderPaginationLabel}
+ renderLink={renderPaginationLink}
+ total={totalArticles}
+ />
+ )}
{error ? (
<Notice
// eslint-disable-next-line react/jsx-no-literals -- Kind allowed
diff --git a/src/pages/blog/page/[number].tsx b/src/pages/blog/page/[number].tsx
index 1c723f1..842c2b8 100644
--- a/src/pages/blog/page/[number].tsx
+++ b/src/pages/blog/page/[number].tsx
@@ -4,6 +4,7 @@ import type { GetStaticPaths, GetStaticProps } from 'next';
import Head from 'next/head';
import { useRouter } from 'next/router';
import Script from 'next/script';
+import { useCallback } from 'react';
import { useIntl } from 'react-intl';
import {
getLayout,
@@ -12,6 +13,9 @@ import {
type MetaItemData,
PageLayout,
PostsList,
+ Pagination,
+ type RenderPaginationLink,
+ type RenderPaginationItemAriaLabel,
} from '../../../components';
import {
getArticles,
@@ -131,7 +135,54 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({
description: 'BlogPage: topics list widget title',
id: '2D9tB5',
});
- const postsListBaseUrl = `${ROUTES.BLOG}/page/`;
+ const renderPaginationLink: RenderPaginationLink = useCallback(
+ (pageNum) => `${ROUTES.BLOG}/page/${pageNum}`,
+ []
+ );
+ const renderPaginationLabel: RenderPaginationItemAriaLabel = useCallback(
+ ({ kind, pageNumber: number, isCurrentPage }) => {
+ switch (kind) {
+ case 'backward':
+ return intl.formatMessage(
+ {
+ defaultMessage: 'Go to previous page, page {number}',
+ description: 'BlogPage: previous page label',
+ id: 'faO6BQ',
+ },
+ { number }
+ );
+ case 'forward':
+ return intl.formatMessage(
+ {
+ defaultMessage: 'Go to next page, page {number}',
+ description: 'BlogPage: next page label',
+ id: 'oq3BzP',
+ },
+ { number }
+ );
+ case 'number':
+ default:
+ return isCurrentPage
+ ? intl.formatMessage(
+ {
+ defaultMessage: 'Current page, page {number}',
+ description: 'BlogPage: current page label',
+ id: 'JL6G22',
+ },
+ { number }
+ )
+ : intl.formatMessage(
+ {
+ defaultMessage: 'Go to page {number}',
+ description: 'BlogPage: page number label',
+ id: 'IVczxR',
+ },
+ { number }
+ );
+ }
+ },
+ [intl]
+ );
const headerMeta: MetaItemData[] = totalArticles
? [
@@ -155,6 +206,12 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({
]
: [];
+ const paginationAriaLabel = intl.formatMessage({
+ defaultMessage: 'Pagination',
+ description: 'BlogPage: pagination accessible name',
+ id: 'AXe1Iz',
+ });
+
return (
<>
<Head>
@@ -208,11 +265,13 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({
/>,
]}
>
- <PostsList
- baseUrl={postsListBaseUrl}
- byYear={true}
- pageNumber={pageNumber}
- posts={getPostsList([articles])}
+ <PostsList posts={getPostsList([articles])} sortByYear />
+ <Pagination
+ aria-label={paginationAriaLabel}
+ current={pageNumber}
+ isCentered
+ renderItemAriaLabel={renderPaginationLabel}
+ renderLink={renderPaginationLink}
total={totalArticles}
/>
</PageLayout>
diff --git a/src/pages/recherche/index.tsx b/src/pages/recherche/index.tsx
index a0e5057..effd087 100644
--- a/src/pages/recherche/index.tsx
+++ b/src/pages/recherche/index.tsx
@@ -3,6 +3,7 @@ import type { GetStaticProps } from 'next';
import Head from 'next/head';
import { useRouter } from 'next/router';
import Script from 'next/script';
+import { useCallback } from 'react';
import { useIntl } from 'react-intl';
import {
getLayout,
@@ -13,6 +14,8 @@ import {
PageLayout,
PostsList,
Spinner,
+ SearchForm,
+ type SearchFormSubmit,
} from '../../components';
import {
getArticles,
@@ -22,9 +25,9 @@ import {
getTotalThematics,
getTotalTopics,
} from '../../services/graphql';
+import styles from '../../styles/pages/blog.module.scss';
import type {
NextPageWithLayout,
- RawArticle,
RawThematicPreview,
RawTopicPreview,
} from '../../types';
@@ -33,7 +36,6 @@ import {
getBlogSchema,
getLinksListItems,
getPageLinkFromRawData,
- getPostsList,
getSchemaJson,
getWebPageSchema,
} from '../../utils/helpers';
@@ -41,7 +43,7 @@ import { loadTranslation, type Messages } from '../../utils/helpers/server';
import {
useBreadcrumb,
useDataFromAPI,
- usePagination,
+ usePostsList,
useSettings,
} from '../../utils/hooks';
@@ -59,7 +61,7 @@ const SearchPage: NextPageWithLayout<SearchPageProps> = ({
topicsList,
}) => {
const intl = useIntl();
- const { asPath, query } = useRouter();
+ const { asPath, query, push: routerPush } = useRouter();
const title = query.s
? intl.formatMessage(
{
@@ -116,14 +118,15 @@ const SearchPage: NextPageWithLayout<SearchPageProps> = ({
const schemaJsonLd = getSchemaJson([webpageSchema, blogSchema]);
const {
- data,
error,
+ firstNewResultIndex,
isLoading,
isLoadingMore,
isRefreshing,
hasNextPage,
loadMore,
- } = usePagination<RawArticle>({
+ posts,
+ } = usePostsList({
fallback: [],
fetcher: getArticles,
perPage: blog.postsPerPage,
@@ -167,13 +170,33 @@ const SearchPage: NextPageWithLayout<SearchPageProps> = ({
description: 'SearchPage: topics list widget title',
id: 'N804XO',
});
- const postsListBaseUrl = `${ROUTES.SEARCH}/page/`;
const loadingResults = intl.formatMessage({
defaultMessage: 'Loading the search results...',
description: 'SearchPage: loading search results message',
id: 'EeCqAE',
});
+ const searchSubmitHandler: SearchFormSubmit = useCallback(
+ ({ query: searchQuery }) => {
+ if (!searchQuery)
+ return {
+ messages: {
+ error: intl.formatMessage({
+ defaultMessage: 'Query must be longer than one character.',
+ description: 'NoResults: invalid query message',
+ id: 'VkfO7t',
+ }),
+ },
+ validator: (value) => value.query.length > 1,
+ };
+
+ routerPush({ pathname: ROUTES.SEARCH, query: { s: searchQuery } });
+
+ return undefined;
+ },
+ [intl, routerPush]
+ );
+
return (
<>
<Head>
@@ -227,18 +250,34 @@ const SearchPage: NextPageWithLayout<SearchPageProps> = ({
/>,
]}
>
- {data && data.length > 0 ? (
+ {posts ? null : <Spinner>{loadingResults}</Spinner>}
+ {posts?.length ? (
<PostsList
- baseUrl={postsListBaseUrl}
- byYear={true}
+ className={styles.list}
+ firstNewResult={firstNewResultIndex}
isLoading={isLoading || isLoadingMore || isRefreshing}
- loadMore={loadMore}
- posts={getPostsList(data)}
- showLoadMoreBtn={hasNextPage}
- total={totalArticles ?? 0}
+ onLoadMore={hasNextPage ? loadMore : undefined}
+ posts={posts}
+ sortByYear
/>
) : (
- <Spinner>{loadingResults}</Spinner>
+ <>
+ <p>
+ {intl.formatMessage({
+ defaultMessage: 'No results found.',
+ description: 'SearchPage: no results',
+ id: 'YV//MH',
+ })}
+ </p>
+ <p>
+ {intl.formatMessage({
+ defaultMessage: 'Would you like to try a new search?',
+ description: 'SearchPage: try a new search message',
+ id: 'vtDLzG',
+ })}
+ </p>
+ <SearchForm isLabelHidden onSubmit={searchSubmitHandler} />
+ </>
)}
{error ? (
<Notice
diff --git a/src/pages/sujet/[slug].tsx b/src/pages/sujet/[slug].tsx
index 9094703..66c3d02 100644
--- a/src/pages/sujet/[slug].tsx
+++ b/src/pages/sujet/[slug].tsx
@@ -21,7 +21,7 @@ import {
getTopicsPreview,
getTotalTopics,
} from '../../services/graphql';
-import styles from '../../styles/pages/topic.module.scss';
+import styles from '../../styles/pages/blog.module.scss';
import type { NextPageWithLayout, PageLink, Topic } from '../../types';
import { ROUTES } from '../../utils/constants';
import {
@@ -156,7 +156,6 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({
</>
);
const pageUrl = `${website.url}${asPath}`;
- const postsListBaseUrl = `${ROUTES.TOPICS}/page/`;
return (
<>
@@ -225,11 +224,10 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({
)}
</Heading>
<PostsList
- baseUrl={postsListBaseUrl}
- byYear={true}
+ className={styles.list}
posts={getPostsWithUrl(articles)}
- titleLevel={3}
- total={articles.length}
+ headingLvl={3}
+ sortByYear
/>
</>
) : null}
diff --git a/src/pages/thematique/[slug].tsx b/src/pages/thematique/[slug].tsx
index bb97f47..61d105e 100644
--- a/src/pages/thematique/[slug].tsx
+++ b/src/pages/thematique/[slug].tsx
@@ -20,6 +20,7 @@ import {
getThematicsPreview,
getTotalThematics,
} from '../../services/graphql';
+import styles from '../../styles/pages/blog.module.scss';
import type { NextPageWithLayout, PageLink, Thematic } from '../../types';
import { ROUTES } from '../../utils/constants';
import {
@@ -128,7 +129,6 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({
id: '/42Z0z',
});
const pageUrl = `${website.url}${asPath}`;
- const postsListBaseUrl = `${ROUTES.THEMATICS.INDEX}/page/`;
return (
<>
@@ -197,11 +197,10 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({
)}
</Heading>
<PostsList
- baseUrl={postsListBaseUrl}
- byYear={true}
+ className={styles.list}
posts={getPostsWithUrl(articles)}
- titleLevel={3}
- total={articles.length}
+ headingLvl={3}
+ sortByYear
/>
</>
) : null}