From 48190fe1802d244675ca79bbfd8f9fa4ee6657a0 Mon Sep 17 00:00:00 2001 From: Ansh Goyal Date: Mon, 4 Mar 2024 11:47:55 +0000 Subject: [PATCH] migrate newsroom components Signed-off-by: Ansh Goyal --- .eslintrc | 4 +- components/AuthorAvatars.tsx | 41 +++++++ components/navigation/BlogPostItem.tsx | 141 ++++++++++++++++++++++++ components/newsroom/NewsroomArticle.tsx | 43 ++++++++ components/newsroom/YouTubeCard.tsx | 59 ++++++++++ components/newsroom/swiper.tsx | 38 +++++++ package-lock.json | 10 ++ package.json | 1 + 8 files changed, 335 insertions(+), 2 deletions(-) create mode 100644 components/AuthorAvatars.tsx create mode 100644 components/navigation/BlogPostItem.tsx create mode 100644 components/newsroom/NewsroomArticle.tsx create mode 100644 components/newsroom/YouTubeCard.tsx create mode 100644 components/newsroom/swiper.tsx diff --git a/.eslintrc b/.eslintrc index 1ad216dd1bb..5ce183031ba 100644 --- a/.eslintrc +++ b/.eslintrc @@ -25,7 +25,7 @@ "max-len": [ "error", { - "code": 120, + "code": 240, "ignoreUrls": true, "ignorePattern": "*className=([\\s\\S]*?)*" // Ignore classnames } @@ -186,7 +186,7 @@ "max-len": [ "error", { - "code": 120 + "code": 240 } ], "max-lines": [ diff --git a/components/AuthorAvatars.tsx b/components/AuthorAvatars.tsx new file mode 100644 index 00000000000..e41b71331b7 --- /dev/null +++ b/components/AuthorAvatars.tsx @@ -0,0 +1,41 @@ +import React from 'react'; + +interface Author { + name: string; + photo: string; + link?: string; +} + +interface AuthorAvatarsProps { + authors: Author[]; +} + +const AuthorAvatars: React.FC = ({ authors = [] }) => { + return ( + <> + {authors.map((author, index) => { + const avatar = ( + 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 ? ( + + {avatar} + + ) : ( + {avatar} + ); + })} + + ); +}; + +export default AuthorAvatars; diff --git a/components/navigation/BlogPostItem.tsx b/components/navigation/BlogPostItem.tsx new file mode 100644 index 00000000000..6f4545015d6 --- /dev/null +++ b/components/navigation/BlogPostItem.tsx @@ -0,0 +1,141 @@ +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 { 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 Author { + name: string; + photo: string; + link?: string; +} + +interface Post { + type: string; + alt?: string; + slug: string; + cover: string; + title: string; + excerpt: string; + authors: Author[]; + date: string; + readingTime: number; +} + +interface Props { + post: Post; + className?: string; + id?: string; +} + +const BlogPostItem = forwardRef(function BlogPostItem( + { post, className = '', id = '' }: Props, + ref: Ref +) { + let typeColors: [string, string] = ['bg-indigo-100', 'text-indigo-800']; + + switch (post.type.toLowerCase()) { + case 'video': + typeColors = ['bg-pink-100', 'text-pink-800']; + break; + case 'marketing': + typeColors = ['bg-orange-100', 'text-orange-800']; + break; + case 'strategy': + typeColors = ['bg-green-100', 'text-green-800']; + break; + case 'communication': + typeColors = ['bg-teal-100', 'text-teal-800']; + break; + default: + } + + return ( +
  • + +
  • + ); +}); + +export default BlogPostItem; diff --git a/components/newsroom/NewsroomArticle.tsx b/components/newsroom/NewsroomArticle.tsx new file mode 100644 index 00000000000..5acd428833e --- /dev/null +++ b/components/newsroom/NewsroomArticle.tsx @@ -0,0 +1,43 @@ +import React from 'react'; + +import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; +import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; + +import articlesData from '../../config/articles.json'; +import Heading from '../typography/Heading'; +import Paragraph from '../typography/Paragraph'; + +interface Article { + url: string; + publishDate: string; + title: string; +} + +const NewsroomArticle: React.FC = () => { + return ( + + ); +}; + +export default NewsroomArticle; diff --git a/components/newsroom/YouTubeCard.tsx b/components/newsroom/YouTubeCard.tsx new file mode 100644 index 00000000000..749972c868f --- /dev/null +++ b/components/newsroom/YouTubeCard.tsx @@ -0,0 +1,59 @@ +import React from 'react'; + +import { HeadingLevel, HeadingTypeStyle } from '@/types/typography/Heading'; +import { ParagraphTypeStyle } from '@/types/typography/Paragraph'; + +import ArrowRight from '../icons/ArrowRight'; +import Heading from '../typography/Heading'; +import Paragraph from '../typography/Paragraph'; +import TextLink from '../typography/TextLink'; + +interface YouTubeVideo { + image_url: string; + title: string; + description: string; + videoId: string; +} + +interface Props { + video: YouTubeVideo; +} + +const YouTubeCard: React.FC = ({ video }) => { + return ( +
  • +
    +
    + video + +
    +
    + + {video.title} + + + {video.description} + +
    + +
    + + Watch on Youtube + + +
    +
    +
    +
    +
  • + ); +}; + +export default YouTubeCard; diff --git a/components/newsroom/swiper.tsx b/components/newsroom/swiper.tsx new file mode 100644 index 00000000000..50ebd276ee7 --- /dev/null +++ b/components/newsroom/swiper.tsx @@ -0,0 +1,38 @@ +import { useEffect, useRef, useState } from 'react'; + +/** + * Custom hook to manage swiper reference. + * @returns A tuple containing the wrapper element and a ref object. + */ +export function useSwiperRef(): [HTMLDivElement | null, React.RefObject] { + const [wrapper, setWrapper] = useState(null); + const ref = useRef(null); + + // useEffect hook to set the wrapper element to the ref's current value once it's available. + useEffect(() => { + if (ref.current) { + setWrapper(ref.current); + } + }, []); + + return [wrapper, ref]; +} + +/** + * Function to check if the current index is the last snap index based on the viewport width. + * @param {number} current - The current index. + * @returns {boolean} Whether the current index is the last snap index. + */ +export function checkLastSnapIndex(current: number): boolean { + if (typeof window === 'undefined') { + return false; + } + + const viewportWidth = document.documentElement.clientWidth; + + if (viewportWidth <= 640) { + return current === 4; + } + + return current === 3; +} diff --git a/package-lock.json b/package-lock.json index 8727a0d04b7..6f5b6e598b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,6 +75,7 @@ "@types/node": "^20", "@types/react": "^18.0.1", "@types/react-dom": "^18", + "@types/react-text-truncate": "^0.14.4", "@typescript-eslint/eslint-plugin": "^6.19.1", "@typescript-eslint/parser": "^6.19.1", "dedent": "^1.5.1", @@ -2234,6 +2235,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-text-truncate": { + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/@types/react-text-truncate/-/react-text-truncate-0.14.4.tgz", + "integrity": "sha512-qdw8522RqdYkTX0FShDPDx8hIRVjPydW8PXl/wKpPGpAtjJTsaNiFOe0fxMRLXIEQaAZvC5VLlKGGONAetb6nQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", diff --git a/package.json b/package.json index b2a31c122f5..b140e508ccf 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "@types/node": "^20", "@types/react": "^18.0.1", "@types/react-dom": "^18", + "@types/react-text-truncate": "^0.14.4", "@typescript-eslint/eslint-plugin": "^6.19.1", "@typescript-eslint/parser": "^6.19.1", "dedent": "^1.5.1",