aboutsummaryrefslogtreecommitdiffstats
path: root/src/pages
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-12-01 17:59:30 +0100
committerArmand Philippot <git@armandphilippot.com>2023-12-01 18:06:46 +0100
commit11e3ee75fcab0ab54b2bc1713a402c5cc3070c2d (patch)
tree7cb478ac6b29f2b527eb3ec379b305b74dd4f0ba /src/pages
parentdfdbf6cac1fe3719dc71e130129d28e04ba4e225 (diff)
refactor(pages): refine Topic pages
* add useTopic and useTopicsList hooks to refresh data * add a table of contents * add Cypress tests
Diffstat (limited to 'src/pages')
-rw-r--r--src/pages/sujet/[slug].tsx202
1 files changed, 130 insertions, 72 deletions
diff --git a/src/pages/sujet/[slug].tsx b/src/pages/sujet/[slug].tsx
index 185756b..8a9c2f3 100644
--- a/src/pages/sujet/[slug].tsx
+++ b/src/pages/sujet/[slug].tsx
@@ -15,17 +15,24 @@ import {
PageHeader,
PageSidebar,
PageBody,
+ LoadingPage,
+ TocWidget,
+ Spinner,
} from '../../components';
import {
convertWPTopicPreviewToPageLink,
- convertWPTopicToTopic,
fetchAllTopicsSlugs,
fetchTopic,
fetchTopicsCount,
fetchTopicsList,
} from '../../services/graphql';
import styles from '../../styles/pages/blog.module.scss';
-import type { NextPageWithLayout, PageLink, Topic } from '../../types';
+import type {
+ GraphQLConnection,
+ NextPageWithLayout,
+ WPTopic,
+ WPTopicPreview,
+} from '../../types';
import { CONFIG } from '../../utils/config';
import { ROUTES } from '../../utils/constants';
import {
@@ -34,21 +41,45 @@ import {
getSchemaJson,
getSinglePageSchema,
getWebPageSchema,
+ slugify,
} from '../../utils/helpers';
import { loadTranslation, type Messages } from '../../utils/helpers/server';
-import { useBreadcrumb } from '../../utils/hooks';
+import {
+ useBreadcrumb,
+ useHeadingsTree,
+ useTopic,
+ useTopicsList,
+} from '../../utils/hooks';
export type TopicPageProps = {
- currentTopic: Topic;
- topics: PageLink[];
+ data: {
+ currentTopic: WPTopic;
+ otherTopics: GraphQLConnection<WPTopicPreview>;
+ totalTopics: number;
+ };
translation: Messages;
};
-const TopicPage: NextPageWithLayout<TopicPageProps> = ({
- currentTopic,
- topics,
-}) => {
- const { content, intro, meta, slug, title } = currentTopic;
+const TopicPage: NextPageWithLayout<TopicPageProps> = ({ data }) => {
+ const intl = useIntl();
+ const { isFallback } = useRouter();
+ const { isLoading, topic } = useTopic(
+ data.currentTopic.slug,
+ data.currentTopic
+ );
+ const { isLoading: areTopicsLoading, topics } = useTopicsList({
+ fallback: data.otherTopics,
+ input: { first: data.totalTopics, where: { notIn: [topic.id] } },
+ });
+ const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({
+ title: topic.title,
+ url: `${ROUTES.TOPICS}/${topic.slug}`,
+ });
+ const { ref, tree } = useHeadingsTree({ fromLevel: 2 });
+
+ if (isFallback || isLoading) return <LoadingPage />;
+
+ const { content, intro, meta, slug, title } = topic;
const {
articles,
cover,
@@ -57,17 +88,11 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({
relatedThematics,
website: officialWebsite,
} = meta;
- const intl = useIntl();
- const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({
- title,
- url: `${ROUTES.TOPICS}/${slug}`,
- });
- const { asPath } = useRouter();
const webpageSchema = getWebPageSchema({
description: seo.description,
locale: CONFIG.locales.defaultLocale,
- slug: asPath,
+ slug,
title: seo.title,
updateDate: dates.update,
});
@@ -78,30 +103,46 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({
id: 'topic',
kind: 'page',
locale: CONFIG.locales.defaultLocale,
- slug: asPath,
+ slug,
title,
});
const schemaJsonLd = getSchemaJson([webpageSchema, articleSchema]);
- const topicsListTitle = intl.formatMessage({
- defaultMessage: 'Other topics',
- description: 'TopicPage: other topics list widget title',
- id: 'JpC3JH',
- });
-
- const thematicsListTitle = intl.formatMessage({
- defaultMessage: 'Related thematics',
- description: 'TopicPage: related thematics list widget title',
- id: '/sRqPT',
- });
+ const messages = {
+ widgets: {
+ loadingTopicsList: intl.formatMessage({
+ defaultMessage: 'Topics are loading...',
+ description: 'TopicPage: loading topics message',
+ id: 'uUIgCr',
+ }),
+ thematicsListTitle: intl.formatMessage({
+ defaultMessage: 'Related thematics',
+ description: 'TopicPage: related thematics list widget title',
+ id: '/sRqPT',
+ }),
+ tocTitle: intl.formatMessage({
+ defaultMessage: 'Table of Contents',
+ description: 'PageLayout: table of contents title',
+ id: 'eys2uX',
+ }),
+ topicsListTitle: intl.formatMessage({
+ defaultMessage: 'Other topics',
+ description: 'TopicPage: other topics list widget title',
+ id: 'JpC3JH',
+ }),
+ },
+ browsePostsTitle: intl.formatMessage(
+ {
+ defaultMessage: 'Browse posts in {topicName} topic',
+ description: 'TopicPage: posts list heading',
+ id: 'd+DOFQ',
+ },
+ { topicName: title }
+ ),
+ };
- const getPageHeading = () => (
- <>
- {cover ? <NextImage {...cover} className={styles['topic-logo']} /> : null}
- {title}
- </>
- );
- const pageUrl = `${CONFIG.url}${asPath}`;
+ const pageUrl = `${CONFIG.url}${slug}`;
+ const browsePostHeadingId = slugify(messages.browsePostsTitle);
return (
<Page breadcrumbs={breadcrumbItems}>
@@ -129,7 +170,14 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({
type="application/ld+json"
/>
<PageHeader
- heading={getPageHeading()}
+ heading={
+ <>
+ {cover ? (
+ <NextImage {...cover} className={styles['topic-logo']} />
+ ) : null}
+ {title}
+ </>
+ }
intro={intro}
meta={{
publicationDate: dates.publication,
@@ -138,23 +186,33 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({
website: officialWebsite,
}}
/>
- <PageBody className={styles.body}>
- {/*eslint-disable-next-line react/no-danger -- Necessary for content*/}
- {content ? <div dangerouslySetInnerHTML={{ __html: content }} /> : null}
+ <PageSidebar>
+ <TocWidget
+ heading={<Heading level={2}>{messages.widgets.tocTitle}</Heading>}
+ tree={[
+ ...tree,
+ {
+ children: [],
+ depth: 2,
+ id: browsePostHeadingId,
+ label: messages.browsePostsTitle,
+ },
+ ]}
+ />
+ </PageSidebar>
+ <PageBody>
+ <div
+ className={styles.body}
+ // eslint-disable-next-line react/no-danger -- Necessary for content
+ dangerouslySetInnerHTML={{ __html: content }}
+ ref={ref}
+ />
{articles ? (
<>
- <Heading level={2}>
- {intl.formatMessage(
- {
- defaultMessage: 'All posts in {topicName}',
- description: 'TopicPage: posts list heading',
- id: 'zEN3fd',
- },
- { topicName: title }
- )}
+ <Heading id={browsePostHeadingId} level={2}>
+ {messages.browsePostsTitle}
</Heading>
<PostsList
- className={styles.list}
posts={getPostsWithUrl(articles)}
headingLvl={3}
sortByYear
@@ -166,21 +224,25 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({
{relatedThematics ? (
<LinksWidget
heading={
- <Heading isFake level={3}>
- {thematicsListTitle}
- </Heading>
+ <Heading level={2}>{messages.widgets.thematicsListTitle}</Heading>
}
items={getLinksItemData(relatedThematics)}
/>
) : null}
- <LinksWidget
- heading={
- <Heading isFake level={3}>
- {topicsListTitle}
- </Heading>
- }
- items={getLinksItemData(topics)}
- />
+ {areTopicsLoading ? (
+ <Spinner>{messages.widgets.loadingTopicsList}</Spinner>
+ ) : (
+ <LinksWidget
+ heading={
+ <Heading level={2}>{messages.widgets.topicsListTitle}</Heading>
+ }
+ items={getLinksItemData(
+ topics.edges.map((edge) =>
+ convertWPTopicPreviewToPageLink(edge.node)
+ )
+ )}
+ />
+ )}
</PageSidebar>
</Page>
);
@@ -198,23 +260,19 @@ export const getStaticProps: GetStaticProps<TopicPageProps> = async ({
}) => {
const currentTopic = await fetchTopic((params as TopicParams).slug);
const totalTopics = await fetchTopicsCount();
- const allTopicsEdges = await fetchTopicsList({
+ const otherTopics = await fetchTopicsList({
first: totalTopics,
+ where: { notIn: [currentTopic.databaseId] },
});
- const allTopics = allTopicsEdges.edges.map((edge) =>
- convertWPTopicPreviewToPageLink(edge.node)
- );
- const topicsLinks = allTopics.filter(
- (topic) => topic.url !== `${ROUTES.TOPICS}/${(params as TopicParams).slug}`
- );
const translation = await loadTranslation(locale);
return {
props: {
- currentTopic: JSON.parse(
- JSON.stringify(convertWPTopicToTopic(currentTopic))
- ),
- topics: JSON.parse(JSON.stringify(topicsLinks)),
+ data: {
+ currentTopic: JSON.parse(JSON.stringify(currentTopic)),
+ otherTopics: JSON.parse(JSON.stringify(otherTopics)),
+ totalTopics,
+ },
translation,
},
};