summaryrefslogtreecommitdiffstats
path: root/src/components/organisms/layout
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-05-11 19:53:09 +0200
committerArmand Philippot <git@armandphilippot.com>2022-05-13 15:46:05 +0200
commitc5b516e2c933e77b2550fe6becebacb3fbdd30eb (patch)
treeff685c6c21d5938512c6a2cd60eb92242a703059 /src/components/organisms/layout
parent9c8921db92d16b07ffc2a63ff3c80c4dcdd9ff9d (diff)
chore: add the Blog index page
Diffstat (limited to 'src/components/organisms/layout')
-rw-r--r--src/components/organisms/layout/posts-list.module.scss5
-rw-r--r--src/components/organisms/layout/posts-list.stories.tsx67
-rw-r--r--src/components/organisms/layout/posts-list.test.tsx60
-rw-r--r--src/components/organisms/layout/posts-list.tsx36
-rw-r--r--src/components/organisms/layout/summary.stories.tsx23
-rw-r--r--src/components/organisms/layout/summary.test.tsx16
-rw-r--r--src/components/organisms/layout/summary.tsx64
7 files changed, 172 insertions, 99 deletions
diff --git a/src/components/organisms/layout/posts-list.module.scss b/src/components/organisms/layout/posts-list.module.scss
index f072082..8021b2b 100644
--- a/src/components/organisms/layout/posts-list.module.scss
+++ b/src/components/organisms/layout/posts-list.module.scss
@@ -37,3 +37,8 @@
}
}
}
+
+.btn {
+ display: flex;
+ margin: auto;
+}
diff --git a/src/components/organisms/layout/posts-list.stories.tsx b/src/components/organisms/layout/posts-list.stories.tsx
index d97ad03..de0478f 100644
--- a/src/components/organisms/layout/posts-list.stories.tsx
+++ b/src/components/organisms/layout/posts-list.stories.tsx
@@ -49,6 +49,16 @@ export default {
required: false,
},
},
+ total: {
+ control: {
+ type: 'number',
+ },
+ description: 'The number of posts.',
+ type: {
+ name: 'number',
+ required: true,
+ },
+ },
},
} as ComponentMeta<typeof PostsList>;
@@ -56,23 +66,25 @@ const Template: ComponentStory<typeof PostsList> = (args) => (
<PostsList {...args} />
);
+const excerpt1 =
+ 'Esse et voluptas sapiente modi impedit unde et. Ducimus nulla ea impedit sit placeat nihil assumenda. Rem est fugiat amet quo hic. Corrupti fuga quod animi autem dolorem ullam corrupti vel aut.';
+const excerpt2 =
+ 'Illum quae asperiores quod aut necessitatibus itaque excepturi voluptas. Incidunt exercitationem ullam saepe alias consequatur sed. Quam veniam quaerat voluptatum earum quia quisquam fugiat sed perspiciatis. Et velit saepe est recusandae facilis eos eum ipsum.';
+const excerpt3 =
+ 'Sunt aperiam ut rem impedit dolor id sit. Reprehenderit ipsum iusto fugiat. Quaerat laboriosam magnam facilis. Totam sint aliquam voluptatem in quis laborum sunt eum. Enim aut debitis officiis porro iure quia nihil voluptas ipsum. Praesentium quis necessitatibus cumque quia qui velit quos dolorem.';
+
const posts: Post[] = [
{
- excerpt:
- 'Esse et voluptas sapiente modi impedit unde et. Ducimus nulla ea impedit sit placeat nihil assumenda. Rem est fugiat amet quo hic. Corrupti fuga quod animi autem dolorem ullam corrupti vel aut.',
+ excerpt: excerpt1,
id: 'post-1',
meta: {
- publication: { date: '2022-02-26' },
- readingTime: '5 minutes',
+ dates: { publication: '2022-02-26' },
+ readingTime: { wordsCount: excerpt1.split(' ').length },
thematics: [
- <a key="cat-1" href="#">
- Cat 1
- </a>,
- <a key="cat-2" href="#">
- Cat 2
- </a>,
+ { id: 'cat-1', name: 'Cat 1', url: '#' },
+ { id: 'cat-2', name: 'Cat 2', url: '#' },
],
- commentsCount: '1 comment',
+ commentsCount: 1,
},
title: 'Ratione velit fuga',
url: '#',
@@ -86,35 +98,25 @@ const posts: Post[] = [
},
},
{
- excerpt:
- 'Illum quae asperiores quod aut necessitatibus itaque excepturi voluptas. Incidunt exercitationem ullam saepe alias consequatur sed. Quam veniam quaerat voluptatum earum quia quisquam fugiat sed perspiciatis. Et velit saepe est recusandae facilis eos eum ipsum.',
+ excerpt: excerpt2,
id: 'post-2',
meta: {
- publication: { date: '2022-02-20' },
- readingTime: '8 minutes',
- thematics: [
- <a key="cat-2" href="#">
- Cat 2
- </a>,
- ],
- commentsCount: '0 comments',
+ dates: { publication: '2022-02-20' },
+ readingTime: { wordsCount: excerpt2.split(' ').length },
+ thematics: [{ id: 'cat-2', name: 'Cat 2', url: '#' }],
+ commentsCount: 0,
},
title: 'Debitis laudantium laudantium',
url: '#',
},
{
- excerpt:
- 'Sunt aperiam ut rem impedit dolor id sit. Reprehenderit ipsum iusto fugiat. Quaerat laboriosam magnam facilis. Totam sint aliquam voluptatem in quis laborum sunt eum. Enim aut debitis officiis porro iure quia nihil voluptas ipsum. Praesentium quis necessitatibus cumque quia qui velit quos dolorem.',
+ excerpt: excerpt3,
id: 'post-3',
meta: {
- publication: { date: '2021-12-20' },
- readingTime: '3 minutes',
- thematics: [
- <a key="cat-1" href="#">
- Cat 1
- </a>,
- ],
- commentsCount: '3 comments',
+ dates: { publication: '2021-12-20' },
+ readingTime: { wordsCount: excerpt3.split(' ').length },
+ thematics: [{ id: 'cat-1', name: 'Cat 1', url: '#' }],
+ commentsCount: 3,
},
title: 'Quaerat ut corporis',
url: '#',
@@ -135,6 +137,7 @@ const posts: Post[] = [
export const Default = Template.bind({});
Default.args = {
posts,
+ total: posts.length,
};
/**
@@ -144,6 +147,7 @@ export const ByYears = Template.bind({});
ByYears.args = {
posts,
byYear: true,
+ total: posts.length,
};
ByYears.decorators = [
(Story) => (
@@ -159,4 +163,5 @@ ByYears.decorators = [
export const NoResults = Template.bind({});
NoResults.args = {
posts: [],
+ total: posts.length,
};
diff --git a/src/components/organisms/layout/posts-list.test.tsx b/src/components/organisms/layout/posts-list.test.tsx
index 98af1c3..9b226ac 100644
--- a/src/components/organisms/layout/posts-list.test.tsx
+++ b/src/components/organisms/layout/posts-list.test.tsx
@@ -1,23 +1,25 @@
import { render, screen } from '@test-utils';
import PostsList, { Post } from './posts-list';
+const excerpt1 =
+ 'Esse et voluptas sapiente modi impedit unde et. Ducimus nulla ea impedit sit placeat nihil assumenda. Rem est fugiat amet quo hic. Corrupti fuga quod animi autem dolorem ullam corrupti vel aut.';
+const excerpt2 =
+ 'Illum quae asperiores quod aut necessitatibus itaque excepturi voluptas. Incidunt exercitationem ullam saepe alias consequatur sed. Quam veniam quaerat voluptatum earum quia quisquam fugiat sed perspiciatis. Et velit saepe est recusandae facilis eos eum ipsum.';
+const excerpt3 =
+ 'Sunt aperiam ut rem impedit dolor id sit. Reprehenderit ipsum iusto fugiat. Quaerat laboriosam magnam facilis. Totam sint aliquam voluptatem in quis laborum sunt eum. Enim aut debitis officiis porro iure quia nihil voluptas ipsum. Praesentium quis necessitatibus cumque quia qui velit quos dolorem.';
+
const posts: Post[] = [
{
- excerpt:
- 'Esse et voluptas sapiente modi impedit unde et. Ducimus nulla ea impedit sit placeat nihil assumenda. Rem est fugiat amet quo hic. Corrupti fuga quod animi autem dolorem ullam corrupti vel aut.',
+ excerpt: excerpt1,
id: 'post-1',
meta: {
- publication: { date: '2022-02-26' },
- readingTime: '5 minutes',
+ dates: { publication: '2022-02-26' },
+ readingTime: { wordsCount: excerpt1.split(' ').length },
thematics: [
- <a key="cat-1" href="#">
- Cat 1
- </a>,
- <a key="cat-2" href="#">
- Cat 2
- </a>,
+ { id: 'cat-1', name: 'Cat 1', url: '#' },
+ { id: 'cat-2', name: 'Cat 2', url: '#' },
],
- commentsCount: '1 comment',
+ commentsCount: 1,
},
title: 'Ratione velit fuga',
url: '#',
@@ -26,38 +28,30 @@ const posts: Post[] = [
height: 480,
src: 'http://placeimg.com/640/480',
width: 640,
+ // @ts-ignore - Needed because of the placeholder image.
+ unoptimized: true,
},
},
{
- excerpt:
- 'Illum quae asperiores quod aut necessitatibus itaque excepturi voluptas. Incidunt exercitationem ullam saepe alias consequatur sed. Quam veniam quaerat voluptatum earum quia quisquam fugiat sed perspiciatis. Et velit saepe est recusandae facilis eos eum ipsum.',
+ excerpt: excerpt2,
id: 'post-2',
meta: {
- publication: { date: '2022-02-20' },
- readingTime: '8 minutes',
- thematics: [
- <a key="cat-2" href="#">
- Cat 2
- </a>,
- ],
- commentsCount: '0 comments',
+ dates: { publication: '2022-02-20' },
+ readingTime: { wordsCount: excerpt2.split(' ').length },
+ thematics: [{ id: 'cat-2', name: 'Cat 2', url: '#' }],
+ commentsCount: 0,
},
title: 'Debitis laudantium laudantium',
url: '#',
},
{
- excerpt:
- 'Sunt aperiam ut rem impedit dolor id sit. Reprehenderit ipsum iusto fugiat. Quaerat laboriosam magnam facilis. Totam sint aliquam voluptatem in quis laborum sunt eum. Enim aut debitis officiis porro iure quia nihil voluptas ipsum. Praesentium quis necessitatibus cumque quia qui velit quos dolorem.',
+ excerpt: excerpt3,
id: 'post-3',
meta: {
- publication: { date: '2021-12-20' },
- readingTime: '3 minutes',
- thematics: [
- <a key="cat-1" href="#">
- Cat 1
- </a>,
- ],
- commentsCount: '3 comments',
+ dates: { publication: '2021-12-20' },
+ readingTime: { wordsCount: excerpt3.split(' ').length },
+ thematics: [{ id: 'cat-1', name: 'Cat 1', url: '#' }],
+ commentsCount: 3,
},
title: 'Quaerat ut corporis',
url: '#',
@@ -66,13 +60,15 @@ const posts: Post[] = [
height: 480,
src: 'http://placeimg.com/640/480',
width: 640,
+ // @ts-ignore - Needed because of the placeholder image.
+ unoptimized: true,
},
},
];
describe('PostsList', () => {
it('renders the correct number of posts', () => {
- render(<PostsList posts={posts} />);
+ render(<PostsList posts={posts} total={posts.length} />);
expect(screen.getAllByRole('article')).toHaveLength(posts.length);
});
});
diff --git a/src/components/organisms/layout/posts-list.tsx b/src/components/organisms/layout/posts-list.tsx
index 4855205..daf4491 100644
--- a/src/components/organisms/layout/posts-list.tsx
+++ b/src/components/organisms/layout/posts-list.tsx
@@ -3,6 +3,8 @@ import { FC } 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';
export type Post = SummaryProps & {
/**
@@ -28,6 +30,10 @@ export type PostsListProps = {
* The posts heading level (hn).
*/
titleLevel?: HeadingLevel;
+ /**
+ * The total posts number.
+ */
+ total: number;
};
/**
@@ -40,7 +46,7 @@ const sortPostsByYear = (data: Post[]): YearCollection => {
const yearCollection: YearCollection = {};
data.forEach((post) => {
- const postYear = new Date(post.meta.publication!.date)
+ const postYear = new Date(post.meta.dates.publication)
.getFullYear()
.toString();
yearCollection[postYear] = [...(yearCollection[postYear] || []), post];
@@ -58,6 +64,7 @@ const PostsList: FC<PostsListProps> = ({
byYear = false,
posts,
titleLevel,
+ total,
}) => {
const intl = useIntl();
@@ -106,6 +113,22 @@ const PostsList: FC<PostsListProps> = ({
});
};
+ const progressInfo = intl.formatMessage(
+ {
+ defaultMessage:
+ '{articlesCount, plural, =0 {# loaded articles} one {# loaded article} other {# loaded articles}} out of a total of {total}',
+ description: 'PostsList: loaded articles progress',
+ id: '9MeLN3',
+ },
+ { articlesCount: posts.length, total: total }
+ );
+
+ const loadMore = intl.formatMessage({
+ defaultMessage: 'Load more articles?',
+ id: 'uaqd5F',
+ description: 'PostsList: load more button',
+ });
+
return posts.length === 0 ? (
<p>
{intl.formatMessage({
@@ -115,7 +138,16 @@ const PostsList: FC<PostsListProps> = ({
})}
</p>
) : (
- <>{getPosts()}</>
+ <>
+ {getPosts()}
+ <ProgressBar
+ min={1}
+ max={total}
+ current={posts.length}
+ info={progressInfo}
+ />
+ <Button className={styles.btn}>{loadMore}</Button>
+ </>
);
};
diff --git a/src/components/organisms/layout/summary.stories.tsx b/src/components/organisms/layout/summary.stories.tsx
index 42f1d44..92736b8 100644
--- a/src/components/organisms/layout/summary.stories.tsx
+++ b/src/components/organisms/layout/summary.stories.tsx
@@ -91,18 +91,17 @@ const cover = {
unoptimized: true,
};
+const excerpt =
+ 'Perspiciatis quasi libero nemo non eligendi nam minima. Deleniti expedita tempore. Praesentium explicabo molestiae eaque consectetur vero. Quae nostrum quisquam similique. Ut hic est quas ut esse quisquam nobis.';
+
const meta = {
- publication: { date: '2022-04-11' },
- readingTime: '5 minutes',
+ dates: { publication: '2022-04-11' },
+ readingTime: { wordsCount: excerpt.split(' ').length },
thematics: [
- <a key="cat-1" href="#">
- Cat 1
- </a>,
- <a key="cat-2" href="#">
- Cat 2
- </a>,
+ { id: 'cat-1', name: 'Cat 1', url: '#' },
+ { id: 'cat-2', name: 'Cat 2', url: '#' },
],
- commentsCount: '1 comment',
+ commentsCount: 1,
};
/**
@@ -110,8 +109,7 @@ const meta = {
*/
export const Default = Template.bind({});
Default.args = {
- excerpt:
- 'Perspiciatis quasi libero nemo non eligendi nam minima. Deleniti expedita tempore. Praesentium explicabo molestiae eaque consectetur vero. Quae nostrum quisquam similique. Ut hic est quas ut esse quisquam nobis.',
+ excerpt,
meta,
title: 'Odio odit necessitatibus',
url: '#',
@@ -123,8 +121,7 @@ Default.args = {
export const WithCover = Template.bind({});
WithCover.args = {
cover,
- excerpt:
- 'Perspiciatis quasi libero nemo non eligendi nam minima. Deleniti expedita tempore. Praesentium explicabo molestiae eaque consectetur vero. Quae nostrum quisquam similique. Ut hic est quas ut esse quisquam nobis.',
+ excerpt,
meta,
title: 'Odio odit necessitatibus',
url: '#',
diff --git a/src/components/organisms/layout/summary.test.tsx b/src/components/organisms/layout/summary.test.tsx
index 09b797c..9e34254 100644
--- a/src/components/organisms/layout/summary.test.tsx
+++ b/src/components/organisms/layout/summary.test.tsx
@@ -12,17 +12,13 @@ const excerpt =
'Perspiciatis quasi libero nemo non eligendi nam minima. Deleniti expedita tempore. Praesentium explicabo molestiae eaque consectetur vero. Quae nostrum quisquam similique. Ut hic est quas ut esse quisquam nobis.';
const meta = {
- publication: { date: '2022-04-11' },
- readingTime: '5 minutes',
+ dates: { publication: '2022-04-11' },
+ readingTime: { wordsCount: excerpt.split(' ').length },
thematics: [
- <a key="cat-1" href="#">
- Cat 1
- </a>,
- <a key="cat-2" href="#">
- Cat 2
- </a>,
+ { id: 'cat-1', name: 'Cat 1', url: '#' },
+ { id: 'cat-2', name: 'Cat 2', url: '#' },
],
- commentsCount: '1 comment',
+ commentsCount: 1,
};
const title = 'Odio odit necessitatibus';
@@ -77,6 +73,6 @@ describe('Summary', () => {
it('renders some meta', () => {
render(<Summary excerpt={excerpt} meta={meta} title={title} url={url} />);
- expect(screen.getByText(meta.readingTime)).toBeInTheDocument();
+ expect(screen.getByText(meta.thematics[0].name)).toBeInTheDocument();
});
});
diff --git a/src/components/organisms/layout/summary.tsx b/src/components/organisms/layout/summary.tsx
index 8b47833..1c4a38b 100644
--- a/src/components/organisms/layout/summary.tsx
+++ b/src/components/organisms/layout/summary.tsx
@@ -5,7 +5,9 @@ import Link from '@components/atoms/links/link';
import ResponsiveImage, {
type ResponsiveImageProps,
} from '@components/molecules/images/responsive-image';
-import Meta, { MetaData } from '@components/molecules/layout/meta';
+import Meta, { type MetaData } from '@components/molecules/layout/meta';
+import { type Dates } from '@ts/types/app';
+import useReadingTime from '@utils/hooks/use-reading-time';
import { FC, ReactNode } from 'react';
import { useIntl } from 'react-intl';
import styles from './summary.module.scss';
@@ -15,15 +17,25 @@ export type Cover = Pick<
'alt' | 'src' | 'width' | 'height'
>;
-export type SummaryMeta = Pick<
- MetaData,
- | 'author'
- | 'commentsCount'
- | 'publication'
- | 'readingTime'
- | 'thematics'
- | 'update'
->;
+export type SummaryMetaLink = {
+ id: number | string;
+ name: string;
+ url: string;
+};
+
+export type SummaryMetaReadingTime = {
+ wordsCount: number;
+ onlyMinutes?: boolean;
+};
+
+export type SummaryMeta = {
+ author?: string;
+ commentsCount?: number;
+ dates: Dates;
+ readingTime: SummaryMetaReadingTime;
+ thematics?: SummaryMetaLink[];
+ topics?: SummaryMetaLink[];
+};
export type SummaryProps = {
/**
@@ -79,6 +91,36 @@ const Summary: FC<SummaryProps> = ({
),
}
);
+ const { wordsCount, onlyMinutes } = meta.readingTime;
+ const readingTime = useReadingTime(wordsCount, onlyMinutes);
+
+ const getMeta = (data: SummaryMeta): MetaData => {
+ const { author, commentsCount, dates, thematics, topics } = data;
+
+ return {
+ author,
+ publication: { date: dates.publication },
+ update:
+ dates.update && dates.publication !== dates.update
+ ? { date: dates.update }
+ : undefined,
+ readingTime,
+ thematics: thematics?.map((thematic) => (
+ <Link key={thematic.id} href={thematic.url}>
+ {thematic.name}
+ </Link>
+ )),
+ topics: topics?.map((topic) => (
+ <Link key={topic.id} href={topic.url}>
+ {topic.name}
+ </Link>
+ )),
+ comments: {
+ count: commentsCount || 0,
+ target: `${url}#comments`,
+ },
+ };
+ };
return (
<article className={styles.wrapper}>
@@ -104,7 +146,7 @@ const Summary: FC<SummaryProps> = ({
</ButtonLink>
</div>
<footer className={styles.footer}>
- <Meta data={meta} layout="column" className={styles.meta} />
+ <Meta data={getMeta(meta)} layout="column" className={styles.meta} />
</footer>
</article>
);