From dab72bb270ee2ee47a0b472d5e9e240cba7cbf0f Mon Sep 17 00:00:00 2001
From: Armand Philippot <git@armandphilippot.com>
Date: Fri, 13 May 2022 15:39:55 +0200
Subject: chore: handle blog pagination

---
 src/components/organisms/layout/posts-list.tsx | 73 +++++++++++++++++++++-----
 1 file changed, 60 insertions(+), 13 deletions(-)

(limited to 'src/components/organisms/layout/posts-list.tsx')

diff --git a/src/components/organisms/layout/posts-list.tsx b/src/components/organisms/layout/posts-list.tsx
index daf4491..4d77d20 100644
--- a/src/components/organisms/layout/posts-list.tsx
+++ b/src/components/organisms/layout/posts-list.tsx
@@ -1,10 +1,11 @@
+import Button from '@components/atoms/buttons/button';
 import Heading, { type HeadingLevel } from '@components/atoms/headings/heading';
-import { FC } from 'react';
+import ProgressBar from '@components/atoms/loaders/progress-bar';
+import Spinner from '@components/atoms/loaders/spinner';
+import { FC, Fragment, useRef } from 'react';
 import { useIntl } from 'react-intl';
-import Summary, { type SummaryProps } from './summary';
 import styles from './posts-list.module.scss';
-import ProgressBar from '@components/atoms/loaders/progress-bar';
-import Button from '@components/atoms/buttons/button';
+import Summary, { type SummaryProps } from './summary';
 
 export type Post = SummaryProps & {
   /**
@@ -22,10 +23,22 @@ export type PostsListProps = {
    * True to display the posts by year. Default: false.
    */
   byYear?: boolean;
+  /**
+   * Determine if the data is loading.
+   */
+  isLoading?: boolean;
+  /**
+   * Load more button handler.
+   */
+  loadMore?: () => void;
   /**
    * The posts data.
    */
   posts: Post[];
+  /**
+   * Determine if the load more button should be visible.
+   */
+  showLoadMoreBtn?: boolean;
   /**
    * The posts heading level (hn).
    */
@@ -62,29 +75,42 @@ const sortPostsByYear = (data: Post[]): YearCollection => {
  */
 const PostsList: FC<PostsListProps> = ({
   byYear = false,
+  isLoading = false,
+  loadMore,
   posts,
+  showLoadMoreBtn = false,
   titleLevel,
   total,
 }) => {
   const intl = useIntl();
+  const lastPostRef = useRef<HTMLSpanElement>(null);
 
   /**
    * Retrieve the list of posts.
    *
-   * @param {Posts[]} data - A collection fo posts.
+   * @param {Posts[]} allPosts - A collection fo posts.
    * @param {HeadingLevel} [headingLevel] - The posts heading level (hn).
    * @returns {JSX.Element} The list of posts.
    */
   const getList = (
-    data: Post[],
+    allPosts: Post[],
     headingLevel: HeadingLevel = 2
   ): JSX.Element => {
+    const lastPostId = allPosts[allPosts.length - 1].id;
+
     return (
       <ol className={styles.list}>
-        {data.map(({ id, ...post }) => (
-          <li key={id} className={styles.item}>
-            <Summary {...post} titleLevel={headingLevel} />
-          </li>
+        {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>
+            )}
+          </Fragment>
         ))}
       </ol>
     );
@@ -93,7 +119,7 @@ const PostsList: FC<PostsListProps> = ({
   /**
    * Retrieve the list of posts.
    *
-   * @returns {JSX.Element | JSX.Element[]} - The posts list.
+   * @returns {JSX.Element | JSX.Element[]} The posts list.
    */
   const getPosts = (): JSX.Element | JSX.Element[] => {
     if (!byYear) return getList(posts);
@@ -123,12 +149,23 @@ const PostsList: FC<PostsListProps> = ({
     { articlesCount: posts.length, total: total }
   );
 
-  const loadMore = intl.formatMessage({
+  const loadMoreBody = intl.formatMessage({
     defaultMessage: 'Load more articles?',
     id: 'uaqd5F',
     description: 'PostsList: load more button',
   });
 
+  /**
+   * Load more posts handler.
+   */
+  const loadMorePosts = () => {
+    if (lastPostRef.current) {
+      lastPostRef.current.focus();
+    }
+
+    loadMore && loadMore();
+  };
+
   return posts.length === 0 ? (
     <p>
       {intl.formatMessage({
@@ -140,13 +177,23 @@ const PostsList: FC<PostsListProps> = ({
   ) : (
     <>
       {getPosts()}
+      {isLoading && <Spinner />}
       <ProgressBar
         min={1}
         max={total}
         current={posts.length}
         info={progressInfo}
       />
-      <Button className={styles.btn}>{loadMore}</Button>
+      {showLoadMoreBtn && (
+        <Button
+          kind="tertiary"
+          onClick={loadMorePosts}
+          disabled={isLoading}
+          className={styles.btn}
+        >
+          {loadMoreBody}
+        </Button>
+      )}
     </>
   );
 };
-- 
cgit v1.2.3

