aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/i18n/en.json50
-rw-r--r--src/i18n/fr.json50
-rw-r--r--src/pages/recherche/index.tsx365
-rw-r--r--src/styles/pages/blog.module.scss4
-rw-r--r--src/utils/hooks/index.ts1
-rw-r--r--src/utils/hooks/use-data-from-api.tsx21
-rw-r--r--tests/cypress/e2e/pages/search.cy.ts28
7 files changed, 305 insertions, 214 deletions
diff --git a/src/i18n/en.json b/src/i18n/en.json
index b1768a8..67880a2 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -167,6 +167,10 @@
"defaultMessage": "Sorry, it seems that the page your are looking for does not exist. If you think this path should work, feel free to <link>contact me</link> with the necessary information so that I can fix the problem.",
"description": "Error404Page: page body"
},
+ "A0TsHP": {
+ "defaultMessage": "Please use the form below to start searching:",
+ "description": "SearchPage: search for message"
+ },
"A8hGaK": {
"defaultMessage": "Comment:",
"description": "CommentForm: comment label"
@@ -219,9 +223,9 @@
"defaultMessage": "Thematics",
"description": "SearchPage: thematics list widget title"
},
- "EeCqAE": {
- "defaultMessage": "Loading the search results...",
- "description": "SearchPage: loading search results message"
+ "E+ROR5": {
+ "defaultMessage": "No results found. Would you like to try a new search?",
+ "description": "SearchPage: no results"
},
"Es52wh": {
"defaultMessage": "Blog",
@@ -307,6 +311,10 @@
"defaultMessage": "CV",
"description": "SiteNavbar: main nav - cv link"
},
+ "N+3eau": {
+ "defaultMessage": "Search results for \"{query}\"",
+ "description": "SearchPage: SEO - Page title"
+ },
"N804XO": {
"defaultMessage": "Topics",
"description": "SearchPage: topics list widget title"
@@ -319,6 +327,10 @@
"defaultMessage": "{commentsCount, plural, =0 {No comments} one {# comment} other {# comments}}<a11y> about {title}</a11y>",
"description": "PostPreviewMeta: comments count"
},
+ "NqVQYo": {
+ "defaultMessage": "Search - {websiteName}",
+ "description": "SearchPage: SEO - Page title"
+ },
"Nx8Jo5": {
"defaultMessage": "Github profile",
"description": "ProjectsPage: Github profile link"
@@ -371,6 +383,10 @@
"defaultMessage": "Main navigation",
"description": "SiteNavbar: main nav accessible name"
},
+ "QRDdye": {
+ "defaultMessage": "Search results for {query} - {websiteName}",
+ "description": "SearchPage: SEO - Page title"
+ },
"Qa9twM": {
"defaultMessage": "Reply",
"description": "CommentsList: reply button"
@@ -411,10 +427,6 @@
"defaultMessage": "Send",
"description": "ContactForm: send button"
},
- "VkfO7t": {
- "defaultMessage": "Query must be longer than one character.",
- "description": "NoResults: invalid query message"
- },
"Vrw5/h": {
"defaultMessage": "{website} logo",
"description": "SiteBranding: logo title"
@@ -447,10 +459,6 @@
"defaultMessage": "Leave a comment",
"description": "PageComments: the section title of the comment form"
},
- "YV//MH": {
- "defaultMessage": "No results found.",
- "description": "SearchPage: no results"
- },
"Ygea7s": {
"defaultMessage": "Light theme",
"description": "ThemeToggle: light theme label"
@@ -471,10 +479,6 @@
"defaultMessage": "You can't load more articles without Javascript, please use the pagination instead.",
"description": "BlogPage: pagination no script message"
},
- "ZNBhDP": {
- "defaultMessage": "Search results for {query}",
- "description": "SearchPage: SEO - Page title"
- },
"ZcFroC": {
"defaultMessage": "Thanks, your comment was successfully sent.",
"description": "PageComments: comment form success message"
@@ -523,6 +527,10 @@
"defaultMessage": "Blog: development, open source - Page {number} - {websiteName}",
"description": "BlogPage: SEO - Page title"
},
+ "e3ppRI": {
+ "defaultMessage": "Query must be longer than one character.",
+ "description": "SearchPage: invalid query message"
+ },
"eys2uX": {
"defaultMessage": "Table of Contents",
"description": "PageLayout: table of contents title"
@@ -647,6 +655,10 @@
"defaultMessage": "Discover search results for {query} on {websiteName}.",
"description": "SearchPage: SEO - Meta description"
},
+ "qFqWQH": {
+ "defaultMessage": "Thematics are loading...",
+ "description": "SearchPage: loading thematics message"
+ },
"rVoW4G": {
"defaultMessage": "Thematics are loading...",
"description": "ThematicPage: loading thematics message"
@@ -675,6 +687,10 @@
"defaultMessage": "Partial",
"description": "AckeeToggle: partial option name"
},
+ "tLflgC": {
+ "defaultMessage": "Topics are loading...",
+ "description": "SearchPage: loading topics message"
+ },
"tsWh8x": {
"defaultMessage": "Light theme",
"description": "PrismThemeToggle: light theme label"
@@ -699,10 +715,6 @@
"defaultMessage": "On",
"description": "MotionToggle: activate reduce motion label"
},
- "vtDLzG": {
- "defaultMessage": "Would you like to try a new search?",
- "description": "SearchPage: try a new search message"
- },
"w+BpPg": {
"defaultMessage": "No comments yet. <link>Be the first!</link>",
"description": "PageComments: no comments text"
diff --git a/src/i18n/fr.json b/src/i18n/fr.json
index 50c9ca7..39ae75c 100644
--- a/src/i18n/fr.json
+++ b/src/i18n/fr.json
@@ -167,6 +167,10 @@
"defaultMessage": "Désolé, il semble que la page demandée n’existe pas. Si vous pensez que le chemin devrait exister, n’hésitez pas à <link>me contacter</link> avec les informations nécessaires pour que je puisse corriger le problème.",
"description": "Error404Page: page body"
},
+ "A0TsHP": {
+ "defaultMessage": "Veuillez utiliser le formulaire ci-dessous pour commencer une recherche :",
+ "description": "SearchPage: search for message"
+ },
"A8hGaK": {
"defaultMessage": "Commentaire :",
"description": "CommentForm: comment label"
@@ -219,9 +223,9 @@
"defaultMessage": "Thématiques",
"description": "SearchPage: thematics list widget title"
},
- "EeCqAE": {
- "defaultMessage": "Chargement des résultats…",
- "description": "SearchPage: loading search results message"
+ "E+ROR5": {
+ "defaultMessage": "Aucun résultat. Souhaitez-vous tenter une nouvelle rechercher ?",
+ "description": "SearchPage: no results"
},
"Es52wh": {
"defaultMessage": "Blog",
@@ -307,6 +311,10 @@
"defaultMessage": "CV",
"description": "SiteNavbar: main nav - cv link"
},
+ "N+3eau": {
+ "defaultMessage": "Résultats de la recherche pour « {query} »",
+ "description": "SearchPage: SEO - Page title"
+ },
"N804XO": {
"defaultMessage": "Sujets",
"description": "SearchPage: topics list widget title"
@@ -319,6 +327,10 @@
"defaultMessage": "{commentsCount, plural, =0 {0 commentaire} one {# commentaire} other {# commentaires}}<a11y> à propos de {title}</a11y>",
"description": "PostPreviewMeta: comments count"
},
+ "NqVQYo": {
+ "defaultMessage": "Recherche - {websiteName}",
+ "description": "SearchPage: SEO - Page title"
+ },
"Nx8Jo5": {
"defaultMessage": "Profil Github",
"description": "ProjectsPage: Github profile link"
@@ -371,6 +383,10 @@
"defaultMessage": "Navigation principale",
"description": "SiteNavbar: main nav accessible name"
},
+ "QRDdye": {
+ "defaultMessage": "Résultats de la recherche pour {query} - {websiteName}",
+ "description": "SearchPage: SEO - Page title"
+ },
"Qa9twM": {
"defaultMessage": "Répondre",
"description": "CommentsList: reply button"
@@ -411,10 +427,6 @@
"defaultMessage": "Envoyer",
"description": "ContactForm: send button"
},
- "VkfO7t": {
- "defaultMessage": "Les mots-clés doivent être plus longs qu'un caractère.",
- "description": "NoResults: invalid query message"
- },
"Vrw5/h": {
"defaultMessage": "Logo d’{website}",
"description": "SiteBranding: logo title"
@@ -447,10 +459,6 @@
"defaultMessage": "Laisser un commentaire",
"description": "PageComments: the section title of the comment form"
},
- "YV//MH": {
- "defaultMessage": "Aucun résultat.",
- "description": "SearchPage: no results"
- },
"Ygea7s": {
"defaultMessage": "Thème clair",
"description": "ThemeToggle: light theme label"
@@ -471,10 +479,6 @@
"defaultMessage": "Vous ne pouvez pas charger plus d’articles sans Javascript, veuillez utiliser la pagination.",
"description": "BlogPage: pagination no script message"
},
- "ZNBhDP": {
- "defaultMessage": "Résultats de la recherche pour {query}",
- "description": "SearchPage: SEO - Page title"
- },
"ZcFroC": {
"defaultMessage": "Merci, votre commentaire a été envoyé avec succès.",
"description": "PageComments: comment form success message"
@@ -523,6 +527,10 @@
"defaultMessage": "Blog: développement, libre et open-source - Page {number} - {websiteName}",
"description": "BlogPage: SEO - Page title"
},
+ "e3ppRI": {
+ "defaultMessage": "Les mots-clés doivent être plus longs qu'un caractère.",
+ "description": "SearchPage: invalid query message"
+ },
"eys2uX": {
"defaultMessage": "Table des matières",
"description": "PageLayout: table of contents title"
@@ -647,6 +655,10 @@
"defaultMessage": "Découvrez les résultats de recherche pour {query} sur {websiteName}.",
"description": "SearchPage: SEO - Meta description"
},
+ "qFqWQH": {
+ "defaultMessage": "Les thématiques sont en cours de chargement…",
+ "description": "SearchPage: loading thematics message"
+ },
"rVoW4G": {
"defaultMessage": "Les thématiques sont en cours de chargement…",
"description": "ThematicPage: loading thematics message"
@@ -675,6 +687,10 @@
"defaultMessage": "Partiel",
"description": "AckeeToggle: partial option name"
},
+ "tLflgC": {
+ "defaultMessage": "Les sujets sont en cours de chargement…",
+ "description": "SearchPage: loading topics message"
+ },
"tsWh8x": {
"defaultMessage": "Thème clair",
"description": "PrismThemeToggle: light theme label"
@@ -699,10 +715,6 @@
"defaultMessage": "Marche",
"description": "MotionToggle: activate reduce motion label"
},
- "vtDLzG": {
- "defaultMessage": "Souhaitez-vous essayer une nouvelle recherche ?",
- "description": "SearchPage: try a new search message"
- },
"w+BpPg": {
"defaultMessage": "Aucun commentaire pour le moment. <link>Soyez le premier !</link>",
"description": "PageComments: no comments text"
diff --git a/src/pages/recherche/index.tsx b/src/pages/recherche/index.tsx
index 2bcb1c0..9eaecba 100644
--- a/src/pages/recherche/index.tsx
+++ b/src/pages/recherche/index.tsx
@@ -22,7 +22,6 @@ import {
import {
convertWPThematicPreviewToPageLink,
convertWPTopicPreviewToPageLink,
- fetchPostsCount,
fetchThematicsCount,
fetchThematicsList,
fetchTopicsCount,
@@ -30,6 +29,7 @@ import {
} from '../../services/graphql';
import styles from '../../styles/pages/blog.module.scss';
import type {
+ GraphQLConnection,
NextPageWithLayout,
WPThematicPreview,
WPTopicPreview,
@@ -47,78 +47,70 @@ import { loadTranslation, type Messages } from '../../utils/helpers/server';
import {
useArticlesList,
useBreadcrumb,
- useDataFromAPI,
+ useThematicsList,
+ useTopicsList,
} from '../../utils/hooks';
+const NoResults = () => {
+ const intl = useIntl();
+ const router = useRouter();
+
+ const searchSubmitHandler: SearchFormSubmit = useCallback(
+ async ({ query: searchQuery }) => {
+ if (!searchQuery)
+ return {
+ messages: {
+ error: intl.formatMessage({
+ defaultMessage: 'Query must be longer than one character.',
+ description: 'SearchPage: invalid query message',
+ id: 'e3ppRI',
+ }),
+ },
+ validator: (value) => value.query.length > 1,
+ };
+
+ await router.push({ pathname: ROUTES.SEARCH, query: { s: searchQuery } });
+
+ return undefined;
+ },
+ [intl, router]
+ );
+
+ return (
+ <div className={styles['no-results']}>
+ <p>
+ {router.query.s
+ ? intl.formatMessage({
+ defaultMessage:
+ 'No results found. Would you like to try a new search?',
+ description: 'SearchPage: no results',
+ id: 'E+ROR5',
+ })
+ : intl.formatMessage({
+ defaultMessage: 'Please use the form below to start searching:',
+ description: 'SearchPage: search for message',
+ id: 'A0TsHP',
+ })}
+ </p>
+ <SearchForm isLabelHidden onSubmit={searchSubmitHandler} />
+ </div>
+ );
+};
+
type SearchPageProps = {
- thematicsList: WPThematicPreview[];
- topicsList: WPTopicPreview[];
+ data: {
+ thematics: GraphQLConnection<WPThematicPreview>;
+ topics: GraphQLConnection<WPTopicPreview>;
+ };
translation: Messages;
};
/**
* Search page.
*/
-const SearchPage: NextPageWithLayout<SearchPageProps> = ({
- thematicsList,
- topicsList,
-}) => {
+const SearchPage: NextPageWithLayout<SearchPageProps> = ({ data }) => {
const intl = useIntl();
- const { asPath, query, push: routerPush } = useRouter();
- const title = query.s
- ? intl.formatMessage(
- {
- defaultMessage: 'Search results for {query}',
- description: 'SearchPage: SEO - Page title',
- id: 'ZNBhDP',
- },
- { query: query.s as string }
- )
- : intl.formatMessage({
- defaultMessage: 'Search',
- description: 'SearchPage: SEO - Page title',
- id: 'WDwNDl',
- });
- const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({
- title,
- url: ROUTES.SEARCH,
- });
-
- const page = {
- title: `${title} - ${CONFIG.name}`,
- url: `${CONFIG.url}${asPath}`,
- };
- const pageDescription = query.s
- ? intl.formatMessage(
- {
- defaultMessage:
- 'Discover search results for {query} on {websiteName}.',
- description: 'SearchPage: SEO - Meta description',
- id: 'pg26sn',
- },
- { query: query.s as string, websiteName: CONFIG.name }
- )
- : intl.formatMessage(
- {
- defaultMessage: 'Search for a post on {websiteName}.',
- description: 'SearchPage: SEO - Meta description',
- id: 'npisb3',
- },
- { websiteName: CONFIG.name }
- );
- const webpageSchema = getWebPageSchema({
- description: pageDescription,
- locale: CONFIG.locales.defaultLocale,
- slug: asPath,
- title: page.title,
- });
- const blogSchema = getBlogSchema({
- isSinglePage: false,
- locale: CONFIG.locales.defaultLocale,
- slug: asPath,
- });
- const schemaJsonLd = getSchemaJson([webpageSchema, blogSchema]);
-
+ const { asPath, query } = useRouter();
const {
articles,
error,
@@ -129,68 +121,127 @@ const SearchPage: NextPageWithLayout<SearchPageProps> = ({
hasNextPage,
loadMore,
} = useArticlesList({
- fallback: [],
perPage: CONFIG.postsPerPage,
- searchQuery: query.s as string,
+ searchQuery: typeof query.s === 'string' ? query.s : undefined,
});
-
- const totalArticles = useDataFromAPI<number>(async () =>
- fetchPostsCount({ search: query.s as string })
- );
-
- const thematicsListTitle = intl.formatMessage({
- defaultMessage: 'Thematics',
- description: 'SearchPage: thematics list widget title',
- id: 'Dq6+WH',
- });
-
- const topicsListTitle = intl.formatMessage({
- defaultMessage: 'Topics',
- description: 'SearchPage: topics list widget title',
- id: 'N804XO',
+ const { isLoading: areThematicsLoading, thematics } = useThematicsList({
+ fallback: data.thematics,
+ input: { first: data.thematics.pageInfo.total },
});
- const loadingResults = intl.formatMessage({
- defaultMessage: 'Loading the search results...',
- description: 'SearchPage: loading search results message',
- id: 'EeCqAE',
+ const { isLoading: areTopicsLoading, topics } = useTopicsList({
+ fallback: data.topics,
+ input: { first: data.topics.pageInfo.total },
});
- 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',
- }),
+ const messages = {
+ loading: {
+ thematicsList: intl.formatMessage({
+ defaultMessage: 'Thematics are loading...',
+ description: 'SearchPage: loading thematics message',
+ id: 'qFqWQH',
+ }),
+ topicsList: intl.formatMessage({
+ defaultMessage: 'Topics are loading...',
+ description: 'SearchPage: loading topics message',
+ id: 'tLflgC',
+ }),
+ },
+ pageTitle: query.s
+ ? intl.formatMessage(
+ {
+ defaultMessage: 'Search results for "{query}"',
+ description: 'SearchPage: SEO - Page title',
+ id: 'N+3eau',
},
- validator: (value) => value.query.length > 1,
- };
+ { query: query.s as string }
+ )
+ : intl.formatMessage({
+ defaultMessage: 'Search',
+ description: 'SearchPage: SEO - Page title',
+ id: 'WDwNDl',
+ }),
+ seo: {
+ metaDesc: query.s
+ ? intl.formatMessage(
+ {
+ defaultMessage:
+ 'Discover search results for {query} on {websiteName}.',
+ description: 'SearchPage: SEO - Meta description',
+ id: 'pg26sn',
+ },
+ { query: query.s as string, websiteName: CONFIG.name }
+ )
+ : intl.formatMessage(
+ {
+ defaultMessage: 'Search for a post on {websiteName}.',
+ description: 'SearchPage: SEO - Meta description',
+ id: 'npisb3',
+ },
+ { websiteName: CONFIG.name }
+ ),
+ title: query.s
+ ? intl.formatMessage(
+ {
+ defaultMessage: 'Search results for {query} - {websiteName}',
+ description: 'SearchPage: SEO - Page title',
+ id: 'QRDdye',
+ },
+ { query: query.s as string, websiteName: CONFIG.name }
+ )
+ : intl.formatMessage(
+ {
+ defaultMessage: 'Search - {websiteName}',
+ description: 'SearchPage: SEO - Page title',
+ id: 'NqVQYo',
+ },
+ { websiteName: CONFIG.name }
+ ),
+ },
+ widgets: {
+ thematicsListTitle: intl.formatMessage({
+ defaultMessage: 'Thematics',
+ description: 'SearchPage: thematics list widget title',
+ id: 'Dq6+WH',
+ }),
+ topicsListTitle: intl.formatMessage({
+ defaultMessage: 'Topics',
+ description: 'SearchPage: topics list widget title',
+ id: 'N804XO',
+ }),
+ },
+ };
- routerPush({ pathname: ROUTES.SEARCH, query: { s: searchQuery } });
+ const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({
+ title: messages.pageTitle,
+ url: ROUTES.SEARCH,
+ });
- return undefined;
- },
- [intl, routerPush]
- );
+ const webpageSchema = getWebPageSchema({
+ description: messages.seo.metaDesc,
+ locale: CONFIG.locales.defaultLocale,
+ slug: asPath,
+ title: messages.pageTitle,
+ });
+ const blogSchema = getBlogSchema({
+ isSinglePage: false,
+ locale: CONFIG.locales.defaultLocale,
+ slug: asPath,
+ });
+ const schemaJsonLd = getSchemaJson([webpageSchema, blogSchema]);
- const foundArticles = articles?.flatMap((p) =>
- p.edges.map((edge) => edge.node)
- );
+ const pageUrl = `${CONFIG.url}${asPath}`;
return (
<Page breadcrumbs={breadcrumbItems} isBodyLastChild>
<Head>
- <title>{page.title}</title>
+ <title>{messages.seo.title}</title>
{/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */}
- <meta name="description" content={pageDescription} />
- <meta property="og:url" content={page.url} />
+ <meta name="description" content={messages.seo.metaDesc} />
+ <meta property="og:url" content={pageUrl} />
{/*eslint-disable-next-line react/jsx-no-literals -- Content allowed */}
<meta property="og:type" content="website" />
- <meta property="og:title" content={title} />
- <meta property="og:description" content={pageDescription} />
+ <meta property="og:title" content={messages.pageTitle} />
+ <meta property="og:description" content={messages.seo.title} />
</Head>
<Script
// eslint-disable-next-line react/jsx-no-literals -- Id allowed
@@ -205,36 +256,32 @@ const SearchPage: NextPageWithLayout<SearchPageProps> = ({
id="schema-breadcrumb"
type="application/ld+json"
/>
- <PageHeader heading={title} meta={{ total: totalArticles }} />
- <PageBody className={styles.body}>
- {foundArticles ? null : <Spinner>{loadingResults}</Spinner>}
- {foundArticles?.length ? (
+ <PageHeader
+ heading={messages.pageTitle}
+ meta={{ total: articles ? articles[0].pageInfo.total : undefined }}
+ />
+ <PageBody>
+ {query.s &&
+ ((articles?.length && articles[0].edges.length) || isLoading) ? (
<PostsList
- className={styles.list}
+ className={styles['posts-list']}
firstNewResult={firstNewResultIndex}
isLoading={isLoading || isLoadingMore || isRefreshing}
onLoadMore={hasNextPage ? loadMore : undefined}
- posts={getPostsWithUrl(foundArticles)}
+ posts={
+ articles
+ ? getPostsWithUrl(
+ articles.flatMap((page) =>
+ page.edges.map((edge) => edge.node)
+ )
+ )
+ : []
+ }
sortByYear
+ total={articles ? articles[0].pageInfo.total : undefined}
/>
) : (
- <>
- <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} />
- </>
+ <NoResults />
)}
{error ? (
<Notice
@@ -250,26 +297,34 @@ const SearchPage: NextPageWithLayout<SearchPageProps> = ({
) : null}
</PageBody>
<PageSidebar>
- <LinksWidget
- heading={
- <Heading isFake level={3}>
- {thematicsListTitle}
- </Heading>
- }
- items={getLinksItemData(
- thematicsList.map(convertWPThematicPreviewToPageLink)
- )}
- />
- <LinksWidget
- heading={
- <Heading isFake level={3}>
- {topicsListTitle}
- </Heading>
- }
- items={getLinksItemData(
- topicsList.map(convertWPTopicPreviewToPageLink)
- )}
- />
+ {areThematicsLoading ? (
+ <Spinner>{messages.loading.thematicsList}</Spinner>
+ ) : (
+ <LinksWidget
+ heading={
+ <Heading level={2}>{messages.widgets.thematicsListTitle}</Heading>
+ }
+ items={getLinksItemData(
+ thematics.edges.map((edge) =>
+ convertWPThematicPreviewToPageLink(edge.node)
+ )
+ )}
+ />
+ )}
+ {areTopicsLoading ? (
+ <Spinner>{messages.loading.topicsList}</Spinner>
+ ) : (
+ <LinksWidget
+ heading={
+ <Heading level={2}>{messages.widgets.topicsListTitle}</Heading>
+ }
+ items={getLinksItemData(
+ topics.edges.map((edge) =>
+ convertWPTopicPreviewToPageLink(edge.node)
+ )
+ )}
+ />
+ )}
</PageSidebar>
</Page>
);
@@ -288,8 +343,10 @@ export const getStaticProps: GetStaticProps<SearchPageProps> = async ({
return {
props: {
- thematicsList: thematics.edges.map((edge) => edge.node),
- topicsList: topics.edges.map((edge) => edge.node),
+ data: {
+ thematics,
+ topics,
+ },
translation,
},
};
diff --git a/src/styles/pages/blog.module.scss b/src/styles/pages/blog.module.scss
index 553e9f9..6b68849 100644
--- a/src/styles/pages/blog.module.scss
+++ b/src/styles/pages/blog.module.scss
@@ -19,6 +19,10 @@
}
}
+.no-results {
+ margin-block-start: var(--spacing-md);
+}
+
.pagination {
margin-top: var(--spacing-md);
}
diff --git a/src/utils/hooks/index.ts b/src/utils/hooks/index.ts
index 1e0bfe3..1ee513d 100644
--- a/src/utils/hooks/index.ts
+++ b/src/utils/hooks/index.ts
@@ -4,7 +4,6 @@ export * from './use-articles-list';
export * from './use-boolean';
export * from './use-breadcrumb';
export * from './use-comments';
-export * from './use-data-from-api';
export * from './use-form';
export * from './use-github-api';
export * from './use-headings-tree';
diff --git a/src/utils/hooks/use-data-from-api.tsx b/src/utils/hooks/use-data-from-api.tsx
deleted file mode 100644
index 5abcf09..0000000
--- a/src/utils/hooks/use-data-from-api.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { useEffect, useState } from 'react';
-
-/**
- * Fetch data from an API.
- *
- * This hook is a wrapper to `setState` + `useEffect`.
- *
- * @param fetcher - A function to fetch data from API.
- * @returns {T | undefined} The requested data.
- */
-export const useDataFromAPI = <T,>(
- fetcher: () => Promise<T>
-): T | undefined => {
- const [data, setData] = useState<T>();
-
- useEffect(() => {
- fetcher().then((apiData) => setData(apiData));
- }, [fetcher]);
-
- return data;
-};
diff --git a/tests/cypress/e2e/pages/search.cy.ts b/tests/cypress/e2e/pages/search.cy.ts
new file mode 100644
index 0000000..bde592e
--- /dev/null
+++ b/tests/cypress/e2e/pages/search.cy.ts
@@ -0,0 +1,28 @@
+import { ROUTES } from '../../../../src/utils/constants';
+
+describe('Search Page', () => {
+ beforeEach(() => {
+ cy.visit(ROUTES.SEARCH);
+ });
+
+ it('successfully loads', () => {
+ cy.findByRole('heading', { level: 1 }).should('exist');
+ });
+
+ it('contains a breadcrumbs', () => {
+ cy.findByRole('navigation', { name: 'Fil d’Ariane' }).should('exist');
+ });
+
+ it('contains a thematics list widget and a topics list widget', () => {
+ cy.findByRole('heading', { level: 2, name: 'Thématiques' }).should('exist');
+ cy.findByRole('heading', { level: 2, name: 'Sujets' }).should('exist');
+ });
+
+ it('provides a form to search for keywords', () => {
+ const keywords = 'coldark';
+
+ cy.findByRole('searchbox').type(keywords);
+ cy.findByRole('button', { name: /Rechercher/ }).click();
+ cy.findByRole('heading', { level: 1 }).should('contain.text', keywords);
+ });
+});