From 7255d25f6834a208c0ed44636356cc260f6ab6ba Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Wed, 27 Sep 2023 17:38:23 +0200 Subject: 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 --- .../templates/page/page-layout.module.scss | 10 ++ src/components/templates/page/page-layout.tsx | 179 ++++++++++++--------- 2 files changed, 115 insertions(+), 74 deletions(-) (limited to 'src/components/templates') 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 = ({ const bodyRef = useRef(null); const isMounted = useIsMounted(bodyRef); - const hasComments = Array.isArray(comments) && comments.length > 0; - const [status, setStatus] = useState('info'); - const [statusMessage, setStatusMessage] = useState(''); - const isReplyRef = useRef(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( + 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 = ({ meta={headerMeta} title={title} /> - {withToC && ( + {withToC ? ( = ({ })} className={`${styles.sidebar} ${styles['sidebar--first']}`} > - {isMounted && bodyRef.current && ( + {isMounted && bodyRef.current ? ( - )} + ) : null} - )} + ) : null} {typeof children === 'string' ? (
= ({ {children}
)} - {footerMeta && hasMeta(footerMeta) && ( + {footerMeta && hasMeta(footerMeta) ? ( - )} + ) : null} = ({ > {widgets} - {allowComments && ( + {allowComments ? (
- + {commentsTitle} - {hasComments ? ( + {hasComments(comments) ? ( ) : null } @@ -273,20 +305,19 @@ export const PageLayout: FC = ({ className={styles.comments__form} saveComment={saveComment} title={commentFormTitle} - titleAlignment="center" Notice={ - isReplyRef.current === false && statusMessage ? ( + commentStatus && !commentStatus.isReply ? ( ) : null } />
- )} + ) : null} ); }; -- cgit v1.2.3