diff options
Diffstat (limited to 'src/components/organisms/layout')
| -rw-r--r-- | src/components/organisms/layout/footer.tsx | 16 | ||||
| -rw-r--r-- | src/components/organisms/layout/posts-list.tsx | 134 | ||||
| -rw-r--r-- | src/components/organisms/layout/summary.tsx | 16 |
3 files changed, 82 insertions, 84 deletions
diff --git a/src/components/organisms/layout/footer.tsx b/src/components/organisms/layout/footer.tsx index f1f3236..36e85a7 100644 --- a/src/components/organisms/layout/footer.tsx +++ b/src/components/organisms/layout/footer.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react'; +import type { FC } from 'react'; import { useIntl } from 'react-intl'; import { Copyright, type CopyrightProps } from '../../atoms'; import { @@ -50,26 +50,26 @@ export const Footer: FC<FooterProps> = ({ description: 'Footer: an accessible name for footer nav', id: 'd4N8nD', }); + const footerClass = `${styles.wrapper} ${className}`; + const btnClass = `${styles['back-to-top']} ${backToTopClassName}`; return ( - <footer className={`${styles.wrapper} ${className}`}> + <footer className={footerClass}> <Copyright dates={copyright.dates} icon={copyright.icon} owner={copyright.owner} /> - {navItems && ( + {navItems ? ( <Nav aria-label={ariaLabel} className={styles.nav} items={navItems} + // eslint-disable-next-line react/jsx-no-literals -- Hardcoded config kind="footer" /> - )} - <BackToTop - className={`${styles['back-to-top']} ${backToTopClassName}`} - target={topId} - /> + ) : null} + <BackToTop className={btnClass} to={topId} /> </footer> ); }; diff --git a/src/components/organisms/layout/posts-list.tsx b/src/components/organisms/layout/posts-list.tsx index e214ca7..f04ba74 100644 --- a/src/components/organisms/layout/posts-list.tsx +++ b/src/components/organisms/layout/posts-list.tsx @@ -1,4 +1,5 @@ -import { FC, Fragment, useRef } from 'react'; +/* eslint-disable max-statements */ +import { type FC, Fragment, useRef, useCallback } from 'react'; import { useIntl } from 'react-intl'; import { useIsMounted, useSettings } from '../../../utils/hooks'; import { @@ -20,9 +21,7 @@ export type Post = Omit<SummaryProps, 'titleLevel'> & { id: string | number; }; -export type YearCollection = { - [key: string]: Post[]; -}; +export type YearCollection = Record<string, Post[]>; export type PostsListProps = Pick<PaginationProps, 'baseUrl' | 'siblings'> & Pick<NoResultsProps, 'searchPage'> & { @@ -67,16 +66,16 @@ export type PostsListProps = Pick<PaginationProps, 'baseUrl' | 'siblings'> & * @returns {YearCollection} The posts sorted by year. */ const sortPostsByYear = (data: Post[]): YearCollection => { - const yearCollection: YearCollection = {}; + const yearCollection: Partial<YearCollection> = {}; data.forEach((post) => { const postYear = new Date(post.meta.dates.publication) .getFullYear() .toString(); - yearCollection[postYear] = [...(yearCollection[postYear] || []), post]; + yearCollection[postYear] = [...(yearCollection[postYear] ?? []), post]; }); - return yearCollection; + return yearCollection as YearCollection; }; /** @@ -102,7 +101,6 @@ export const PostsList: FC<PostsListProps> = ({ const lastPostRef = useRef<HTMLSpanElement>(null); const isMounted = useIsMounted(listRef); const { blog } = useSettings(); - const lastPostId = posts.length ? posts[posts.length - 1].id : 0; /** @@ -115,24 +113,22 @@ export const PostsList: FC<PostsListProps> = ({ const getList = ( allPosts: Post[], headingLevel: HeadingLevel = 2 - ): JSX.Element => { - return ( - <ol className={styles.list} ref={listRef}> - {allPosts.map(({ id, ...post }) => ( - <Fragment key={id}> - <li className={styles.item}> - <Summary {...post} titleLevel={headingLevel} /> + ): JSX.Element => ( + <ol className={styles.list} ref={listRef}> + {allPosts.map(({ id, ...post }) => ( + <Fragment key={id}> + <li className={styles.item}> + <Summary {...post} titleLevel={headingLevel} /> + </li> + {id === lastPostId && ( + <li> + <span ref={lastPostRef} tabIndex={-1} /> </li> - {id === lastPostId && ( - <li> - <span ref={lastPostRef} tabIndex={-1} /> - </li> - )} - </Fragment> - ))} - </ol> - ); - }; + )} + </Fragment> + ))} + </ol> + ); /** * Retrieve the list of posts. @@ -140,23 +136,21 @@ export const PostsList: FC<PostsListProps> = ({ * @returns {JSX.Element | JSX.Element[]} The posts list. */ const getPosts = (): JSX.Element | JSX.Element[] => { - const firstLevel = titleLevel || 2; + const firstLevel = titleLevel ?? 2; if (!byYear) return getList(posts, firstLevel); const postsPerYear = sortPostsByYear(posts); const years = Object.keys(postsPerYear).reverse(); const nextLevel = (firstLevel + 1) as HeadingLevel; - return years.map((year) => { - return ( - <section key={year} className={styles.section}> - <Heading level={firstLevel} className={styles.year}> - {year} - </Heading> - {getList(postsPerYear[year], nextLevel)} - </section> - ); - }); + return years.map((year) => ( + <section key={year} className={styles.section}> + <Heading level={firstLevel} className={styles.year}> + {year} + </Heading> + {getList(postsPerYear[year], nextLevel)} + </section> + )); }; const progressInfo = intl.formatMessage( @@ -166,7 +160,7 @@ export const PostsList: FC<PostsListProps> = ({ description: 'PostsList: loaded articles progress', id: '9MeLN3', }, - { articlesCount: posts.length, total: total } + { articlesCount: posts.length, total } ); const loadMoreBody = intl.formatMessage({ @@ -178,41 +172,43 @@ export const PostsList: FC<PostsListProps> = ({ /** * Load more posts handler. */ - const loadMorePosts = () => { + const loadMorePosts = useCallback(() => { if (lastPostRef.current) { lastPostRef.current.focus(); } - loadMore && loadMore(); - }; + if (loadMore) loadMore(); + }, [loadMore]); - const getProgressBar = () => { - return ( - <> - <ProgressBar - aria-label={progressInfo} - current={posts.length} - id="loaded-posts" - label={progressInfo} - min={1} - max={total} - /> - {showLoadMoreBtn && ( - <Button - kind="tertiary" - onClick={loadMorePosts} - disabled={isLoading} - className={styles.btn} - > - {loadMoreBody} - </Button> - )} - </> - ); - }; + const getProgressBar = () => ( + <> + <ProgressBar + aria-label={progressInfo} + current={posts.length} + // eslint-disable-next-line react/jsx-no-literals -- Id allowed. + id="loaded-posts" + label={progressInfo} + min={1} + max={total} + /> + {showLoadMoreBtn ? ( + <Button + className={styles.btn} + isDisabled={isLoading} + // eslint-disable-next-line react/jsx-no-literals -- Kind allowed. + kind="tertiary" + onClick={loadMorePosts} + > + {loadMoreBody} + </Button> + ) : null} + </> + ); const getPagination = () => { - return posts.length <= blog.postsPerPage ? ( + if (posts.length < blog.postsPerPage) return null; + + return ( <Pagination baseUrl={baseUrl} current={pageNumber} @@ -220,19 +216,15 @@ export const PostsList: FC<PostsListProps> = ({ siblings={siblings} total={total} /> - ) : ( - <></> ); }; - if (posts.length === 0) { - return <NoResults searchPage={searchPage} />; - } + if (posts.length === 0) return <NoResults searchPage={searchPage} />; return ( <> {getPosts()} - {isLoading && <Spinner />} + {isLoading ? <Spinner /> : null} {isMounted ? getProgressBar() : getPagination()} </> ); diff --git a/src/components/organisms/layout/summary.tsx b/src/components/organisms/layout/summary.tsx index cacd6d2..e7a5d48 100644 --- a/src/components/organisms/layout/summary.tsx +++ b/src/components/organisms/layout/summary.tsx @@ -1,6 +1,6 @@ -import { FC, ReactNode } from 'react'; +import type { FC, ReactNode } from 'react'; import { useIntl } from 'react-intl'; -import { type Article, type Meta as MetaType } from '../../../types'; +import type { Article, Meta as MetaType } from '../../../types'; import { useReadingTime } from '../../../utils/hooks'; import { Arrow, @@ -70,6 +70,7 @@ export const Summary: FC<SummaryProps> = ({ { title, a11y: (chunks: ReactNode) => ( + // eslint-disable-next-line react/jsx-no-literals -- SR class allowed <span className="screen-reader-text">{chunks}</span> ), } @@ -99,7 +100,7 @@ export const Summary: FC<SummaryProps> = ({ )), comments: { about: title, - count: commentsCount || 0, + count: commentsCount ?? 0, target: `${url}#comments`, }, }; @@ -107,7 +108,7 @@ export const Summary: FC<SummaryProps> = ({ return ( <article className={styles.wrapper}> - {cover && <ResponsiveImage className={styles.cover} {...cover} />} + {cover ? <ResponsiveImage className={styles.cover} {...cover} /> : null} <header className={styles.header}> <Link href={url} className={styles.link}> <Heading level={titleLevel} className={styles.title}> @@ -116,13 +117,16 @@ export const Summary: FC<SummaryProps> = ({ </Link> </header> <div className={styles.body}> + {/* eslint-disable-next-line react/no-danger -- Not safe but intro can + * contains links or formatting so we need it. */} <div dangerouslySetInnerHTML={{ __html: intro }} /> - <ButtonLink target={url} className={styles['read-more']}> + <ButtonLink className={styles['read-more']} to={url}> <> {readMore} <Arrow aria-hidden={true} className={styles.icon} + // eslint-disable-next-line react/jsx-no-literals -- Direction allowed direction="right" /> </> @@ -133,7 +137,9 @@ export const Summary: FC<SummaryProps> = ({ className={styles.meta} data={getMeta()} groupClassName={styles.meta__item} + // eslint-disable-next-line react/jsx-no-literals -- Layout allowed itemsLayout="stacked" + // eslint-disable-next-line react/jsx-no-literals -- Layout allowed layout="column" withSeparator={false} /> |
