aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/templates/page
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-09-27 17:38:23 +0200
committerArmand Philippot <git@armandphilippot.com>2023-10-24 12:25:00 +0200
commit7255d25f6834a208c0ed44636356cc260f6ab6ba (patch)
tree88016a958190f766a3ac0ab4b77f4732e17502e8 /src/components/templates/page
parentba793e043e4d8515b1a9ea490ee2c5f92b1fd6c2 (diff)
refactor(components): rewrite Heading component
* remove `alignment` and `withMargin` props (consumer should handle that) * move styles to Sass placeholders to avoid repeats with headings coming from WordPress * refactor some other components that depend on Heading to avoid ESlint errors
Diffstat (limited to 'src/components/templates/page')
-rw-r--r--src/components/templates/page/page-layout.module.scss10
-rw-r--r--src/components/templates/page/page-layout.tsx179
2 files changed, 115 insertions, 74 deletions
diff --git a/src/components/templates/page/page-layout.module.scss b/src/components/templates/page/page-layout.module.scss
index d29df2c..09bb957 100644
--- a/src/components/templates/page/page-layout.module.scss
+++ b/src/components/templates/page/page-layout.module.scss
@@ -81,6 +81,12 @@
}
}
+ &__title {
+ width: fit-content;
+ margin-bottom: var(--spacing-md);
+ margin-inline: auto;
+ }
+
&__no-comments {
text-align: center;
}
@@ -90,3 +96,7 @@
margin: auto;
}
}
+
+.notice {
+ margin-top: var(--spacing-sm);
+}
diff --git a/src/components/templates/page/page-layout.tsx b/src/components/templates/page/page-layout.tsx
index 72bfd3f..dfd9353 100644
--- a/src/components/templates/page/page-layout.tsx
+++ b/src/components/templates/page/page-layout.tsx
@@ -1,15 +1,22 @@
import Script from 'next/script';
-import { FC, HTMLAttributes, ReactNode, useRef, useState } from 'react';
+import {
+ type FC,
+ type HTMLAttributes,
+ type ReactNode,
+ useRef,
+ useState,
+ useCallback,
+} from 'react';
import { useIntl } from 'react-intl';
-import { BreadcrumbList } from 'schema-dts';
+import type { BreadcrumbList } from 'schema-dts';
import { sendComment } from '../../../services/graphql';
-import { SendCommentInput } from '../../../types';
+import type { Approved, SendCommentInput, SingleComment } from '../../../types';
import { useIsMounted } from '../../../utils/hooks';
import { Heading, Notice, type NoticeKind, Sidebar } from '../../atoms';
import {
Breadcrumb,
type BreadcrumbItem,
- MetaData,
+ type MetaData,
PageFooter,
type PageFooterProps,
PageHeader,
@@ -24,6 +31,29 @@ import {
} from '../../organisms';
import styles from './page-layout.module.scss';
+/**
+ * Check if there is at least one comment.
+ *
+ * @param {SingleComment[] | undefined} comments - The comments.
+ */
+const hasComments = (
+ comments: SingleComment[] | undefined
+): comments is SingleComment[] =>
+ Array.isArray(comments) && comments.length > 0;
+
+/**
+ * Check if meta properties are defined.
+ *
+ * @param {MetaData} meta - The metadata.
+ */
+const hasMeta = (meta: MetaData) => Object.values(meta).every((value) => value);
+
+type CommentStatus = {
+ isReply: boolean;
+ kind: NoticeKind;
+ message: string;
+};
+
export type PageLayoutProps = {
/**
* True if the page accepts new comments. Default: false.
@@ -118,67 +148,69 @@ export const PageLayout: FC<PageLayoutProps> = ({
const bodyRef = useRef<HTMLDivElement>(null);
const isMounted = useIsMounted(bodyRef);
- const hasComments = Array.isArray(comments) && comments.length > 0;
- const [status, setStatus] = useState<NoticeKind>('info');
- const [statusMessage, setStatusMessage] = useState<string>('');
- const isReplyRef = useRef<boolean>(false);
-
- const saveComment: CommentFormProps['saveComment'] = async (data, reset) => {
- if (!id) throw new Error('Page id missing. Cannot save comment.');
-
- const { author, comment: commentBody, email, parentId, website } = data;
- const commentData: SendCommentInput = {
- author,
- authorEmail: email,
- authorUrl: website ?? '',
- clientMutationId: 'contact',
- commentOn: id,
- content: commentBody,
- parent: parentId,
- };
- const { comment, success } = await sendComment(commentData);
+ const [commentStatus, setCommentStatus] = useState<CommentStatus | undefined>(
+ undefined
+ );
- isReplyRef.current = !!parentId;
+ const isSuccessStatus = useCallback(
+ (comment: Approved | null, isReply: boolean, isSuccess: boolean) => {
+ if (isSuccess) {
+ const successPrefix = intl.formatMessage({
+ defaultMessage: 'Thanks, your comment was successfully sent.',
+ description: 'PageLayout: comment form success message',
+ id: 'B290Ph',
+ });
+ const successMessage = comment?.approved
+ ? intl.formatMessage({
+ defaultMessage: 'It has been approved.',
+ id: 'g3+Ahv',
+ description: 'PageLayout: comment approved.',
+ })
+ : intl.formatMessage({
+ defaultMessage: 'It is now awaiting moderation.',
+ id: 'Vmj5cw',
+ description: 'PageLayout: comment awaiting moderation',
+ });
+ setCommentStatus({
+ isReply,
+ kind: 'success',
+ message: `${successPrefix} ${successMessage}`,
+ });
+ return true;
+ }
- if (success) {
- setStatus('success');
- const successPrefix = intl.formatMessage({
- defaultMessage: 'Thanks, your comment was successfully sent.',
- description: 'PageLayout: comment form success message',
- id: 'B290Ph',
- });
- const successMessage = comment?.approved
- ? intl.formatMessage({
- defaultMessage: 'It has been approved.',
- id: 'g3+Ahv',
- description: 'PageLayout: comment approved.',
- })
- : intl.formatMessage({
- defaultMessage: 'It is now awaiting moderation.',
- id: 'Vmj5cw',
- description: 'PageLayout: comment awaiting moderation',
- });
- setStatusMessage(`${successPrefix} ${successMessage}`);
- reset();
- } else {
const error = intl.formatMessage({
defaultMessage: 'An error occurred:',
description: 'PageLayout: comment form error message',
id: 'fkcTGp',
});
- setStatus('error');
- setStatusMessage(error);
- }
- };
+ setCommentStatus({ isReply, kind: 'error', message: error });
+ return false;
+ },
+ [intl]
+ );
- /**
- * Check if meta properties are defined.
- *
- * @param {MetaData} meta - The metadata.
- */
- const hasMeta = (meta: MetaData) => {
- return Object.values(meta).every((value) => value);
- };
+ const saveComment: CommentFormProps['saveComment'] = useCallback(
+ async (data, reset) => {
+ if (!id) throw new Error('Page id missing. Cannot save comment.');
+
+ const { author, comment: commentBody, email, parentId, website } = data;
+ const isReply = !!parentId;
+ const commentData: SendCommentInput = {
+ author,
+ authorEmail: email,
+ authorUrl: website ?? '',
+ clientMutationId: 'comment',
+ commentOn: id,
+ content: commentBody,
+ parent: parentId,
+ };
+ const { comment, success } = await sendComment(commentData);
+
+ if (isSuccessStatus(comment, isReply, success)) reset();
+ },
+ [id, isSuccessStatus]
+ );
return (
<>
@@ -198,7 +230,7 @@ export const PageLayout: FC<PageLayoutProps> = ({
meta={headerMeta}
title={title}
/>
- {withToC && (
+ {withToC ? (
<Sidebar
aria-label={intl.formatMessage({
defaultMessage: 'Table of contents sidebar',
@@ -207,11 +239,11 @@ export const PageLayout: FC<PageLayoutProps> = ({
})}
className={`${styles.sidebar} ${styles['sidebar--first']}`}
>
- {isMounted && bodyRef.current && (
+ {isMounted && bodyRef.current ? (
<TableOfContents wrapper={bodyRef.current} />
- )}
+ ) : null}
</Sidebar>
- )}
+ ) : null}
{typeof children === 'string' ? (
<div
{...bodyAttributes}
@@ -224,9 +256,9 @@ export const PageLayout: FC<PageLayoutProps> = ({
{children}
</div>
)}
- {footerMeta && hasMeta(footerMeta) && (
+ {footerMeta && hasMeta(footerMeta) ? (
<PageFooter meta={footerMeta} className={styles.footer} />
- )}
+ ) : null}
<Sidebar
aria-label={intl.formatMessage({
defaultMessage: 'Sidebar',
@@ -237,22 +269,22 @@ export const PageLayout: FC<PageLayoutProps> = ({
>
{widgets}
</Sidebar>
- {allowComments && (
+ {allowComments ? (
<div className={styles.comments} id="comments">
<section className={styles.comments__section}>
- <Heading level={2} alignment="center">
+ <Heading className={styles.comments__title} level={2}>
{commentsTitle}
</Heading>
- {hasComments ? (
+ {hasComments(comments) ? (
<CommentsList
comments={comments}
depth={2}
Notice={
- isReplyRef.current === true && statusMessage ? (
+ commentStatus?.isReply ? (
<Notice
className={styles.notice}
- kind={status}
- message={statusMessage}
+ kind={commentStatus.kind}
+ message={commentStatus.message}
/>
) : null
}
@@ -273,20 +305,19 @@ export const PageLayout: FC<PageLayoutProps> = ({
className={styles.comments__form}
saveComment={saveComment}
title={commentFormTitle}
- titleAlignment="center"
Notice={
- isReplyRef.current === false && statusMessage ? (
+ commentStatus && !commentStatus.isReply ? (
<Notice
className={styles.notice}
- kind={status}
- message={statusMessage}
+ kind={commentStatus.kind}
+ message={commentStatus.message}
/>
) : null
}
/>
</section>
</div>
- )}
+ ) : null}
</>
);
};