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

reduce cloudfront cost (move collection processing to server-side) #871

Merged
Merged
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
21 changes: 17 additions & 4 deletions packages/components/src/interfaces/internal/CollectionCard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,20 @@ export interface LinkCardProps extends BaseCardProps {
variant: "link"
}

export type CollectionCardProps =
| ArticleCardProps
| FileCardProps
| LinkCardProps
export type AllCardProps = ArticleCardProps | FileCardProps | LinkCardProps

// NOTE: This is client-side rendering and we want as much pre-processing
// on the server as possible to improve performance + reduce file and bandwidth size
// Thus, only the necessary props are passed to this component.
export type CollectionCardProps = Pick<
AllCardProps,
"lastUpdated" | "category" | "title" | "description" | "image"
> & {
referenceLinkHref: string | undefined
imageSrc: string | undefined
itemTitle: string
}

// NOTE: This is to ensure no additional props are being passed to this component
export type ProcessedCollectionCardProps = CollectionCardProps &
Record<string, never>
seaerchin marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 5 additions & 1 deletion packages/components/src/interfaces/internal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ export {
type ArticlePageHeaderProps,
} from "./ArticlePageHeader"
export type { BreadcrumbProps } from "./Breadcrumb"
export type {
AllCardProps,
CollectionCardProps,
ProcessedCollectionCardProps,
} from "./CollectionCard"
export type { ChildrenPagesProps } from "./ChildrenPages"
export type { CollectionCardProps } from "./CollectionCard"
export type { ContentProps } from "./Content"
export {
ContentPageHeaderSchema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { withChromaticModes } from "@isomer/storybook-config"
import type { CollectionCardProps } from "~/interfaces"
import { CollectionCard } from "./CollectionCard"

const meta: Meta<CollectionCardProps> = {
const meta: Meta<typeof CollectionCard> = {
title: "Next/Internal Components/CollectionCard",
component: CollectionCard,
argTypes: {},
Expand All @@ -16,115 +16,62 @@ const meta: Meta<CollectionCardProps> = {
},
chromatic: withChromaticModes(["desktop", "mobile"]),
},
args: {
site: {
siteName: "Isomer Next",
siteMap: {
id: "1",
title: "Home",
permalink: "/",
lastModified: "",
layout: "homepage",
summary: "",
children: [],
},
theme: "isomer-next",
isGovernment: true,
logoUrl: "https://www.isomer.gov.sg/images/isomer-logo.svg",
lastUpdated: "2021-10-01",
assetsBaseUrl: "https://cms.isomer.gov.sg",
navBarItems: [],
footerItems: {
privacyStatementLink: "https://www.isomer.gov.sg/privacy",
termsOfUseLink: "https://www.isomer.gov.sg/terms",
siteNavItems: [],
},
search: {
type: "localSearch",
searchUrl: "/search",
},
},
},
}
export default meta
type Story = StoryObj<typeof CollectionCard>

const generateArgs = ({
variant,
shouldShowDate = true,
isLastUpdatedUndefined = false,
withoutImage = false,
fileDetails,
title = "A journal on microscopic plastic and their correlation to the number of staycations enjoyed per millennials between the ages of 30-42, substantiated by research from IDK university",
description = "We've looked at how people's spending correlates with how much microscopic plastic they consumed over the year. We've looked at how people's spending correlates with how much microscopic plastic they consumed over the year.",
}: {
variant: string
shouldShowDate?: boolean
isLastUpdatedUndefined?: boolean
withoutImage?: boolean
fileDetails?: {
type: string
size: string
}
title?: string
description?: string
}): Partial<CollectionCardProps> & { shouldShowDate?: boolean } => {
return {
lastUpdated: isLastUpdatedUndefined ? undefined : "December 2, 2023",
category: "Research",
title,
url: "/",
description,
image: withoutImage
? undefined
: {
src: "https://placehold.co/500x500",
alt: "placeholder",
},
variant: variant as "link" | "article" | "file" | undefined,
fileDetails,
referenceLinkHref: "/",
imageSrc: "https://placehold.co/500x500",
itemTitle: title,
shouldShowDate,
}
}

export const Default: Story = {
args: generateArgs({ variant: "article" }),
args: generateArgs({}),
}

export const UndefinedDate: Story = {
args: generateArgs({ variant: "article", isLastUpdatedUndefined: true }),
args: generateArgs({ isLastUpdatedUndefined: true }),
}

export const HideDate: Story = {
args: generateArgs({
variant: "article",
shouldShowDate: false,
isLastUpdatedUndefined: true,
}),
}

export const ArticleWithoutImage: Story = {
args: generateArgs({ variant: "article", withoutImage: true }),
}

export const File: Story = {
args: generateArgs({
variant: "file",
fileDetails: { type: "pdf", size: "2.3 MB" },
}),
}

export const FileWithoutImage: Story = {
args: generateArgs({
variant: "file",
withoutImage: true,
fileDetails: { type: "pdf", size: "2.3 MB" },
}),
export const CardWithoutImage: Story = {
args: generateArgs({ withoutImage: true }),
}

export const ShortDescription: Story = {
args: generateArgs({
variant: "article",
title: "Short title",
description: "Short description",
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,34 @@

import { Text } from "react-aria-components"

import type { CollectionCardProps as BaseCollectionCardProps } from "~/interfaces"
import type { CollectionCardProps } from "~/interfaces"
import type { CollectionPageSchemaType } from "~/types"
import { tv } from "~/lib/tv"
import {
focusVisibleHighlight,
getFormattedDate,
getReferenceLinkHref,
isExternalUrl,
} from "~/utils"
import { focusVisibleHighlight, getFormattedDate } from "~/utils"
import { ImageClient } from "../../complex/Image"
import { Link } from "../Link"

type CollectionCardProps = BaseCollectionCardProps & {
shouldShowDate?: boolean
}

const collectionCardLinkStyle = tv({
extend: focusVisibleHighlight,
base: "prose-title-md-semibold line-clamp-3 w-fit underline-offset-4 hover:underline",
})

export const CollectionCard = ({
shouldShowDate = true,
LinkComponent,
url,
lastUpdated,
title,
description,
category,
image,
site,
...props
}: CollectionCardProps): JSX.Element => {
const file = props.variant === "file" ? props.fileDetails : null
const itemTitle = `${title}${file ? ` [${file.type.toUpperCase()}, ${file.size.toUpperCase()}]` : ""}`
const imgSrc =
isExternalUrl(image?.src) || site.assetsBaseUrl === undefined
? image?.src
: `${site.assetsBaseUrl}${image?.src}`

referenceLinkHref,
imageSrc,
itemTitle,
siteAssetsBaseUrl,
shouldShowDate = true,
}: CollectionCardProps & {
shouldShowDate?: boolean
siteAssetsBaseUrl: string | undefined
LinkComponent: CollectionPageSchemaType["LinkComponent"]
}): JSX.Element => {
return (
<div className="flex border-collapse flex-col gap-3 border-b border-divider-medium py-5 first:border-t lg:flex-row lg:gap-6">
{shouldShowDate && (
Expand All @@ -52,7 +41,7 @@ export const CollectionCard = ({
<h3 className="inline-block">
<Link
LinkComponent={LinkComponent}
href={getReferenceLinkHref(url, site.siteMap, site.assetsBaseUrl)}
href={referenceLinkHref}
className={collectionCardLinkStyle()}
>
<span title={itemTitle}>{itemTitle}</span>
Expand All @@ -71,11 +60,11 @@ export const CollectionCard = ({
{image && (
<div className="relative mt-3 min-h-40 w-full shrink-0 lg:ml-4 lg:mt-0 lg:h-auto lg:w-1/4">
<ImageClient
src={imgSrc || ""}
src={imageSrc || ""}
alt={image.alt}
width="100%"
className="absolute left-0 h-full w-full rounded object-cover"
assetsBaseUrl={site.assetsBaseUrl}
assetsBaseUrl={siteAssetsBaseUrl}
/>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,3 +371,17 @@ export const AllResultsNoDate: Story = {
await expect(lastWordOccurences.length).toBe(10)
},
}

export const FileCard: Story = {
args: generateArgs({
collectionItems: [COLLECTION_ITEMS[1]] as IsomerSitemap[],
}),
}

export const FileCardNoImage: Story = {
args: generateArgs({
collectionItems: [
{ ...COLLECTION_ITEMS[1], image: undefined } as IsomerSitemap,
],
}),
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import type { Exact } from "type-fest"

import type { CollectionPageSchemaType, IsomerSiteProps } from "~/engine"
import type { CollectionCardProps } from "~/interfaces"
import type { AllCardProps, ProcessedCollectionCardProps } from "~/interfaces"
import {
getBreadcrumbFromSiteMap,
getParsedDate,
getReferenceLinkHref,
getSitemapAsArray,
isExternalUrl,
} from "~/utils"
import { Skeleton } from "../Skeleton"
import CollectionClient from "./CollectionClient"
Expand All @@ -12,7 +16,7 @@ import { getAvailableFilters, shouldShowDate } from "./utils"
const getCollectionItems = (
site: IsomerSiteProps,
permalink: string,
): CollectionCardProps[] => {
): AllCardProps[] => {
let currSitemap = site.siteMap
const permalinkParts = permalink.split("/")

Expand Down Expand Up @@ -84,7 +88,7 @@ const getCollectionItems = (
variant: "article",
url: item.permalink,
}
}) satisfies CollectionCardProps[]
}) satisfies AllCardProps[]

return transformedItems.sort((a, b) => {
// Sort by last updated date, tiebreaker by title
Expand All @@ -102,7 +106,42 @@ const getCollectionItems = (
}

return a.rawDate < b.rawDate ? 1 : -1
}) as CollectionCardProps[]
}) as AllCardProps[]
}

const processedCollectionItems = (
items: AllCardProps[],
): ProcessedCollectionCardProps[] => {
return items.map((item) => {
const {
site,
variant,
lastUpdated,
category,
title,
description,
image,
url,
} = item
const file = variant === "file" ? item.fileDetails : null
return {
lastUpdated,
category,
title,
description,
image,
referenceLinkHref: getReferenceLinkHref(
url,
site.siteMap,
site.assetsBaseUrl,
),
imageSrc:
isExternalUrl(item.image?.src) || site.assetsBaseUrl === undefined
? item.image?.src
: `${site.assetsBaseUrl}${item.image?.src}`,
itemTitle: `${item.title}${file ? ` [${file.type.toUpperCase()}, ${file.size.toUpperCase()}]` : ""}`,
seaerchin marked this conversation as resolved.
Show resolved Hide resolved
} as Exact<ProcessedCollectionCardProps, ProcessedCollectionCardProps>
})
}

const CollectionLayout = ({
Expand All @@ -115,6 +154,7 @@ const CollectionLayout = ({
const { permalink } = page

const items = getCollectionItems(site, permalink)
const processedItems = processedCollectionItems(items)
const breadcrumb = getBreadcrumbFromSiteMap(
site.siteMap,
page.permalink.split("/").slice(1),
Expand All @@ -131,11 +171,11 @@ const CollectionLayout = ({
<CollectionClient
page={page}
breadcrumb={breadcrumb}
items={items}
filters={getAvailableFilters(items)}
shouldShowDate={shouldShowDate(items)}
items={processedItems}
filters={getAvailableFilters(processedItems)}
shouldShowDate={shouldShowDate(processedItems)}
siteAssetsBaseUrl={site.assetsBaseUrl}
LinkComponent={LinkComponent}
site={site}
/>
</Skeleton>
)
Expand Down
Loading
Loading