Skip to content

Commit

Permalink
add robustness with messages when type is missing element
Browse files Browse the repository at this point in the history
  • Loading branch information
IvanKiral committed Nov 22, 2024
1 parent 5c76a92 commit 4f7c227
Show file tree
Hide file tree
Showing 11 changed files with 323 additions and 174 deletions.
53 changes: 32 additions & 21 deletions src/components/FeaturedArticle.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
import React from "react";
import FeaturedContent from "./FeaturedContent";
import FeaturedComponentBase from "./FeaturedComponentBase";
import { Article } from "../model";
import { Replace } from "../utils/types";
import RenderElement from "./RenderElement";

type FeaturedArticleProps = Readonly<{
article: Partial<Article>;
article: Replace<Article, { elements: Partial<Article["elements"]> }>;
}>;

const FeaturedArticle: React.FC<FeaturedArticleProps> = ({ article }) => {
const shouldRender = article.elements?.title.value || article.elements?.introduction.value
|| article.elements?.publish_date.value || article.elements?.image.value.length;
const shouldRender = Object.entries(article.elements).length > 0;

return (
<FeaturedContent type="article" image={article.elements?.image}>
{shouldRender && (
<>
<div>
return shouldRender && (
<FeaturedComponentBase type="article" image={article.elements?.image}>
<>
<div>
<RenderElement element={article.elements.title} elementCodename="title" requiredElementType="text">
<h2 className="text-center xl:text-left text-5xl font-semibold text-burgundy">
{article.elements?.title.value}
{article.elements.title?.value}
</h2>
</RenderElement>
<RenderElement
element={article.elements.publish_date}
elementCodename="publish_date"
requiredElementType="date_time"
>
<p className="text-center xl:text-left text-gray-light mt-6 text-lg">
{article.elements?.publish_date.value
{article.elements.publish_date?.value
&& `Published on ${
new Date(article.elements.publish_date.value!).toLocaleDateString("en-US", {
month: "short",
Expand All @@ -28,18 +35,22 @@ const FeaturedArticle: React.FC<FeaturedArticleProps> = ({ article }) => {
})
}`}
</p>
</RenderElement>
<RenderElement
element={article.elements.introduction}
elementCodename="introduction"
requiredElementType="text"
>
<p className="text-center xl:text-left text-gray-700 mt-4 text-xl">
{article.elements?.introduction.value}
{article.elements.introduction?.value}
</p>
</div>
{article.elements?.introduction.value && (
<a href="#" className="text-center xl:text-left text-burgundy text-xl mt-6 font-semibold underline">
Read more
</a>
)}
</>
)}
</FeaturedContent>
</RenderElement>
</div>
<a href="#" className="text-center xl:text-left text-burgundy text-xl mt-6 font-semibold underline">
Read more
</a>
</>
</FeaturedComponentBase>
);
};

Expand Down
41 changes: 41 additions & 0 deletions src/components/FeaturedComponentBase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Elements } from "@kontent-ai/delivery-sdk";
import { FC, PropsWithChildren } from "react";
import RenderElement from "./RenderElement";

type FeaturedContentProps = PropsWithChildren<
Readonly<{
type: "article" | "event";
image?: Elements.AssetsElement;
}>
>;

const FeaturedComponentBase: FC<FeaturedContentProps> = ({ type, image, children }) => {
const img = image?.value[0];
return (
<div className="flex flex-col gap-5 xl:gap-16 xl:flex-row py-5 xl:py-[104px] items-center">
<div className="basis-1/3">
<RenderElement element={image} elementCodename="image" requiredElementType="asset">
{img && (
<>
<span className="px-3.5 py-1.5 absolute text-[12px] bg-azure text-white mt-4 ms-4 rounded-md font-bold">
{type === "event" ? "FEATURED EVENT" : "FEATURED ARTICLE"}
</span>
<img
width={640}
height={420}
src={image.value[0]?.url ?? ""}
alt={image.value[0].description ?? "image alt"}
className="object-cover rounded-lg static"
/>
</>
)}
</RenderElement>
</div>
<div className="basis-2/3 flex flex-col">
{children}
</div>
</div>
);
};

export default FeaturedComponentBase;
59 changes: 29 additions & 30 deletions src/components/FeaturedContent.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,36 @@
import { Elements } from "@kontent-ai/delivery-sdk";
import { FC, PropsWithChildren } from "react";
import { FC } from "react";
import { isArticle, isEvent, LandingPage } from "../model";
import PageSection from "./PageSection";
import FeaturedArticle from "./FeaturedArticle";
import FeaturedEvent from "./FeaturedEvent";
import Divider from "./Divider";

type FeaturedContentProps = PropsWithChildren<
Readonly<{
type: "article" | "event";
image?: Elements.AssetsElement;
}>
>;
type FeaturedContentProps = {
featuredContent: LandingPage["elements"]["featured_content"];
};

const FeaturedContent: FC<FeaturedContentProps> = ({ featuredContent }) => {
const featuredArticle = featuredContent.linkedItems.find(isArticle);
const featuredEvent = featuredContent.linkedItems.find(isEvent);

const FeaturedContent: FC<FeaturedContentProps> = ({ type, image, children }) => {
const img = image?.value[0];
return (
<div className="flex flex-col gap-5 xl:gap-16 xl:flex-row py-5 xl:py-[104px] items-center">
<div className="basis-1/3">
{img && (
<>
<span className="px-3.5 py-1.5 absolute text-[12px] bg-azure text-white mt-4 ms-4 rounded-md font-bold">
{type === "event" ? "FEATURED EVENT" : "FEATURED ARTICLE"}
</span>
<img
width={640}
height={420}
src={image.value[0]?.url ?? ""}
alt={image.value[0].description ?? "image alt"}
className="object-cover rounded-lg static"
/>
</>
<>
{featuredArticle
&& (
<PageSection color="bg-creme">
<FeaturedArticle article={featuredArticle} />
</PageSection>
)}

{featuredArticle && featuredEvent && <Divider />}

{featuredEvent
&& (
<PageSection color="bg-white">
<FeaturedEvent event={featuredEvent} />
</PageSection>
)}
</div>
<div className="basis-2/3 flex flex-col">
{children}
</div>
</div>
</>
);
};

Expand Down
82 changes: 47 additions & 35 deletions src/components/FeaturedEvent.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,79 @@
import { FC } from "react";
import FeaturedContent from "./FeaturedContent";
import FeaturedComponentBase from "./FeaturedComponentBase";
import { Event } from "../model";
import { formatDate } from "../utils/date";
import { browserParse, transformToPortableText } from "@kontent-ai/rich-text-resolver";
import { PortableText } from "@portabletext/react";
import { defaultPortableRichTextComponents } from "../utils/richtext";
import { Replace } from "../utils/types";
import RenderElement from "./RenderElement";

type FeaturedEventProps = Readonly<{
event: Partial<Event>;
event: Replace<Event, { elements: Partial<Event["elements"]> }>;
}>;

const FeaturedEvent: FC<FeaturedEventProps> = ({ event }) => {
const descriptionPortableText = transformToPortableText(browserParse(event.elements?.description.value ?? ""));

const createTag = (text: string) => (
<div key={text} className="px-4 py-2 border-solid border rounded-full border-[#1D1D1B]">
<p className="text-[#1D1D1B] text-lg">{text}</p>
</div>
);

const shouldRender = event.elements?.name.value || event.elements?.description.value !== "<p><br></p>"
|| event.elements?.start_date.value
|| event.elements?.end_date.value || event.elements?.image.value.length || event.elements?.event_topic.value.length
|| event.elements?.event_type.value.length;
const shouldRender = Object.entries(event.elements).length > 0;

return (
<FeaturedContent image={event.elements?.image} type="event">
{shouldRender
? (
<>
<div>
return shouldRender
? (
<FeaturedComponentBase image={event.elements.image} type="event">
<>
<div>
<RenderElement element={event.elements.name} elementCodename="name" requiredElementType="text">
<h2 className="text-center xl:text-left text-5xl font-semibold text-burgundy">
{event.elements?.name.value}
{event.elements.name?.value}
</h2>
</RenderElement>
<RenderElement
element={event.elements.start_date}
elementCodename="start_date"
requiredElementType="date_time"
>
<p className="text-center xl:text-left text-gray-light mt-6 text-lg">
{`${
event.elements?.start_date.value !== null
? formatDate(event.elements?.start_date.value as string)
event.elements.start_date?.value !== null
? formatDate(event.elements.start_date?.value as string)
: ""
}${
event.elements?.end_date.value !== null
? ` - ${formatDate(event.elements?.end_date.value as string)}`
event.elements.end_date?.value !== null
? ` - ${formatDate(event.elements.end_date?.value as string)}`
: ""
}`}
</p>
<div className="flex mt-6 gap-2 justify-center xl:justify-normal">
{event.elements?.event_type.value.map(t => createTag(t.name.toUpperCase()))}
{event.elements?.event_topic.value.map(t => createTag(t.name.toUpperCase()))}
</div>
</RenderElement>
<div className="flex mt-6 gap-2 justify-center xl:justify-normal">
{event.elements.event_type?.value.map(t => createTag(t.name.toUpperCase()))}
{event.elements.event_topic?.value.map(t => createTag(t.name.toUpperCase()))}
</div>
<RenderElement
element={event.elements.description}
elementCodename="description"
requiredElementType="rich_text"
>
<div className="mt-4">
<PortableText value={descriptionPortableText} components={defaultPortableRichTextComponents} />
<PortableText
value={transformToPortableText(browserParse(event.elements.description?.value ?? "<p></p>"))}
components={defaultPortableRichTextComponents}
/>
</div>
</div>
{event.elements?.description.value !== "<p><br></p>" && (
<a href="#" className="text-center xl:text-left text-burgundy text-xl mt-6 font-semibold underline">
Read more
</a>
)}
</>
)
: <></>}
</FeaturedContent>
);
</RenderElement>
</div>
{event.elements.description?.value !== "<p><br></p>" && (
<a href="#" className="text-center xl:text-left text-burgundy text-xl mt-6 font-semibold underline">
Read more
</a>
)}
</>
</FeaturedComponentBase>
)
: <></>;
};

export default FeaturedEvent;
45 changes: 26 additions & 19 deletions src/components/HeroImage.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,43 @@
import { Elements } from "@kontent-ai/delivery-sdk";
import { FC } from "react";
import RenderElement from "./RenderElement";

type HeroImageProps = Readonly<{
data: {
headline?: string;
subheadline?: string;
heroImage: Elements.AssetsElement;
headline?: Elements.TextElement;
subheadline?: Elements.TextElement;
heroImage?: Elements.AssetsElement;
};
}>;

const HeroImage: FC<HeroImageProps> = ({ data }) => {
return (
<div className="flex flex-col xl:flex-row pt-10 xl:pt-[104px] pb-10 xl:pb-[160px] gap-5">
<div className="xl:basis-1/2">
<h1 className="text-center xl:text-left font-libre text-[64px] md:text-[94px] text-burgundy font-bold leading-[64px] md:leading-[78px]">
{data.headline}
</h1>
<p className="text-center xl:text-left font-sans text-xl text-gray">
{data.subheadline}
</p>
<RenderElement element={data.headline} elementCodename="headline" requiredElementType="text">
<h1 className="text-center xl:text-left font-libre text-[64px] md:text-[94px] text-burgundy font-bold leading-[64px] md:leading-[78px]">
{data.headline?.value}
</h1>
</RenderElement>
<RenderElement element={data.subheadline} elementCodename="subheadline" requiredElementType="text">
<p className="text-center xl:text-left font-sans text-xl text-gray">{data.subheadline?.value}</p>
</RenderElement>
</div>
<div className="xl:basis-1/2">
{data.heroImage.value[0] && (
<img
className=" object-cover mx-auto"
width={670}
height={440}
src={data.heroImage.value[0].url}
alt={data.heroImage.value[0].description ?? "image-alt"}
>
</img>
)}
<RenderElement element={data.heroImage} elementCodename="hero_image" requiredElementType="text">
{data.heroImage?.value[0]
? (
<img
className=" object-cover mx-auto"
width={670}
height={440}
src={data.heroImage.value[0].url}
alt={data.heroImage.value[0].description ?? "image-alt"}
>
</img>
)
: <></>}
</RenderElement>
</div>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions src/components/PageContent.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { FC } from "react";
import { PortableText, PortableTextReactComponents, PortableTextTypeComponentProps } from "@portabletext/react";
import { Elements } from "@kontent-ai/delivery-sdk";
import { Video as VideoElement } from "../model";
import { LandingPage, Video as VideoElement } from "../model";
import Video from "./Video";
import { browserParse, PortableTextComponent, transformToPortableText } from "@kontent-ai/rich-text-resolver";
import { defaultPortableRichTextComponents } from "../utils/richtext";

type PageContentProps = {
body: Elements.RichTextElement<VideoElement>;
body: LandingPage["elements"]["body_copy"];
};

const PageContent: FC<PageContentProps> = ({ body }) => {
Expand Down
Loading

0 comments on commit 4f7c227

Please sign in to comment.