-
-
Notifications
You must be signed in to change notification settings - Fork 637
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: migrate newsroom components (#2734)
Co-authored-by: Akshat Nema <[email protected]>%0ACo-authored-by: akshatnema <[email protected]>
- Loading branch information
1 parent
e4ece93
commit 4e91da6
Showing
15 changed files
with
771 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -183,12 +183,6 @@ | |
} | ||
], | ||
"max-depth": "error", | ||
"max-len": [ | ||
"error", | ||
{ | ||
"code": 120 | ||
} | ||
], | ||
"max-lines": [ | ||
"error", | ||
{ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import React from 'react'; | ||
|
||
interface Author { | ||
name: string; | ||
photo: string; | ||
link?: string; | ||
} | ||
|
||
interface AuthorAvatarsProps { | ||
authors: Author[]; | ||
} | ||
|
||
/** | ||
* @description This component takes an array of authors and renders their avatars. | ||
* @param {AuthorAvatarsProps} props - The component props. | ||
* @param {Author[]} props.authors - The authors to render avatars for. | ||
*/ | ||
export default function AuthorAvatars({ authors = [] }: AuthorAvatarsProps) { | ||
return ( | ||
<> | ||
{authors.map((author, index) => { | ||
const avatar = ( | ||
<img | ||
key={index} | ||
title={author.name} | ||
className={`${index > 0 ? `left- absolute${index * 7} top-0` : `mr- relative${(authors.length - 1) * 7}`} z-${(authors.length - 1 - index) * 10} size-10 rounded-full border-2 border-white object-cover hover:z-50`} | ||
src={author.photo} | ||
loading='lazy' | ||
data-testid='AuthorAvatars-img' | ||
alt={author.name} // Added alt attribute here | ||
/> | ||
); | ||
|
||
return author.link ? ( | ||
<a href={author.link} key={index} data-testid='AuthorAvatars-link'> | ||
{avatar} | ||
</a> | ||
) : ( | ||
<React.Fragment key={index}>{avatar}</React.Fragment> | ||
); | ||
})} | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import moment from 'moment'; | ||
import Link from 'next/link'; | ||
import type { Ref } from 'react'; | ||
import { forwardRef } from 'react'; | ||
import TextTruncate from 'react-text-truncate'; | ||
|
||
import { BlogPostType } from '@/types/components/navigation/BlogPostType'; | ||
import type { IBlogPost } from '@/types/post'; | ||
import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; | ||
import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; | ||
|
||
import AuthorAvatars from '../AuthorAvatars'; | ||
import Heading from '../typography/Heading'; | ||
import Paragraph from '../typography/Paragraph'; | ||
|
||
interface BlogPostItemProps { | ||
post: IBlogPost; | ||
className?: string; | ||
id?: string; | ||
} | ||
|
||
/** | ||
* @description Functional component representing a single blog post item. | ||
* @param {Object} props - Props for the BlogPostItem component. | ||
* @param {IBlogPost} props.post - The blog post data. | ||
* @param {string} [props.className=''] - Additional CSS classes for styling. | ||
* @param {string} [props.id=''] - The HTML id attribute for the component. | ||
* @param {Ref<HTMLLIElement>} ref - Reference object for the component. | ||
*/ | ||
export default forwardRef(function BlogPostItem({ post, className = '', id = '' }: BlogPostItemProps, | ||
ref: Ref<HTMLLIElement>) { | ||
let typeColors: [string, string] = ['bg-indigo-100', 'text-indigo-800']; | ||
|
||
switch (post.type.toLowerCase()) { | ||
case BlogPostType.Video: | ||
typeColors = ['bg-pink-100', 'text-pink-800']; | ||
break; | ||
case BlogPostType.Marketing: | ||
typeColors = ['bg-orange-100', 'text-orange-800']; | ||
break; | ||
case BlogPostType.Strategy: | ||
typeColors = ['bg-green-100', 'text-green-800']; | ||
break; | ||
case BlogPostType.Communication: | ||
typeColors = ['bg-teal-100', 'text-teal-800']; | ||
break; | ||
default: | ||
} | ||
|
||
return ( | ||
<li className={`rounded-lg ${className}`} ref={ref} id={id}> | ||
<article className='h-full rounded-lg'> | ||
<Link legacyBehavior href={post.slug} passHref> | ||
<a | ||
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> | ||
<Link legacyBehavior href={post.slug}> | ||
<a 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> | ||
</a> | ||
</Link> | ||
</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 className='hover:underline'> | ||
{post.authors | ||
.map((author, index) => author.link ? ( | ||
<a | ||
key={index} | ||
data-alt={author.name} | ||
href={author.link} | ||
onClick={(e) => { | ||
e.stopPropagation(); | ||
}} | ||
target='_blank' | ||
rel='noreferrer' | ||
> | ||
{author.name} | ||
</a> | ||
) : ( | ||
author.name | ||
)) | ||
.reduce((prev, curr) => [prev, ' & ', curr].join(''))} | ||
</span> | ||
</Heading> | ||
<Paragraph typeStyle={ParagraphTypeStyle.sm} className='flex'> | ||
<time dateTime={post.date}>{moment(post.date).format('MMMM D, YYYY')}</time> | ||
<span className='mx-1'>·</span> | ||
<span>{post.readingTime} min read</span> | ||
</Paragraph> | ||
</div> | ||
</div> | ||
</div> | ||
</a> | ||
</Link> | ||
</article> | ||
</li> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import moment from 'moment'; | ||
import Link from 'next/link'; | ||
import TextTruncate from 'react-text-truncate'; | ||
|
||
import { BlogPostType } from '@/types/components/navigation/BlogPostType'; | ||
import type { IBlogPost } from '@/types/post'; | ||
import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; | ||
import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; | ||
|
||
import AuthorAvatars from '../AuthorAvatars'; | ||
import Heading from '../typography/Heading'; | ||
import Paragraph from '../typography/Paragraph'; | ||
|
||
interface FeaturedBlogPostProps { | ||
post: IBlogPost; | ||
className?: string; | ||
} | ||
|
||
/** | ||
* @description Renders a featured blog post with the provided data. | ||
* @param {FeaturedBlogPostProps} props - The component props. | ||
* @param {string} [props.className=''] - Additional CSS classes for styling. | ||
*/ | ||
export default function FeaturedBlogPost({ post, className = '' }: FeaturedBlogPostProps) { | ||
let typeColors = ['bg-indigo-100', 'text-indigo-800']; | ||
|
||
switch (post.type.toLowerCase()) { | ||
case BlogPostType.Video: | ||
typeColors = ['bg-pink-100', 'text-pink-800']; | ||
break; | ||
case BlogPostType.Marketing: | ||
typeColors = ['bg-orange-100', 'text-orange-800']; | ||
break; | ||
case BlogPostType.Strategy: | ||
typeColors = ['bg-green-100', 'text-green-800']; | ||
break; | ||
case BlogPostType.Communication: | ||
typeColors = ['bg-teal-100', 'text-teal-800']; | ||
break; | ||
default: | ||
} | ||
|
||
return ( | ||
<div className={`rounded-lg ${className}`}> | ||
<article className='h-full rounded-lg'> | ||
<Link legacyBehavior href={post.slug} passHref> | ||
<a | ||
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 md:max-w-164 md:flex-row'} | ||
data-testid='FeaturedBlogPostItem-Link' | ||
> | ||
<img | ||
className='w-full object-cover md:w-56' | ||
src={post.cover} | ||
alt='' | ||
data-testid='FeaturedBlogPostItem-Img' | ||
/> | ||
<div className='flex flex-1 flex-col justify-between border-none bg-white p-6 text-left'> | ||
<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]}`} | ||
data-testid='FeaturedBlogPost-type' | ||
> | ||
{post.type} | ||
</span> | ||
</Paragraph> | ||
<Link legacyBehavior href={post.slug}> | ||
<a className='block' data-testid='FeaturedBlog-title'> | ||
<Heading level={HeadingLevel.h3} typeStyle={HeadingTypeStyle.smSemibold} className='mt-2'> | ||
{post.title} | ||
</Heading> | ||
<Paragraph typeStyle={ParagraphTypeStyle.sm} className='mt-3'> | ||
<TextTruncate element='span' line={2} text={post.excerpt} /> | ||
</Paragraph> | ||
</a> | ||
</Link> | ||
</div> | ||
<div className='mt-6 flex items-center'> | ||
<div className='relative shrink-0' data-testid='FeaturedBlog-Authorimg'> | ||
<AuthorAvatars authors={post.authors} /> | ||
</div> | ||
<div className='ml-3'> | ||
<Heading level={HeadingLevel.h3} typeStyle={HeadingTypeStyle.xsSemibold} textColor='text-gray-900'> | ||
<span className='hover:underline' data-testid='FeaturedBlogPost-AuthorName'> | ||
{post.authors | ||
.map((author, index) => author.link ? ( | ||
<a | ||
key={index} | ||
data-alt={author.name} | ||
href={author.link} | ||
onClick={(e) => { | ||
e.stopPropagation(); | ||
}} | ||
target='_blank' | ||
rel='noreferrer' | ||
> | ||
{author.name} | ||
</a> | ||
) : ( | ||
author.name | ||
)) | ||
.reduce((prev, curr) => [prev, ' & ', curr].join(''))} | ||
</span> | ||
</Heading> | ||
<Paragraph typeStyle={ParagraphTypeStyle.sm} className='flex'> | ||
<time dateTime={post.date} data-testid='FeaturedBlogPost-date'> | ||
{moment(post.date).format('MMMM D, YYYY')} | ||
</time> | ||
<span className='mx-1'>·</span> | ||
<span data-testid='FeaturedBlogPost-RT'>{post.readingTime} min read</span> | ||
</Paragraph> | ||
</div> | ||
</div> | ||
</div> | ||
</a> | ||
</Link> | ||
</article> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.