diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-11-29 12:28:03 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-29 13:47:07 +0100 |
| commit | dfa894b76ee3584bf169710c78c57330c5d6ee67 (patch) | |
| tree | 41380a93e2838452236a720f27f85fb14502f56a | |
| parent | 29a1dec4de0aa7ba64ef068a83b1b8589fbc3ad0 (diff) | |
fix(pages,services): make thematics & topics pages usable again
When I refactored the fetchers and convertors in #f111685 I forgot to
convert WPThematicPreview and WPTopicPreview so the thematics and
topics pages was broken.
I also:
* removed the ToC added by error in #70b4f63
* fix heading styles
* fix website url in topics pages
20 files changed, 815 insertions, 72 deletions
diff --git a/src/components/templates/page/page-header.tsx b/src/components/templates/page/page-header.tsx index 6effc9e..9694c68 100644 --- a/src/components/templates/page/page-header.tsx +++ b/src/components/templates/page/page-header.tsx @@ -153,7 +153,7 @@ const PageHeaderWithRef: ForwardRefRenderFunction< description: 'PageHeader: website meta label', id: '9jh0r2', })} - value={meta.website} + value={<Link href={meta.website}>{meta.website}</Link>} /> ) : null} </MetaList> diff --git a/src/pages/404.tsx b/src/pages/404.tsx index 5f4f89d..a98931f 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -18,7 +18,8 @@ import { type SearchFormSubmit, } from '../components'; import { - convertTaxonomyToPageLink, + convertWPThematicPreviewToPageLink, + convertWPTopicPreviewToPageLink, fetchThematicsCount, fetchThematicsList, fetchTopicsCount, @@ -147,7 +148,9 @@ const Error404Page: NextPageWithLayout<Error404PageProps> = ({ {thematicsListTitle} </Heading> } - items={getLinksItemData(thematicsList.map(convertTaxonomyToPageLink))} + items={getLinksItemData( + thematicsList.map(convertWPThematicPreviewToPageLink) + )} /> <LinksWidget heading={ @@ -155,7 +158,9 @@ const Error404Page: NextPageWithLayout<Error404PageProps> = ({ {topicsListTitle} </Heading> } - items={getLinksItemData(topicsList.map(convertTaxonomyToPageLink))} + items={getLinksItemData( + topicsList.map(convertWPTopicPreviewToPageLink) + )} /> </PageSidebar> </Page> diff --git a/src/pages/blog/index.tsx b/src/pages/blog/index.tsx index 56cbb02..12bc03e 100644 --- a/src/pages/blog/index.tsx +++ b/src/pages/blog/index.tsx @@ -20,7 +20,8 @@ import { PageSidebar, } from '../../components'; import { - convertTaxonomyToPageLink, + convertWPThematicPreviewToPageLink, + convertWPTopicPreviewToPageLink, fetchPostsCount, fetchPostsList, fetchThematicsCount, @@ -265,7 +266,9 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({ {thematicsListTitle} </Heading> } - items={getLinksItemData(thematicsList.map(convertTaxonomyToPageLink))} + items={getLinksItemData( + thematicsList.map(convertWPThematicPreviewToPageLink) + )} /> <LinksWidget heading={ @@ -273,7 +276,9 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({ {topicsListTitle} </Heading> } - items={getLinksItemData(topicsList.map(convertTaxonomyToPageLink))} + items={getLinksItemData( + topicsList.map(convertWPTopicPreviewToPageLink) + )} /> </PageSidebar> </Page> diff --git a/src/pages/blog/page/[number].tsx b/src/pages/blog/page/[number].tsx index d6071d1..35d4bad 100644 --- a/src/pages/blog/page/[number].tsx +++ b/src/pages/blog/page/[number].tsx @@ -20,7 +20,8 @@ import { PageSidebar, } from '../../../components'; import { - convertTaxonomyToPageLink, + convertWPThematicPreviewToPageLink, + convertWPTopicPreviewToPageLink, fetchLastPostCursor, fetchPostsCount, fetchPostsList, @@ -247,7 +248,9 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({ {thematicsListTitle} </Heading> } - items={getLinksItemData(thematicsList.map(convertTaxonomyToPageLink))} + items={getLinksItemData( + thematicsList.map(convertWPThematicPreviewToPageLink) + )} /> <LinksWidget heading={ @@ -255,7 +258,9 @@ const BlogPage: NextPageWithLayout<BlogPageProps> = ({ {topicsListTitle} </Heading> } - items={getLinksItemData(topicsList.map(convertTaxonomyToPageLink))} + items={getLinksItemData( + topicsList.map(convertWPTopicPreviewToPageLink) + )} /> </PageSidebar> </Page> diff --git a/src/pages/recherche/index.tsx b/src/pages/recherche/index.tsx index 293df0e..bb3aa53 100644 --- a/src/pages/recherche/index.tsx +++ b/src/pages/recherche/index.tsx @@ -20,7 +20,8 @@ import { PageBody, } from '../../components'; import { - convertTaxonomyToPageLink, + convertWPThematicPreviewToPageLink, + convertWPTopicPreviewToPageLink, fetchPostsCount, fetchPostsList, fetchThematicsCount, @@ -253,7 +254,9 @@ const SearchPage: NextPageWithLayout<SearchPageProps> = ({ {thematicsListTitle} </Heading> } - items={getLinksItemData(thematicsList.map(convertTaxonomyToPageLink))} + items={getLinksItemData( + thematicsList.map(convertWPThematicPreviewToPageLink) + )} /> <LinksWidget heading={ @@ -261,7 +264,9 @@ const SearchPage: NextPageWithLayout<SearchPageProps> = ({ {topicsListTitle} </Heading> } - items={getLinksItemData(topicsList.map(convertTaxonomyToPageLink))} + items={getLinksItemData( + topicsList.map(convertWPTopicPreviewToPageLink) + )} /> </PageSidebar> </Page> diff --git a/src/pages/sujet/[slug].tsx b/src/pages/sujet/[slug].tsx index c63906f..483df48 100644 --- a/src/pages/sujet/[slug].tsx +++ b/src/pages/sujet/[slug].tsx @@ -14,11 +14,11 @@ import { Page, PageHeader, PageSidebar, - TocWidget, PageBody, } from '../../components'; import { - convertTaxonomyToPageLink, + convertWPTopicPreviewToPageLink, + convertWPTopicToTopic, fetchAllTopicsSlugs, fetchTopic, fetchTopicsCount, @@ -36,7 +36,7 @@ import { getWebPageSchema, } from '../../utils/helpers'; import { loadTranslation, type Messages } from '../../utils/helpers/server'; -import { useBreadcrumb, useHeadingsTree } from '../../utils/hooks'; +import { useBreadcrumb } from '../../utils/hooks'; export type TopicPageProps = { currentTopic: Topic; @@ -54,7 +54,7 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({ cover, dates, seo, - thematics, + relatedThematics, website: officialWebsite, } = meta; const intl = useIntl(); @@ -62,7 +62,6 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({ title, url: `${ROUTES.TOPICS}/${slug}`, }); - const { ref, tree } = useHeadingsTree({ fromLevel: 2 }); const { asPath } = useRouter(); const webpageSchema = getWebPageSchema({ @@ -103,11 +102,6 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({ </> ); const pageUrl = `${CONFIG.url}${asPath}`; - const tocTitle = intl.formatMessage({ - defaultMessage: 'Table of Contents', - description: 'PageLayout: table of contents title', - id: 'eys2uX', - }); return ( <Page breadcrumbs={breadcrumbItems}> @@ -144,13 +138,7 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({ website: officialWebsite, }} /> - <PageSidebar> - <TocWidget - heading={<Heading level={3}>{tocTitle}</Heading>} - tree={tree} - /> - </PageSidebar> - <PageBody className={styles.body} ref={ref}> + <PageBody className={styles.body}> {/*eslint-disable-next-line react/no-danger -- Necessary for content*/} {content ? <div dangerouslySetInnerHTML={{ __html: content }} /> : null} {articles ? ( @@ -175,14 +163,14 @@ const TopicPage: NextPageWithLayout<TopicPageProps> = ({ ) : null} </PageBody> <PageSidebar> - {thematics ? ( + {relatedThematics ? ( <LinksWidget heading={ <Heading isFake level={3}> {thematicsListTitle} </Heading> } - items={getLinksItemData(thematics)} + items={getLinksItemData(relatedThematics)} /> ) : null} <LinksWidget @@ -214,7 +202,7 @@ export const getStaticProps: GetStaticProps<TopicPageProps> = async ({ first: totalTopics, }); const allTopics = allTopicsEdges.edges.map((edge) => - convertTaxonomyToPageLink(edge.node) + convertWPTopicPreviewToPageLink(edge.node) ); const topicsLinks = allTopics.filter( (topic) => topic.url !== `${ROUTES.TOPICS}/${(params as TopicParams).slug}` @@ -223,7 +211,9 @@ export const getStaticProps: GetStaticProps<TopicPageProps> = async ({ return { props: { - currentTopic: JSON.parse(JSON.stringify(currentTopic)), + currentTopic: JSON.parse( + JSON.stringify(convertWPTopicToTopic(currentTopic)) + ), topics: JSON.parse(JSON.stringify(topicsLinks)), translation, }, diff --git a/src/pages/thematique/[slug].tsx b/src/pages/thematique/[slug].tsx index f8c3404..9ea52e1 100644 --- a/src/pages/thematique/[slug].tsx +++ b/src/pages/thematique/[slug].tsx @@ -13,11 +13,11 @@ import { Page, PageHeader, PageSidebar, - TocWidget, PageBody, } from '../../components'; import { - convertTaxonomyToPageLink, + convertWPThematicPreviewToPageLink, + convertWPThematicToThematic, fetchAllThematicsSlugs, fetchThematic, fetchThematicsCount, @@ -35,7 +35,7 @@ import { getWebPageSchema, } from '../../utils/helpers'; import { loadTranslation, type Messages } from '../../utils/helpers/server'; -import { useBreadcrumb, useHeadingsTree } from '../../utils/hooks'; +import { useBreadcrumb } from '../../utils/hooks'; export type ThematicPageProps = { currentThematic: Thematic; @@ -48,13 +48,12 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({ thematics, }) => { const { content, intro, meta, slug, title } = currentThematic; - const { articles, dates, seo, topics } = meta; + const { articles, dates, seo, relatedTopics } = meta; const intl = useIntl(); const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({ title, url: `${ROUTES.THEMATICS.INDEX}/${slug}`, }); - const { ref, tree } = useHeadingsTree({ fromLevel: 2 }); const { asPath } = useRouter(); const webpageSchema = getWebPageSchema({ @@ -87,11 +86,6 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({ id: '/42Z0z', }); const pageUrl = `${CONFIG.url}${asPath}`; - const tocTitle = intl.formatMessage({ - defaultMessage: 'Table of Contents', - description: 'PageLayout: table of contents title', - id: 'eys2uX', - }); return ( <Page breadcrumbs={breadcrumbItems}> @@ -127,13 +121,7 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({ updateDate: dates.update, }} /> - <PageSidebar> - <TocWidget - heading={<Heading level={3}>{tocTitle}</Heading>} - tree={tree} - /> - </PageSidebar> - <PageBody className={styles.body} ref={ref}> + <PageBody className={styles.body}> {/*eslint-disable-next-line react/no-danger -- Necessary for content*/} <div dangerouslySetInnerHTML={{ __html: content }} /> {articles ? ( @@ -166,14 +154,14 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({ } items={getLinksItemData(thematics)} /> - {topics ? ( + {relatedTopics ? ( <LinksWidget heading={ <Heading isFake level={3}> {topicsListTitle} </Heading> } - items={getLinksItemData(topics)} + items={getLinksItemData(relatedTopics)} /> ) : null} </PageSidebar> @@ -197,7 +185,7 @@ export const getStaticProps: GetStaticProps<ThematicPageProps> = async ({ first: totalThematics, }); const allThematics = allThematicsEdges.edges.map((edge) => - convertTaxonomyToPageLink(edge.node) + convertWPThematicPreviewToPageLink(edge.node) ); const allThematicsLinks = allThematics.filter( (thematic) => @@ -208,7 +196,9 @@ export const getStaticProps: GetStaticProps<ThematicPageProps> = async ({ return { props: { - currentThematic: JSON.parse(JSON.stringify(currentThematic)), + currentThematic: JSON.parse( + JSON.stringify(convertWPThematicToThematic(currentThematic)) + ), thematics: JSON.parse(JSON.stringify(allThematicsLinks)), translation, }, diff --git a/src/services/graphql/helpers/convert-post-preview-to-article-preview.test.ts b/src/services/graphql/helpers/convert-post-preview-to-article-preview.test.ts index c13684f..cb14fdb 100644 --- a/src/services/graphql/helpers/convert-post-preview-to-article-preview.test.ts +++ b/src/services/graphql/helpers/convert-post-preview-to-article-preview.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from '@jest/globals'; import type { WPPostPreview, WPThematicPreview } from '../../../types'; import { convertPostPreviewToArticlePreview } from './convert-post-preview-to-article-preview'; -import { convertTaxonomyToPageLink } from './convert-taxonomy-to-page-link'; +import { convertWPThematicPreviewToPageLink } from './convert-taxonomy-to-page-link'; import { convertWPImgToImg } from './convert-wp-image-to-img'; describe('convert-post-preview-to-article-preview', () => { @@ -124,7 +124,7 @@ describe('convert-post-preview-to-article-preview', () => { const result = convertPostPreviewToArticlePreview(post); expect(result.meta.thematics).toStrictEqual( - thematics.map(convertTaxonomyToPageLink) + thematics.map(convertWPThematicPreviewToPageLink) ); }); }); diff --git a/src/services/graphql/helpers/convert-post-preview-to-article-preview.ts b/src/services/graphql/helpers/convert-post-preview-to-article-preview.ts index 78777eb..1998d79 100644 --- a/src/services/graphql/helpers/convert-post-preview-to-article-preview.ts +++ b/src/services/graphql/helpers/convert-post-preview-to-article-preview.ts @@ -1,5 +1,5 @@ import type { ArticlePreview, WPPostPreview } from '../../../types'; -import { convertTaxonomyToPageLink } from './convert-taxonomy-to-page-link'; +import { convertWPThematicPreviewToPageLink } from './convert-taxonomy-to-page-link'; import { convertWPImgToImg } from './convert-wp-image-to-img'; export const convertPostPreviewToArticlePreview = ({ @@ -26,7 +26,7 @@ export const convertPostPreviewToArticlePreview = ({ }, thematics: acfPosts && 'postsInThematic' in acfPosts - ? acfPosts.postsInThematic?.map(convertTaxonomyToPageLink) + ? acfPosts.postsInThematic?.map(convertWPThematicPreviewToPageLink) : undefined, wordsCount: info.wordsCount, }, diff --git a/src/services/graphql/helpers/convert-post-to-article.ts b/src/services/graphql/helpers/convert-post-to-article.ts index b540a77..383dc47 100644 --- a/src/services/graphql/helpers/convert-post-to-article.ts +++ b/src/services/graphql/helpers/convert-post-to-article.ts @@ -1,6 +1,9 @@ import type { Article, WPPost } from '../../../types'; import { updateContentTree } from '../../../utils/helpers'; -import { convertTaxonomyToPageLink } from './convert-taxonomy-to-page-link'; +import { + convertWPThematicPreviewToPageLink, + convertWPTopicPreviewToPageLink, +} from './convert-taxonomy-to-page-link'; import { convertWPImgToImg } from './convert-wp-image-to-img'; export const convertPostToArticle = async ({ @@ -33,8 +36,10 @@ export const convertPostToArticle = async ({ description: seo.metaDesc, title: seo.title, }, - thematics: acfPosts?.postsInThematic?.map(convertTaxonomyToPageLink), - topics: acfPosts?.postsInTopic?.map(convertTaxonomyToPageLink), + thematics: acfPosts?.postsInThematic?.map( + convertWPThematicPreviewToPageLink + ), + topics: acfPosts?.postsInTopic?.map(convertWPTopicPreviewToPageLink), wordsCount: info.wordsCount, }, slug, diff --git a/src/services/graphql/helpers/convert-taxonomy-to-page-link.test.ts b/src/services/graphql/helpers/convert-taxonomy-to-page-link.test.ts index b687ccb..54a62ad 100644 --- a/src/services/graphql/helpers/convert-taxonomy-to-page-link.test.ts +++ b/src/services/graphql/helpers/convert-taxonomy-to-page-link.test.ts @@ -1,6 +1,10 @@ import { describe, expect, it } from '@jest/globals'; import type { WPThematicPreview, WPTopicPreview } from '../../../types'; -import { convertTaxonomyToPageLink } from './convert-taxonomy-to-page-link'; +import { ROUTES } from '../../../utils/constants'; +import { + convertWPThematicPreviewToPageLink, + convertWPTopicPreviewToPageLink, +} from './convert-taxonomy-to-page-link'; describe('convert-taxonomy-to-page-link', () => { it('can convert a WPThematicPreview object to a Thematic object', () => { @@ -9,12 +13,12 @@ describe('convert-taxonomy-to-page-link', () => { slug: '/the-thematic-slug', title: 'et ut alias', }; - const result = convertTaxonomyToPageLink(thematic); + const result = convertWPThematicPreviewToPageLink(thematic); expect(result.id).toBe(thematic.databaseId); expect(result.logo).toBeUndefined(); expect(result.name).toBe(thematic.title); - expect(result.url).toBe(thematic.slug); + expect(result.url).toBe(`${ROUTES.THEMATICS.INDEX}/${thematic.slug}`); }); it('can convert a WPTopicPreview object to a Topic object', () => { @@ -34,7 +38,7 @@ describe('convert-taxonomy-to-page-link', () => { slug: '/the-topic-slug', title: 'et ut alias', }; - const result = convertTaxonomyToPageLink(topic); + const result = convertWPTopicPreviewToPageLink(topic); expect(result.id).toBe(topic.databaseId); expect(result.logo?.alt).toBe(topic.featuredImage?.node.altText); @@ -47,6 +51,6 @@ describe('convert-taxonomy-to-page-link', () => { topic.featuredImage?.node.mediaDetails.width ); expect(result.name).toBe(topic.title); - expect(result.url).toBe(topic.slug); + expect(result.url).toBe(`${ROUTES.TOPICS}/${topic.slug}`); }); }); diff --git a/src/services/graphql/helpers/convert-taxonomy-to-page-link.ts b/src/services/graphql/helpers/convert-taxonomy-to-page-link.ts index 2294fb7..9b42eea 100644 --- a/src/services/graphql/helpers/convert-taxonomy-to-page-link.ts +++ b/src/services/graphql/helpers/convert-taxonomy-to-page-link.ts @@ -3,9 +3,10 @@ import type { WPThematicPreview, WPTopicPreview, } from '../../../types'; +import { ROUTES } from '../../../utils/constants'; import { convertWPImgToImg } from './convert-wp-image-to-img'; -export const convertTaxonomyToPageLink = ({ +const convertTaxonomyToPageLink = ({ databaseId, slug, title, @@ -21,3 +22,19 @@ export const convertTaxonomyToPageLink = ({ url: slug, }; }; + +export const convertWPThematicPreviewToPageLink = ( + thematic: WPThematicPreview +): PageLink => + convertTaxonomyToPageLink({ + ...thematic, + slug: `${ROUTES.THEMATICS.INDEX}/${thematic.slug}`, + }); + +export const convertWPTopicPreviewToPageLink = ( + topic: WPTopicPreview +): PageLink => + convertTaxonomyToPageLink({ + ...topic, + slug: `${ROUTES.TOPICS}/${topic.slug}`, + }); diff --git a/src/services/graphql/helpers/convert-wp-thematic-to-thematic.test.ts b/src/services/graphql/helpers/convert-wp-thematic-to-thematic.test.ts new file mode 100644 index 0000000..e535a21 --- /dev/null +++ b/src/services/graphql/helpers/convert-wp-thematic-to-thematic.test.ts @@ -0,0 +1,261 @@ +import { describe, expect, it } from '@jest/globals'; +import type { WPThematic } from '../../../types'; +import { ROUTES } from '../../../utils/constants'; +import { + getUniquePageLinks, + sortPageLinksByName, +} from '../../../utils/helpers'; +import { convertPostPreviewToArticlePreview } from './convert-post-preview-to-article-preview'; +import { convertWPTopicPreviewToPageLink } from './convert-taxonomy-to-page-link'; +import { convertWPThematicToThematic } from './convert-wp-thematic-to-thematic'; + +describe('convert-wp-thematic-to-thematic', () => { + /* eslint-disable max-statements */ + it('converts a WPThematic object to a Thematic object', () => { + const thematic = { + acfThematics: null, + contentParts: { + afterMore: + 'Repellat eius adipisci et voluptate fugit enim aut aut recusandae. In sit quisquam est rerum molestiae quos quaerat repellat totam. Porro reiciendis sed adipisci dolore impedit et.', + beforeMore: + 'Id maxime illo laborum laborum. Ab culpa voluptatem non qui provident adipisci corrupti eius. Delectus facere praesentium. Debitis recusandae nemo ut velit.', + }, + databaseId: 1, + date: '2023-11-27', + featuredImage: null, + modified: '2023-11-27', + seo: { + metaDesc: 'Nemo illum veniam aliquam.', + title: 'qui id cupiditate', + }, + slug: '/sequi-veritatis-earum', + title: 'sequi veritatis earum', + } satisfies WPThematic; + const result = convertWPThematicToThematic(thematic); + + expect(result.content).toBe(thematic.contentParts.afterMore); + expect(result.intro).toBe(thematic.contentParts.beforeMore); + expect(result.meta.articles).toBeUndefined(); + expect(result.meta.cover).toBeUndefined(); + expect(result.meta.dates.publication).toBe(thematic.date); + expect(result.meta.dates.update).toBe(thematic.modified); + expect(result.meta.seo.description).toBe(thematic.seo.metaDesc); + expect(result.meta.seo.title).toBe(thematic.seo.title); + expect(result.meta.relatedTopics).toBeUndefined(); + expect(result.slug).toBe(`${ROUTES.THEMATICS.INDEX}/${thematic.slug}`); + expect(result.title).toBe(thematic.title); + }); + /* eslint-enable max-statements */ + + it('can convert the cover', () => { + const thematic = { + acfThematics: null, + contentParts: { + afterMore: + 'Repellat eius adipisci et voluptate fugit enim aut aut recusandae. In sit quisquam est rerum molestiae quos quaerat repellat totam. Porro reiciendis sed adipisci dolore impedit et.', + beforeMore: + 'Id maxime illo laborum laborum. Ab culpa voluptatem non qui provident adipisci corrupti eius. Delectus facere praesentium. Debitis recusandae nemo ut velit.', + }, + databaseId: 1, + date: '2023-11-27', + featuredImage: { + node: { + altText: 'expedita commodi placeat', + mediaDetails: { + height: 480, + width: 640, + }, + sourceUrl: 'https://picsum.photos/640/480', + title: 'et sint et', + }, + }, + modified: '2023-11-27', + seo: { + metaDesc: 'Nemo illum veniam aliquam.', + title: 'qui id cupiditate', + }, + slug: '/sequi-veritatis-earum', + title: 'sequi veritatis earum', + } satisfies WPThematic; + const result = convertWPThematicToThematic(thematic); + + expect(result.meta.cover?.alt).toBe(thematic.featuredImage.node.altText); + expect(result.meta.cover?.height).toBe( + thematic.featuredImage.node.mediaDetails.height + ); + expect(result.meta.cover?.src).toBe(thematic.featuredImage.node.sourceUrl); + expect(result.meta.cover?.title).toBe(thematic.featuredImage.node.title); + expect(result.meta.cover?.width).toBe( + thematic.featuredImage.node.mediaDetails.width + ); + }); + + it('can convert the articles', () => { + const thematic = { + acfThematics: { + postsInThematic: [ + { + acfPosts: null, + commentCount: 2, + contentParts: { + beforeMore: + 'Iste cupiditate natus esse ut et ut nihil excepturi. Blanditiis optio sit et velit vel nobis iste hic aspernatur. Labore doloremque facere nulla provident aspernatur qui incidunt.', + }, + databaseId: 8, + date: '2023-09-20', + featuredImage: null, + info: { + wordsCount: 250, + }, + modified: '2023-09-21', + slug: '/quam-voluptatem-quos', + title: 'quam voluptatem quos', + }, + { + acfPosts: null, + commentCount: 7, + contentParts: { + beforeMore: + 'Dolorum sit consectetur inventore exercitationem. Natus quam corporis ut aut rerum nemo architecto quia odit. Voluptate eos commodi voluptas totam dolorum.', + }, + databaseId: 13, + date: '2023-10-12', + featuredImage: null, + info: { + wordsCount: 356, + }, + modified: '2023-10-15', + slug: '/ipsa-dolorem-repellendus', + title: 'ipsa dolorem repellendus', + }, + ], + }, + contentParts: { + afterMore: + 'Repellat eius adipisci et voluptate fugit enim aut aut recusandae. In sit quisquam est rerum molestiae quos quaerat repellat totam. Porro reiciendis sed adipisci dolore impedit et.', + beforeMore: + 'Id maxime illo laborum laborum. Ab culpa voluptatem non qui provident adipisci corrupti eius. Delectus facere praesentium. Debitis recusandae nemo ut velit.', + }, + databaseId: 1, + date: '2023-11-27', + featuredImage: null, + modified: '2023-11-27', + seo: { + metaDesc: 'Nemo illum veniam aliquam.', + title: 'qui id cupiditate', + }, + slug: '/sequi-veritatis-earum', + title: 'sequi veritatis earum', + } satisfies WPThematic; + const result = convertWPThematicToThematic(thematic); + + expect(result.meta.articles).toStrictEqual( + thematic.acfThematics.postsInThematic.map( + convertPostPreviewToArticlePreview + ) + ); + }); + + it('can retrieve the related topics from the articles', () => { + const thematic = { + acfThematics: { + postsInThematic: [ + { + acfPosts: { + postsInTopic: [ + { + databaseId: 4, + featuredImage: null, + slug: '/voluptas-sit-ut', + title: 'voluptas sit ut', + }, + { + databaseId: 9, + featuredImage: null, + slug: '/ex-omnis-voluptas', + title: 'ex omnis voluptas', + }, + ], + }, + commentCount: 2, + contentParts: { + beforeMore: + 'Iste cupiditate natus esse ut et ut nihil excepturi. Blanditiis optio sit et velit vel nobis iste hic aspernatur. Labore doloremque facere nulla provident aspernatur qui incidunt.', + }, + databaseId: 8, + date: '2023-09-20', + featuredImage: null, + info: { + wordsCount: 250, + }, + modified: '2023-09-21', + slug: '/quam-voluptatem-quos', + title: 'quam voluptatem quos', + }, + { + acfPosts: { + postsInTopic: [ + { + databaseId: 9, + featuredImage: null, + slug: '/ex-omnis-voluptas', + title: 'ex omnis voluptas', + }, + { + databaseId: 11, + featuredImage: null, + slug: '/dicta-quisquam-asperiores', + title: 'dicta quisquam asperiores', + }, + { + databaseId: 22, + featuredImage: null, + slug: '/consectetur-laudantium-illum', + title: 'consectetur laudantium illum', + }, + ], + }, + commentCount: 7, + contentParts: { + beforeMore: + 'Dolorum sit consectetur inventore exercitationem. Natus quam corporis ut aut rerum nemo architecto quia odit. Voluptate eos commodi voluptas totam dolorum.', + }, + databaseId: 13, + date: '2023-10-12', + featuredImage: null, + info: { + wordsCount: 356, + }, + modified: '2023-10-15', + slug: '/ipsa-dolorem-repellendus', + title: 'ipsa dolorem repellendus', + }, + ], + }, + contentParts: { + afterMore: + 'Repellat eius adipisci et voluptate fugit enim aut aut recusandae. In sit quisquam est rerum molestiae quos quaerat repellat totam. Porro reiciendis sed adipisci dolore impedit et.', + beforeMore: + 'Id maxime illo laborum laborum. Ab culpa voluptatem non qui provident adipisci corrupti eius. Delectus facere praesentium. Debitis recusandae nemo ut velit.', + }, + databaseId: 1, + date: '2023-11-27', + featuredImage: null, + modified: '2023-11-27', + seo: { + metaDesc: 'Nemo illum veniam aliquam.', + title: 'qui id cupiditate', + }, + slug: '/sequi-veritatis-earum', + title: 'sequi veritatis earum', + } satisfies WPThematic; + const result = convertWPThematicToThematic(thematic); + const topics = thematic.acfThematics.postsInThematic.flatMap((post) => + post.acfPosts.postsInTopic.map(convertWPTopicPreviewToPageLink) + ); + const uniqueThematics = + getUniquePageLinks(topics).sort(sortPageLinksByName); + + expect(result.meta.relatedTopics).toStrictEqual(uniqueThematics); + }); +}); diff --git a/src/services/graphql/helpers/convert-wp-thematic-to-thematic.ts b/src/services/graphql/helpers/convert-wp-thematic-to-thematic.ts new file mode 100644 index 0000000..cabfa18 --- /dev/null +++ b/src/services/graphql/helpers/convert-wp-thematic-to-thematic.ts @@ -0,0 +1,60 @@ +import type { + PageLink, + Thematic, + WPPostPreview, + WPThematic, +} from '../../../types'; +import { ROUTES } from '../../../utils/constants'; +import { + getUniquePageLinks, + sortPageLinksByName, +} from '../../../utils/helpers'; +import { convertPostPreviewToArticlePreview } from './convert-post-preview-to-article-preview'; +import { convertWPTopicPreviewToPageLink } from './convert-taxonomy-to-page-link'; +import { convertWPImgToImg } from './convert-wp-image-to-img'; + +const getRelatedTopicsFrom = (posts: WPPostPreview[]): PageLink[] => { + const topics: PageLink[] = []; + + for (const post of posts) { + if ( + post.acfPosts && + 'postsInTopic' in post.acfPosts && + post.acfPosts.postsInTopic + ) { + topics.push( + ...post.acfPosts.postsInTopic.map(convertWPTopicPreviewToPageLink) + ); + } + } + + return getUniquePageLinks(topics).sort(sortPageLinksByName); +}; + +export const convertWPThematicToThematic = (thematic: WPThematic): Thematic => { + return { + content: thematic.contentParts.afterMore, + intro: thematic.contentParts.beforeMore, + meta: { + articles: thematic.acfThematics?.postsInThematic?.map( + convertPostPreviewToArticlePreview + ), + cover: thematic.featuredImage + ? convertWPImgToImg(thematic.featuredImage.node) + : undefined, + dates: { + publication: thematic.date, + update: thematic.modified, + }, + seo: { + description: thematic.seo.metaDesc, + title: thematic.seo.title, + }, + relatedTopics: thematic.acfThematics?.postsInThematic + ? getRelatedTopicsFrom(thematic.acfThematics.postsInThematic) + : undefined, + }, + slug: `${ROUTES.THEMATICS.INDEX}/${thematic.slug}`, + title: thematic.title, + }; +}; diff --git a/src/services/graphql/helpers/convert-wp-topic-to-topic.test.ts b/src/services/graphql/helpers/convert-wp-topic-to-topic.test.ts new file mode 100644 index 0000000..bfe2ba9 --- /dev/null +++ b/src/services/graphql/helpers/convert-wp-topic-to-topic.test.ts @@ -0,0 +1,280 @@ +import { describe, expect, it } from '@jest/globals'; +import type { WPTopic } from '../../../types'; +import { ROUTES } from '../../../utils/constants'; +import { + getUniquePageLinks, + sortPageLinksByName, +} from '../../../utils/helpers'; +import { convertPostPreviewToArticlePreview } from './convert-post-preview-to-article-preview'; +import { convertWPThematicPreviewToPageLink } from './convert-taxonomy-to-page-link'; +import { convertWPTopicToTopic } from './convert-wp-topic-to-topic'; + +describe('convert-wp-topic-to-topic', () => { + /* eslint-disable max-statements */ + it('converts a WPTopic object to a Topic object', () => { + const topic = { + acfTopics: null, + contentParts: { + afterMore: + 'Sit quam officia officia ea hic. Velit architecto dignissimos sint est rerum praesentium ad ut. Dicta eligendi tenetur iure quis consequatur alias sit est voluptatibus. Vel ullam hic. Assumenda nisi voluptatum est. Molestiae odit consequatur qui enim itaque.', + beforeMore: + 'Aut aut ut. Et laboriosam et id expedita. Laudantium corporis placeat.', + }, + databaseId: 1, + date: '2023-11-27', + featuredImage: null, + modified: '2023-11-27', + seo: { + metaDesc: 'Earum qui tenetur a.', + title: 'est eligendi placeat', + }, + slug: '/debitis-nulla-sapiente', + title: 'debitis nulla sapiente', + } satisfies WPTopic; + const result = convertWPTopicToTopic(topic); + + expect(result.content).toBe(topic.contentParts.afterMore); + expect(result.intro).toBe(topic.contentParts.beforeMore); + expect(result.meta.articles).toBeUndefined(); + expect(result.meta.cover).toBeUndefined(); + expect(result.meta.dates.publication).toBe(topic.date); + expect(result.meta.dates.update).toBe(topic.modified); + expect(result.meta.seo.description).toBe(topic.seo.metaDesc); + expect(result.meta.seo.title).toBe(topic.seo.title); + expect(result.meta.relatedThematics).toBeUndefined(); + expect(result.meta.website).toBeUndefined(); + expect(result.slug).toBe(`${ROUTES.TOPICS}/${topic.slug}`); + expect(result.title).toBe(topic.title); + }); + /* eslint-enable max-statements */ + + it('can convert the cover', () => { + const topic = { + acfTopics: null, + contentParts: { + afterMore: + 'Sit quam officia officia ea hic. Velit architecto dignissimos sint est rerum praesentium ad ut. Dicta eligendi tenetur iure quis consequatur alias sit est voluptatibus. Vel ullam hic. Assumenda nisi voluptatum est. Molestiae odit consequatur qui enim itaque.', + beforeMore: + 'Aut aut ut. Et laboriosam et id expedita. Laudantium corporis placeat.', + }, + databaseId: 1, + date: '2023-11-27', + featuredImage: { + node: { + altText: 'rem omnis nulla', + mediaDetails: { + height: 480, + width: 640, + }, + sourceUrl: 'https://picsum.photos/640/480', + title: 'earum eos non', + }, + }, + modified: '2023-11-27', + seo: { + metaDesc: 'Earum qui tenetur a.', + title: 'est eligendi placeat', + }, + slug: '/debitis-nulla-sapiente', + title: 'debitis nulla sapiente', + } satisfies WPTopic; + const result = convertWPTopicToTopic(topic); + + expect(result.meta.cover?.alt).toBe(topic.featuredImage.node.altText); + expect(result.meta.cover?.height).toBe( + topic.featuredImage.node.mediaDetails.height + ); + expect(result.meta.cover?.src).toBe(topic.featuredImage.node.sourceUrl); + expect(result.meta.cover?.title).toBe(topic.featuredImage.node.title); + expect(result.meta.cover?.width).toBe( + topic.featuredImage.node.mediaDetails.width + ); + }); + + it('can retrieve the website', () => { + const topic = { + acfTopics: { + officialWebsite: 'https://example.test', + postsInTopic: null, + }, + contentParts: { + afterMore: + 'Sit quam officia officia ea hic. Velit architecto dignissimos sint est rerum praesentium ad ut. Dicta eligendi tenetur iure quis consequatur alias sit est voluptatibus. Vel ullam hic. Assumenda nisi voluptatum est. Molestiae odit consequatur qui enim itaque.', + beforeMore: + 'Aut aut ut. Et laboriosam et id expedita. Laudantium corporis placeat.', + }, + databaseId: 1, + date: '2023-11-27', + featuredImage: null, + modified: '2023-11-27', + seo: { + metaDesc: 'Earum qui tenetur a.', + title: 'est eligendi placeat', + }, + slug: '/debitis-nulla-sapiente', + title: 'debitis nulla sapiente', + } satisfies WPTopic; + const result = convertWPTopicToTopic(topic); + + expect(result.meta.website).toBe(topic.acfTopics.officialWebsite); + }); + + it('can convert the articles', () => { + const topic = { + acfTopics: { + officialWebsite: null, + postsInTopic: [ + { + acfPosts: null, + commentCount: 4, + contentParts: { + beforeMore: + 'Dolor cupiditate nisi sed qui numquam provident cumque et. Harum nihil soluta id pariatur possimus temporibus est eligendi ex. Culpa hic consequuntur expedita culpa alias voluptatem.', + }, + databaseId: 2, + date: '2023-07-22', + featuredImage: null, + info: { + wordsCount: 412, + }, + modified: '2023-07-23', + slug: '/qui-temporibus-velit', + title: 'qui temporibus velit', + }, + { + acfPosts: null, + commentCount: 2, + contentParts: { + beforeMore: + 'Quia quis dolorem. Nobis iusto nihil omnis omnis. Et qui cum porro omnis. Omnis tempore quis adipisci sapiente nisi quod tempore porro. Facere quia ad amet accusantium ipsam autem consequatur alias.', + }, + databaseId: 6, + date: '2023-08-12', + featuredImage: null, + info: { + wordsCount: 365, + }, + modified: '2023-08-12', + slug: '/dicta-esse-et', + title: 'dicta esse et', + }, + ], + }, + contentParts: { + afterMore: + 'Sit quam officia officia ea hic. Velit architecto dignissimos sint est rerum praesentium ad ut. Dicta eligendi tenetur iure quis consequatur alias sit est voluptatibus. Vel ullam hic. Assumenda nisi voluptatum est. Molestiae odit consequatur qui enim itaque.', + beforeMore: + 'Aut aut ut. Et laboriosam et id expedita. Laudantium corporis placeat.', + }, + databaseId: 1, + date: '2023-11-27', + featuredImage: null, + modified: '2023-11-27', + seo: { + metaDesc: 'Earum qui tenetur a.', + title: 'est eligendi placeat', + }, + slug: '/debitis-nulla-sapiente', + title: 'debitis nulla sapiente', + } satisfies WPTopic; + const result = convertWPTopicToTopic(topic); + + expect(result.meta.articles).toStrictEqual( + topic.acfTopics.postsInTopic.map(convertPostPreviewToArticlePreview) + ); + }); + + it('can retrieve the related thematics from the articles', () => { + const topic = { + acfTopics: { + officialWebsite: null, + postsInTopic: [ + { + acfPosts: { + postsInThematic: [ + { + databaseId: 5, + slug: '/consequatur-est-modi', + title: 'consequatur est modi', + }, + { + databaseId: 15, + slug: '/repudiandae-est-quia', + title: 'repudiandae est quia', + }, + ], + }, + commentCount: 4, + contentParts: { + beforeMore: + 'Dolor cupiditate nisi sed qui numquam provident cumque et. Harum nihil soluta id pariatur possimus temporibus est eligendi ex. Culpa hic consequuntur expedita culpa alias voluptatem.', + }, + databaseId: 2, + date: '2023-07-22', + featuredImage: null, + info: { + wordsCount: 412, + }, + modified: '2023-07-23', + slug: '/qui-temporibus-velit', + title: 'qui temporibus velit', + }, + { + acfPosts: { + postsInThematic: [ + { + databaseId: 7, + slug: '/similique-ea-natus', + title: 'similique ea natus', + }, + { + databaseId: 15, + slug: '/repudiandae-est-quia', + title: 'repudiandae est quia', + }, + ], + }, + commentCount: 2, + contentParts: { + beforeMore: + 'Quia quis dolorem. Nobis iusto nihil omnis omnis. Et qui cum porro omnis. Omnis tempore quis adipisci sapiente nisi quod tempore porro. Facere quia ad amet accusantium ipsam autem consequatur alias.', + }, + databaseId: 6, + date: '2023-08-12', + featuredImage: null, + info: { + wordsCount: 365, + }, + modified: '2023-08-12', + slug: '/dicta-esse-et', + title: 'dicta esse et', + }, + ], + }, + contentParts: { + afterMore: + 'Sit quam officia officia ea hic. Velit architecto dignissimos sint est rerum praesentium ad ut. Dicta eligendi tenetur iure quis consequatur alias sit est voluptatibus. Vel ullam hic. Assumenda nisi voluptatum est. Molestiae odit consequatur qui enim itaque.', + beforeMore: + 'Aut aut ut. Et laboriosam et id expedita. Laudantium corporis placeat.', + }, + databaseId: 1, + date: '2023-11-27', + featuredImage: null, + modified: '2023-11-27', + seo: { + metaDesc: 'Earum qui tenetur a.', + title: 'est eligendi placeat', + }, + slug: '/debitis-nulla-sapiente', + title: 'debitis nulla sapiente', + } satisfies WPTopic; + const result = convertWPTopicToTopic(topic); + const thematics = topic.acfTopics.postsInTopic.flatMap((post) => + post.acfPosts.postsInThematic.map(convertWPThematicPreviewToPageLink) + ); + const uniqueThematics = + getUniquePageLinks(thematics).sort(sortPageLinksByName); + + expect(result.meta.relatedThematics).toStrictEqual(uniqueThematics); + }); +}); diff --git a/src/services/graphql/helpers/convert-wp-topic-to-topic.ts b/src/services/graphql/helpers/convert-wp-topic-to-topic.ts new file mode 100644 index 0000000..b0136c7 --- /dev/null +++ b/src/services/graphql/helpers/convert-wp-topic-to-topic.ts @@ -0,0 +1,56 @@ +import type { PageLink, Topic, WPPostPreview, WPTopic } from '../../../types'; +import { ROUTES } from '../../../utils/constants'; +import { + getUniquePageLinks, + sortPageLinksByName, +} from '../../../utils/helpers'; +import { convertPostPreviewToArticlePreview } from './convert-post-preview-to-article-preview'; +import { convertWPThematicPreviewToPageLink } from './convert-taxonomy-to-page-link'; +import { convertWPImgToImg } from './convert-wp-image-to-img'; + +const getRelatedThematicsFrom = (posts: WPPostPreview[]): PageLink[] => { + const thematics: PageLink[] = []; + + for (const post of posts) { + if ( + post.acfPosts && + 'postsInThematic' in post.acfPosts && + post.acfPosts.postsInThematic + ) { + thematics.push( + ...post.acfPosts.postsInThematic.map(convertWPThematicPreviewToPageLink) + ); + } + } + + return getUniquePageLinks(thematics).sort(sortPageLinksByName); +}; + +export const convertWPTopicToTopic = (topic: WPTopic): Topic => { + return { + content: topic.contentParts.afterMore, + intro: topic.contentParts.beforeMore, + meta: { + articles: topic.acfTopics?.postsInTopic?.map( + convertPostPreviewToArticlePreview + ), + cover: topic.featuredImage + ? convertWPImgToImg(topic.featuredImage.node) + : undefined, + dates: { + publication: topic.date, + update: topic.modified, + }, + seo: { + description: topic.seo.metaDesc, + title: topic.seo.title, + }, + relatedThematics: topic.acfTopics?.postsInTopic + ? getRelatedThematicsFrom(topic.acfTopics.postsInTopic) + : undefined, + website: topic.acfTopics?.officialWebsite ?? undefined, + }, + slug: `${ROUTES.TOPICS}/${topic.slug}`, + title: topic.title, + }; +}; diff --git a/src/services/graphql/helpers/index.ts b/src/services/graphql/helpers/index.ts index 16e93d2..9dae107 100644 --- a/src/services/graphql/helpers/index.ts +++ b/src/services/graphql/helpers/index.ts @@ -5,3 +5,5 @@ export * from './convert-recent-post-to-recent-article'; export * from './convert-taxonomy-to-page-link'; export * from './convert-wp-comment-to-comment'; export * from './convert-wp-image-to-img'; +export * from './convert-wp-thematic-to-thematic'; +export * from './convert-wp-topic-to-topic'; diff --git a/src/styles/pages/blog.module.scss b/src/styles/pages/blog.module.scss index 0fbde7c..e099088 100644 --- a/src/styles/pages/blog.module.scss +++ b/src/styles/pages/blog.module.scss @@ -1,5 +1,7 @@ @use "../abstracts/functions" as fun; @use "../abstracts/mixins" as mix; +@use "../abstracts/placeholders"; +@use "partials/article-links"; .list { @include mix.media("screen") { @@ -16,3 +18,51 @@ max-width: fun.convert-px(50); margin: 0 var(--spacing-xs) 0 0; } + +:where(.body) { + :global { + @include article-links.styles; + + h2 { + @extend %h2; + + margin-block-end: var(--spacing-sm); + } + } +} + +:global([data-theme="light"]) { + :local { + .body { + :global { + a { + &.download { + @extend %light-download-link; + } + + &.external { + @extend %light-external-link; + } + } + } + } + } +} + +:global([data-theme="dark"]) { + :local { + .body { + :global { + a { + &.download { + @extend %dark-download-link; + } + + &.external { + @extend %dark-external-link; + } + } + } + } + } +} diff --git a/src/types/data.ts b/src/types/data.ts index 7b9a879..21f773e 100644 --- a/src/types/data.ts +++ b/src/types/data.ts @@ -267,18 +267,18 @@ export type ProjectPreview = Omit<Project, 'meta'> & { meta: Omit<ProjectMeta, 'license' | 'repos'>; }; -export type ThematicMeta = PageMeta & { +export type ThematicMeta = Omit<PageMeta, 'wordsCount'> & { articles?: ArticlePreview[]; - topics?: PageLink[]; + relatedTopics?: PageLink[]; }; export type Thematic = Page & { meta: ThematicMeta; }; -export type TopicMeta = PageMeta & { +export type TopicMeta = Omit<PageMeta, 'wordsCount'> & { articles?: ArticlePreview[]; - thematics?: PageLink[]; + relatedThematics?: PageLink[]; website?: string; }; diff --git a/src/utils/helpers/pages.tsx b/src/utils/helpers/pages.tsx index 9e015db..24f5503 100644 --- a/src/utils/helpers/pages.tsx +++ b/src/utils/helpers/pages.tsx @@ -3,6 +3,14 @@ import type { LinksWidgetItemData, PostData } from '../../components'; import type { ArticlePreview, PageLink } from '../../types'; import { ROUTES } from '../constants'; +export const getUniquePageLinks = (pageLinks: PageLink[]): PageLink[] => { + const pageLinksIds = pageLinks.map((pageLink) => pageLink.id); + + return pageLinks.filter( + ({ id }, index) => !pageLinksIds.includes(id, index + 1) + ); +}; + /** * Method to sort PageLink objects by name. * |
