From 3398acb369d9651a08ed87da557d6fe595ffe11b Mon Sep 17 00:00:00 2001 From: times-tools Date: Thu, 28 Nov 2024 09:33:51 +0000 Subject: [PATCH] feat(TMRS-482): add new CategorisedArticles component --- packages/article-extras/src/article-extras.js | 12 +++- .../article-skeleton/src/article-skeleton.js | 3 +- .../related-articles/src/related-articles.js | 3 +- .../src/templates/standard/index.js | 5 +- .../src/templates/styles/responsive.js | 6 +- .../src/components/carousel/styles.ts | 1 + .../CategorisedArticles.tsx | 44 +++++++++++++ .../__tests__/CategorisedArticles.test.tsx | 27 ++++++++ .../CategorisedArticles.test.tsx.snap | 61 +++++++++++++++++++ .../components/in-article-info-card/styles.ts | 1 + .../RecommendedArticles.tsx | 4 +- packages/ts-components/src/index.ts | 3 + .../__tests__/formatters.test.tsx | 6 +- .../linkedArticles}/formatters.ts | 4 +- .../linkedArticles}/styles.ts | 0 .../elements/Image/LazyImage/LazyImage.tsx | 2 +- .../__snapshots__/Image.test.tsx.snap | 3 +- 17 files changed, 168 insertions(+), 17 deletions(-) create mode 100644 packages/ts-components/src/components/categorised-articles/CategorisedArticles.tsx create mode 100644 packages/ts-components/src/components/categorised-articles/__tests__/CategorisedArticles.test.tsx create mode 100644 packages/ts-components/src/components/categorised-articles/__tests__/__snapshots__/CategorisedArticles.test.tsx.snap rename packages/ts-components/src/{components/recommended-articles => utils}/__tests__/formatters.test.tsx (94%) rename packages/ts-components/src/{components/recommended-articles => utils/linkedArticles}/formatters.ts (97%) rename packages/ts-components/src/{components/recommended-articles => utils/linkedArticles}/styles.ts (100%) diff --git a/packages/article-extras/src/article-extras.js b/packages/article-extras/src/article-extras.js index 4b03cb2d9bc..6f92dacdfd7 100644 --- a/packages/article-extras/src/article-extras.js +++ b/packages/article-extras/src/article-extras.js @@ -5,7 +5,7 @@ import ArticleComments from "@times-components/article-comments"; import RelatedArticles from "@times-components/related-articles"; import { MessageContext } from "@times-components/message-bar"; import SaveAndShareBar from "@times-components/save-and-share-bar"; -import { RecommendedFetch, Breadcrumb } from "@times-components/ts-components"; +import { RecommendedFetch, Breadcrumb, CategorisedArticles } from "@times-components/ts-components"; import ArticleTopics from "./article-topics"; import { @@ -35,6 +35,7 @@ const ArticleExtras = ({ section, articleHeadline, relatedArticleSlice, + categorisedArticles, relatedArticlesVisible, commentingConfig, topics, @@ -57,6 +58,8 @@ const ArticleExtras = ({ return null; }; + const { categoryArticles, parentCategoryArticles } = categorisedArticles; + /* Nativo insert Sponsored Articles after the div#sponsored-article element. They are not able to insert directly into that element hence the container div */ const sponsoredArticlesAndRelatedArticles = ( isRecommendedActive, @@ -69,6 +72,7 @@ const ArticleExtras = ({ analyticsStream={analyticsStream} isVisible={relatedArticlesVisible} slice={relatedArticleSlice} + hideBorder={!isRecommendedActive && Boolean(categoryArticles)} /> {isRecommendedActive && ( )} + {!isRecommendedActive && categoryArticles && ( + + )} + {!isRecommendedActive && parentCategoryArticles && ( + + )} PROMOTED CONTENT diff --git a/packages/article-skeleton/src/article-skeleton.js b/packages/article-skeleton/src/article-skeleton.js index d3cbc7657c0..00d92d1aa49 100644 --- a/packages/article-skeleton/src/article-skeleton.js +++ b/packages/article-skeleton/src/article-skeleton.js @@ -146,7 +146,7 @@ const ArticleSkeleton = ({ window.removeEventListener("scroll", handleScroll); }; }, []); - const { hostName, canonicalUrl, breadcrumbs } = articleDataFromRender || {}; + const { hostName, canonicalUrl, breadcrumbs, categorisedArticles } = articleDataFromRender || {}; const articleUrl = hostName && canonicalUrl ? `${hostName}${canonicalUrl}` : url; @@ -424,6 +424,7 @@ const ArticleSkeleton = ({ commentsEnabled={commentsEnabled} registerNode={registerNode} relatedArticleSlice={relatedArticleSlice} + categorisedArticles={categorisedArticles} relatedArticlesVisible={ !!observed.get("related-articles") } diff --git a/packages/related-articles/src/related-articles.js b/packages/related-articles/src/related-articles.js index d4568b73d31..18c751ae338 100644 --- a/packages/related-articles/src/related-articles.js +++ b/packages/related-articles/src/related-articles.js @@ -13,7 +13,7 @@ class RelatedArticles extends Component { } render() { - const { isVisible, onPress, slice, heading } = this.props; + const { isVisible, onPress, slice, heading, hideBorder } = this.props; if (!slice) return null; const { items, sliceName } = slice; if ( @@ -61,6 +61,7 @@ class RelatedArticles extends Component { items.map(item => diff --git a/packages/slice-layout/src/templates/standard/index.js b/packages/slice-layout/src/templates/standard/index.js index 5a6a9d8b73d..3dd3effc8bc 100644 --- a/packages/slice-layout/src/templates/standard/index.js +++ b/packages/slice-layout/src/templates/standard/index.js @@ -19,7 +19,7 @@ class StandardSlice extends Component { } render() { - const { renderItems } = this.props; + const { renderItems, hideBorder } = this.props; const items = renderItems(this.config); @@ -28,10 +28,9 @@ class StandardSlice extends Component { } const { ChildrenContainer, ConfigWrapper } = this; - return ( - + {items .map(item => ( diff --git a/packages/slice-layout/src/templates/styles/responsive.js b/packages/slice-layout/src/templates/styles/responsive.js index 432cb077a0e..64b42b94059 100644 --- a/packages/slice-layout/src/templates/styles/responsive.js +++ b/packages/slice-layout/src/templates/styles/responsive.js @@ -5,13 +5,15 @@ import { breakpoints, colours, spacing } from "@times-components/ts-styleguide"; export const SliceContainer = styled(TcView)` align-items: center; border-bottom-color: ${colours.functional.keyline}; - border-bottom-width: 1px; + border-bottom-width: ${({ hideBorder }) => hideBorder ? 0 : '1px'}; border-style: solid; flex: 1; justify-content: center; + ${({ hideBorder }) => hideBorder && 'margin-bottom: 12px'}; + @media (-webkit-min-device-pixel-ratio: 2) { - border-bottom-width: 0.5px; + border-bottom-width: ${({ hideBorder }) => hideBorder ? 0 : '0.5px'}; } `; diff --git a/packages/ts-components/src/components/carousel/styles.ts b/packages/ts-components/src/components/carousel/styles.ts index cb8ba078591..d7209a779f5 100644 --- a/packages/ts-components/src/components/carousel/styles.ts +++ b/packages/ts-components/src/components/carousel/styles.ts @@ -186,6 +186,7 @@ export const CarouselContainer = styled.div<{ } `; +// @ts-ignore export const StyledCarousel = styled(ReactElasticCarousel)<{ isWide: boolean; sectionColour: string; diff --git a/packages/ts-components/src/components/categorised-articles/CategorisedArticles.tsx b/packages/ts-components/src/components/categorised-articles/CategorisedArticles.tsx new file mode 100644 index 00000000000..a760ee76a70 --- /dev/null +++ b/packages/ts-components/src/components/categorised-articles/CategorisedArticles.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { + Slice, + SliceArticle, + MouseEventType +} from '@times-components/ts-slices'; + +import { useTrackingContext } from '../../helpers/tracking/TrackingContextProvider'; +import { + Article, + getRecommendedArticlesSlice +} from '../../utils/linkedArticles/formatters'; + +import { Header } from '../../utils/linkedArticles/styles'; + +interface CategorisedArticles { + heading: string; + articles: Article[]; +} +export const CategorisedArticles: React.FC<{ + heading: string; + articles: any; +}> = ({ heading, articles }) => { + const { fireAnalyticsEvent } = useTrackingContext(); + + const onClickHandler = (__: MouseEventType, article: SliceArticle) => { + if (fireAnalyticsEvent) { + fireAnalyticsEvent({ + action: 'Clicked', + attrs: { article_parent_name: article.headline } + }); + } + }; + + return ( +
+
{`More from ${heading}`}
+ +
+ ); +}; diff --git a/packages/ts-components/src/components/categorised-articles/__tests__/CategorisedArticles.test.tsx b/packages/ts-components/src/components/categorised-articles/__tests__/CategorisedArticles.test.tsx new file mode 100644 index 00000000000..f09425dc84e --- /dev/null +++ b/packages/ts-components/src/components/categorised-articles/__tests__/CategorisedArticles.test.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/react'; +import { CategorisedArticles } from '../CategorisedArticles'; + +describe('render CategorisedArticles', () => { + it('should render header and slice with articles when valid props provided', () => { + const mockArticles = [{ headline: 'Test Article' }]; + const mockFireEvent = jest.fn(); + + jest.mock('../../../helpers/tracking/TrackingContextProvider', () => ({ + useTrackingContext: () => ({ + fireAnalyticsEvent: mockFireEvent + }) + })); + + const { container, asFragment } = render( + + ); + + expect(asFragment()).toMatchSnapshot(); + expect( + container.querySelector('#categorised-articles') + ).toBeInTheDocument(); + expect(screen.getByText('More from Sports')).toBeInTheDocument(); + }); +}); diff --git a/packages/ts-components/src/components/categorised-articles/__tests__/__snapshots__/CategorisedArticles.test.tsx.snap b/packages/ts-components/src/components/categorised-articles/__tests__/__snapshots__/CategorisedArticles.test.tsx.snap new file mode 100644 index 00000000000..89dead9736e --- /dev/null +++ b/packages/ts-components/src/components/categorised-articles/__tests__/__snapshots__/CategorisedArticles.test.tsx.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`render CategorisedArticles should render header and slice with articles when valid props provided 1`] = ` + +
+
+ More from Sports +
+
+
+ +
+
+
+
+`; diff --git a/packages/ts-components/src/components/in-article-info-card/styles.ts b/packages/ts-components/src/components/in-article-info-card/styles.ts index d33a1d8b946..57747050934 100755 --- a/packages/ts-components/src/components/in-article-info-card/styles.ts +++ b/packages/ts-components/src/components/in-article-info-card/styles.ts @@ -117,6 +117,7 @@ export const CarouselIndicator = styled.div<{ active?: boolean }>` cursor: pointer; `; +// @ts-ignore export const StyledCarousel = styled(ReactElasticCarousel)<{ sectionColour: string; }>` diff --git a/packages/ts-components/src/components/recommended-articles/RecommendedArticles.tsx b/packages/ts-components/src/components/recommended-articles/RecommendedArticles.tsx index 4744f1f444f..808df3baf59 100644 --- a/packages/ts-components/src/components/recommended-articles/RecommendedArticles.tsx +++ b/packages/ts-components/src/components/recommended-articles/RecommendedArticles.tsx @@ -8,9 +8,9 @@ import { import { useFetch } from '../../helpers/fetch/FetchProvider'; import { useTrackingContext } from '../../helpers/tracking/TrackingContextProvider'; -import { getRecommendedArticlesSlice } from './formatters'; +import { getRecommendedArticlesSlice } from '../../utils/linkedArticles/formatters'; -import { Header } from './styles'; +import { Header } from '../../utils/linkedArticles/styles'; export const RecommendedArticles: React.FC<{ heading: string; diff --git a/packages/ts-components/src/index.ts b/packages/ts-components/src/index.ts index cf3eb1f72a7..64c1f954562 100644 --- a/packages/ts-components/src/index.ts +++ b/packages/ts-components/src/index.ts @@ -88,6 +88,9 @@ export { export { RecommendedFetch } from './components/recommended-articles/RecommendedFetch'; +export { + CategorisedArticles +} from './components/categorised-articles/CategorisedArticles'; // Helpers export { FetchProvider } from './helpers/fetch/FetchProvider'; diff --git a/packages/ts-components/src/components/recommended-articles/__tests__/formatters.test.tsx b/packages/ts-components/src/utils/__tests__/formatters.test.tsx similarity index 94% rename from packages/ts-components/src/components/recommended-articles/__tests__/formatters.test.tsx rename to packages/ts-components/src/utils/__tests__/formatters.test.tsx index 0de6f5ac431..c103682b598 100644 --- a/packages/ts-components/src/components/recommended-articles/__tests__/formatters.test.tsx +++ b/packages/ts-components/src/utils/__tests__/formatters.test.tsx @@ -1,7 +1,7 @@ -import { getArticles } from '../helpers'; -import previewData from '../../../fixtures/preview-data/recommended-articles'; +import { getArticles } from '../../components/recommended-articles/helpers'; +import previewData from '../../fixtures/preview-data/recommended-articles'; -import { getRecommendedArticlesSlice } from '../formatters'; +import { getRecommendedArticlesSlice } from '../linkedArticles/formatters'; const expectedArticles = [ { diff --git a/packages/ts-components/src/components/recommended-articles/formatters.ts b/packages/ts-components/src/utils/linkedArticles/formatters.ts similarity index 97% rename from packages/ts-components/src/components/recommended-articles/formatters.ts rename to packages/ts-components/src/utils/linkedArticles/formatters.ts index 54b0b461f8c..6d000c6b44b 100644 --- a/packages/ts-components/src/components/recommended-articles/formatters.ts +++ b/packages/ts-components/src/utils/linkedArticles/formatters.ts @@ -21,7 +21,7 @@ type Media = { type SummaryText = { __typename: string; text: string }; type Summary = { __typename: string; children: SummaryText[] }; -type Article = { +export type Article = { __typename: string; url: string; slug: string; @@ -31,7 +31,7 @@ type Article = { bylines?: Byline[]; summary?: Summary; media?: Media; - categoryPath: string; + categoryPath?: string; }; // HELPERS diff --git a/packages/ts-components/src/components/recommended-articles/styles.ts b/packages/ts-components/src/utils/linkedArticles/styles.ts similarity index 100% rename from packages/ts-components/src/components/recommended-articles/styles.ts rename to packages/ts-components/src/utils/linkedArticles/styles.ts diff --git a/packages/ts-slices/src/components/elements/Image/LazyImage/LazyImage.tsx b/packages/ts-slices/src/components/elements/Image/LazyImage/LazyImage.tsx index faba1c9e115..cd0a296cf0d 100644 --- a/packages/ts-slices/src/components/elements/Image/LazyImage/LazyImage.tsx +++ b/packages/ts-slices/src/components/elements/Image/LazyImage/LazyImage.tsx @@ -12,7 +12,7 @@ export const LazyImage: React.FC<{ }> = ({ url, alt, isRoundal, isBackground }) => { const ref: React.RefObject = React.createRef(); - const [src, setSrc] = useState(); + const [src, setSrc] = useState(url); useEffect( () => { diff --git a/packages/ts-slices/src/components/elements/Image/__tests__/__snapshots__/Image.test.tsx.snap b/packages/ts-slices/src/components/elements/Image/__tests__/__snapshots__/Image.test.tsx.snap index 0c10e5ae181..77263a2f535 100644 --- a/packages/ts-slices/src/components/elements/Image/__tests__/__snapshots__/Image.test.tsx.snap +++ b/packages/ts-slices/src/components/elements/Image/__tests__/__snapshots__/Image.test.tsx.snap @@ -11,10 +11,11 @@ exports[` should render image component correctly 1`] = ` class="sc-bxivhb gqElVf" >
image alt text