From 11e3ee75fcab0ab54b2bc1713a402c5cc3070c2d Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Fri, 1 Dec 2023 17:59:30 +0100 Subject: refactor(pages): refine Topic pages * add useTopic and useTopicsList hooks to refresh data * add a table of contents * add Cypress tests --- src/pages/sujet/[slug].tsx | 202 +++++++++++++++++++++++++++++---------------- 1 file changed, 130 insertions(+), 72 deletions(-) (limited to 'src/pages') 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; + totalTopics: number; + }; translation: Messages; }; -const TopicPage: NextPageWithLayout = ({ - currentTopic, - topics, -}) => { - const { content, intro, meta, slug, title } = currentTopic; +const TopicPage: NextPageWithLayout = ({ 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 ; + + const { content, intro, meta, slug, title } = topic; const { articles, cover, @@ -57,17 +88,11 @@ const TopicPage: NextPageWithLayout = ({ 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 = ({ 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 ? : null} - {title} - - ); - const pageUrl = `${CONFIG.url}${asPath}`; + const pageUrl = `${CONFIG.url}${slug}`; + const browsePostHeadingId = slugify(messages.browsePostsTitle); return ( @@ -129,7 +170,14 @@ const TopicPage: NextPageWithLayout = ({ type="application/ld+json" /> + {cover ? ( + + ) : null} + {title} + + } intro={intro} meta={{ publicationDate: dates.publication, @@ -138,23 +186,33 @@ const TopicPage: NextPageWithLayout = ({ website: officialWebsite, }} /> - - {/*eslint-disable-next-line react/no-danger -- Necessary for content*/} - {content ?
: null} + + {messages.widgets.tocTitle}} + tree={[ + ...tree, + { + children: [], + depth: 2, + id: browsePostHeadingId, + label: messages.browsePostsTitle, + }, + ]} + /> + + +
{articles ? ( <> - - {intl.formatMessage( - { - defaultMessage: 'All posts in {topicName}', - description: 'TopicPage: posts list heading', - id: 'zEN3fd', - }, - { topicName: title } - )} + + {messages.browsePostsTitle} = ({ {relatedThematics ? ( - {thematicsListTitle} - + {messages.widgets.thematicsListTitle} } items={getLinksItemData(relatedThematics)} /> ) : null} - - {topicsListTitle} - - } - items={getLinksItemData(topics)} - /> + {areTopicsLoading ? ( + {messages.widgets.loadingTopicsList} + ) : ( + {messages.widgets.topicsListTitle} + } + items={getLinksItemData( + topics.edges.map((edge) => + convertWPTopicPreviewToPageLink(edge.node) + ) + )} + /> + )} ); @@ -198,23 +260,19 @@ export const getStaticProps: GetStaticProps = 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, }, }; -- cgit v1.2.3