Skip to content
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

feat: enhance blog cards with read more button #3641

Closed
Closed
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
147 changes: 77 additions & 70 deletions components/navigation/BlogPostItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading';
import { ParagraphTypeStyle } from '@/types/typography/Paragraph';

import AuthorAvatars from '../AuthorAvatars';
import Button from '../buttons/Button';
import Heading from '../typography/Heading';
import Paragraph from '../typography/Paragraph';

Expand Down Expand Up @@ -54,82 +55,88 @@ export default forwardRef(function BlogPostItem(
return (
<li className={`list-none rounded-lg ${className}`} ref={ref} id={id}>
<article className='h-full rounded-lg'>
<Link href={post.slug}>
<span
className={
'flex h-full cursor-pointer flex-col divide-y divide-gray-200 overflow-hidden rounded-lg border border-gray-200 shadow-md transition-all duration-300 ease-in-out hover:shadow-lg'
}
data-testid='BlogPostItem-Link'
>
<img
className='h-48 w-full object-cover'
src={post.cover}
alt=''
loading='lazy'
data-testid='BlogPostItem-Img'
/>
<div className='flex flex-1 flex-col justify-between bg-white p-6'>
<div className='flex-1'>
<Paragraph typeStyle={ParagraphTypeStyle.sm} textColor='text-indigo-500'>
<span
className={`inline-flex items-center rounded-full px-3 py-0.5 ${typeColors[0]} ${typeColors[1]}`}
>
{post.type}
</span>
</Paragraph>
<span className='block'>
<Heading level={HeadingLevel.h5} typeStyle={HeadingTypeStyle.smSemibold} className='mt-2'>
{post.title}
</Heading>
<Paragraph typeStyle={ParagraphTypeStyle.sm} className='mt-3'>
<TextTruncate element='span' line={4} text={post.excerpt} />
</Paragraph>
<span
className={
'flex h-full cursor-pointer flex-col divide-y divide-gray-200 overflow-hidden rounded-lg border border-gray-200 bg-white shadow-sm transition-all duration-300 ease-in-out hover:border-primary-200 hover:shadow-lg'
}
data-testid='BlogPostItem-Link'
>
<img
className='h-48 w-full object-cover'
src={post.cover}
alt=''
loading='lazy'
data-testid='BlogPostItem-Img'
/>
<div className='flex flex-1 flex-col justify-between bg-white p-6'>
<div className='flex-1'>
<Paragraph typeStyle={ParagraphTypeStyle.sm} textColor='text-indigo-500'>
<span className={`inline-flex items-center rounded-full px-3 py-0.5 ${typeColors[0]} ${typeColors[1]}`}>
{post.type}
</span>
</Paragraph>
<span className='block'>
<Heading level={HeadingLevel.h5} typeStyle={HeadingTypeStyle.smSemibold} className='mt-2'>
{post.title}
</Heading>
<Paragraph typeStyle={ParagraphTypeStyle.sm} className='mt-3'>
<TextTruncate element='span' line={4} text={post.excerpt} />
</Paragraph>
</span>
</div>
<div className='mt-6 flex items-center'>
<div className='relative shrink-0'>
<AuthorAvatars authors={post.authors} />
</div>
<div className='mt-6 flex items-center'>
<div className='relative shrink-0'>
<AuthorAvatars authors={post.authors} />
</div>
<div className='ml-3'>
<Heading level={HeadingLevel.h3} typeStyle={HeadingTypeStyle.xsSemibold} textColor='text-gray-900'>
<span>
{post.authors
.map((author, index) =>
author.link ? (
<button
key={index}
data-alt={author.name}
className='cursor-pointer border-none bg-inherit p-0 hover:underline'
onClick={(e) => {
e.preventDefault();
<div className='ml-3'>
<Heading level={HeadingLevel.h3} typeStyle={HeadingTypeStyle.xsSemibold} textColor='text-gray-900'>
<span>
{post.authors
.map((author, index) =>
author.link ? (
<button
key={index}
data-alt={author.name}
className='cursor-pointer border-none bg-inherit p-0 hover:underline'
onClick={(e) => {
e.preventDefault();

// Handle the click event, e.g., navigate to author.link
window.open(author.link, '_blank');
}}
>
{author.name}
</button>
) : (
author.name
)
// Handle the click event, e.g., navigate to author.link
window.open(author.link, '_blank');
}}
>
{author.name}
</button>
Comment on lines +97 to +109
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve author link implementation.

The current implementation using button elements for author links can be improved:

  1. Using window.open directly is not recommended for security reasons
  2. The implementation doesn't handle keyboard navigation properly

Consider using Next.js Link component with proper security attributes:

-                          <button
-                            key={index}
-                            data-alt={author.name}
-                            className='cursor-pointer border-none bg-inherit p-0 hover:underline'
-                            onClick={(e) => {
-                              e.preventDefault();
-                              window.open(author.link, '_blank');
-                            }}
-                          >
-                            {author.name}
-                          </button>
+                          <Link
+                            key={index}
+                            href={author.link}
+                            target="_blank"
+                            rel="noopener noreferrer"
+                            className='hover:underline'
+                          >
+                            {author.name}
+                          </Link>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
key={index}
data-alt={author.name}
className='cursor-pointer border-none bg-inherit p-0 hover:underline'
onClick={(e) => {
e.preventDefault();
// Handle the click event, e.g., navigate to author.link
window.open(author.link, '_blank');
}}
>
{author.name}
</button>
) : (
author.name
)
// Handle the click event, e.g., navigate to author.link
window.open(author.link, '_blank');
}}
>
{author.name}
</button>
<Link
key={index}
href={author.link}
target="_blank"
rel="noopener noreferrer"
className='hover:underline'
>
{author.name}
</Link>

) : (
author.name
)
.reduce((prev, curr, index) => (
<React.Fragment key={`author-${index}`}>
{prev} & {curr}
</React.Fragment>
))}
</span>
</Heading>
<Paragraph typeStyle={ParagraphTypeStyle.sm} className='flex'>
<time dateTime={post.date}>{moment(post.date).format('MMMM D, YYYY')}</time>
<span className='mx-1'>&middot;</span>
<span>{post.readingTime} min read</span>
</Paragraph>
</div>
)
.reduce((prev, curr, index) => (
<React.Fragment key={`author-${index}`}>
{prev} & {curr}
</React.Fragment>
))}
</span>
</Heading>
<Paragraph typeStyle={ParagraphTypeStyle.sm} className='flex'>
<time dateTime={post.date}>{moment(post.date).format('MMMM D, YYYY')}</time>
</Paragraph>
</div>
</div>
</span>
</Link>
<div className='mt-4 flex items-center justify-between'>
<Paragraph typeStyle={ParagraphTypeStyle.sm} className='text-gray-600'>
{post.readingTime} min read
</Paragraph>
<Link href={post.slug}>
<Button
text='Read More →'
className='inline-flex items-center rounded-full bg-primary-500 px-4 py-1.5 text-sm font-medium text-white transition-all duration-200 hover:bg-primary-600'
data-testid='BlogPostItem-ReadMore'
/>
</Link>
</div>
</div>
</span>
</article>
</li>
);
Expand Down
13 changes: 13 additions & 0 deletions components/newsroom/FeaturedBlogPost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading';
import { ParagraphTypeStyle } from '@/types/typography/Paragraph';

import AuthorAvatars from '../AuthorAvatars';
import Button from '../buttons/Button';
import Heading from '../typography/Heading';
import Paragraph from '../typography/Paragraph';

Expand Down Expand Up @@ -107,6 +108,18 @@ export default function FeaturedBlogPost({ post, className = '' }: FeaturedBlogP
</Paragraph>
</div>
</div>
<div className='mt-4 flex items-center justify-between'>
<Paragraph typeStyle={ParagraphTypeStyle.sm} className='text-gray-600'>
{post.readingTime} min read
</Paragraph>
<span className='mx-1 text-gray-300'>•</span>
<Button
href={post.slug}
text='Read More →'
className='inline-flex items-center rounded-full bg-primary-500 px-4 py-1.5 text-sm font-medium text-white transition-all duration-200 hover:bg-primary-600'
data-testid='FeaturedBlogPost-ReadMore'
/>
</div>
</div>
</span>
</Link>
Expand Down
Loading