Skip to content

feat(blog) Migrate BlogCard component #5323

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`BlogCard component renders correctly 1`] = `
<div>
<div
class="blogCard"
>
<div
class="title"
>
<a
href="/undefined/blog/category-mock/sample-blog"
>
Sample Test Blog
</a>
<div
class="metadata"
>
<a
class="category"
href="/undefined/blog/category-mock"
>
category-mock
</a>
<span>
1 min read
</span>
</div>
</div>
<div
class="content"
>
<h4>
April 21, 2023
</h4>
<p>
components.blog.blogCard.author.by

<span>
Bat Man
</span>
</p>
</div>
</div>
</div>
`;
32 changes: 32 additions & 0 deletions components/Blog/BlogCard/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { render } from '@testing-library/react';
import { IntlProvider } from 'react-intl';

import BlogCard from '..';

jest.mock('next/router', () => ({
useRouter: jest.fn().mockReturnValue({}),
}));

jest.mock('../../../../hooks/useLocale', () => ({
useLocale: jest.fn().mockReturnValue({
currentLocale: {},
}),
}));

describe('BlogCard component', () => {
it('renders correctly', () => {
const { container } = render(
<IntlProvider locale="en" onError={() => {}}>
<BlogCard
author="Bat Man"
category="category-mock"
date="2023-04-21 23:40:56.77"
slug="/blog/category-mock/sample-blog"
title="Sample Test Blog"
readingTime="1 min read"
/>
</IntlProvider>
);
expect(container).toMatchSnapshot();
});
});
72 changes: 72 additions & 0 deletions components/Blog/BlogCard/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
.blogCard {
display: flex;
flex-direction: column;
padding: var(--space-32) var(--space-24) var(--space-32) 0;

@media (max-width: 900px) {
padding: var(--space-12) var(--space-24);
}

.title {
background-color: var(--color-blog-card-background);
border-radius: 5px;
display: flex;
flex: 1 1 0px;
flex-direction: column;
padding: 1rem 1.5rem;

a {
color: var(--color-text-accent);
font-size: 2em;
margin-bottom: 10px;
text-decoration: none;

&:hover {
text-decoration: underline;
}
}
}

.metadata {
align-self: flex-start;
background-color: var(--color-dropdown-hover);
border-radius: 1rem;
display: flex;
margin-top: auto;
padding: 0.25rem;

span,
a {
color: var(--color-text-high-contrast);
font-size: var(--font-size-body3);
padding: 0.125rem 0.5rem;

&.category {
background-color: var(--color-dropdown-background);
border-radius: 1rem;
margin: 0;
}
}
}

.content {
justify-self: flex-end;

h4 {
margin: 0;
margin-top: 7px;
opacity: 0.7;
}

p {
margin: 7px 0;
opacity: 0.8;

li {
display: inline;
list-style: none;
margin: 0 3px;
}
}
}
}
18 changes: 18 additions & 0 deletions components/Blog/BlogCard/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import BlockQuote from './index';
import type { Meta as MetaObj, StoryObj } from '@storybook/react';

type Story = StoryObj<typeof BlockQuote>;
type Meta = MetaObj<typeof BlockQuote>;

export const Default: Story = {
args: {
author: 'Bat Man',
category: 'category-mock',
date: '2023-04-21 23:40:56.77',
slug: '/blog/category-mock/sample-blog',
title: 'Sample Test Blog',
readingTime: '1 min read',
},
};

export default { component: BlockQuote } as Meta;
50 changes: 50 additions & 0 deletions components/Blog/BlogCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { FormattedDate, FormattedMessage } from 'react-intl';
import styles from './index.module.scss';
import LocalizedLink from '../../LocalizedLink';
import navigation from '../../../navigation.json';
import type { BlogPost } from '../../../types';
import type { FC } from 'react';

const getBlogCategoryUrl = (category: string): string =>
`${navigation.blog.link}/${category}/`;

type BlogCardProps = Omit<BlogPost, 'file'>;

const BlogCard: FC<BlogCardProps> = ({
title,
author,
date,
category,
readingTime,
slug,
}) => (
<div className={styles.blogCard}>
<div className={styles.title}>
<LocalizedLink href={slug}>{title}</LocalizedLink>
<div className={styles.metadata}>
{category && (
<LocalizedLink
className={styles.category}
href={getBlogCategoryUrl(category)}
>
{category}
</LocalizedLink>
)}
<span>{readingTime}</span>
</div>
</div>
<div className={styles.content}>
<h4>
<FormattedDate value={date} day="numeric" month="long" year="numeric" />
</h4>
{author && (
<p>
<FormattedMessage id="components.blog.blogCard.author.by" />{' '}
<span>{author}</span>
</p>
)}
</div>
</div>
);

export default BlogCard;
1 change: 1 addition & 0 deletions i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"components.article.editLink.title.edit": "Edit this page on GitHub",
"components.article.editLink.title.translate": "Interested to help with translations?",
"components.common.languageSelector.button.title": "Switch Language",
"components.blog.blogCard.author.by": "by",
"components.footer.links.trademark": "Trademark Policy",
"components.footer.links.privacy": "Privacy Policy",
"components.footer.links.codeOfConduct": "Code of Conduct",
Expand Down
1 change: 1 addition & 0 deletions types/blog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface BlogPost {
date: string;
category: string;
slug: string;
readingTime?: string; // TODO: verify this works when implementing blog
file: string;
}

Expand Down