Skip to content

Commit

Permalink
♻️ refactor: refactor Blog Hero to support art directed image
Browse files Browse the repository at this point in the history
  • Loading branch information
switchnollie committed May 21, 2024
1 parent 42d76f6 commit 2aff832
Show file tree
Hide file tree
Showing 15 changed files with 96 additions and 29 deletions.
4 changes: 3 additions & 1 deletion content/blog/android-open-source.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
---
title: Android Open Source Project
date: 2024-02-19
image: /assets/blog/aosp/hero.jpg
image:
base: /assets/blog/aosp/hero.jpg
minWidthM: /assets/blog/aosp/heroL.jpg
descriptionShort: The new-generation infotainment system in the Macan is based on Android Automotive OS. In this contributor story, Michael Eichler highlights the benefits of building upon a well-proven and established open-source platform instead of having to reinvent the wheel.
author:
name: Android Open Source Project
Expand Down
3 changes: 2 additions & 1 deletion content/blog/oss-review-toolkit.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
title: OSS Review Toolkit
date: 2023-11-03
image: /assets/blog/ort/hero.jpg
image:
base: /assets/blog/ort/hero.jpg
descriptionShort: In this contributor story, the Open Source Office (OSO) at Porsche AG explains why it has chosen OSS Review Toolkit (ORT) as their core service for all in-house developed initiatives and why they preferred to collaborate with the community rather than buying a commercial solution.
author:
name: OSS Review Toolkit (ORT)
Expand Down
3 changes: 2 additions & 1 deletion content/blog/porsche-design-system.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
title: Porsche Design System
date: 2023-08-30
image: /assets/blog/pds/hero.jpg
image:
base: /assets/blog/pds/hero.jpg
descriptionShort: The Porsche Design System provides all the fundamental UXI guidelines and pattern-based web components to build brand driven, consistent and intuitive designs for digital Porsche products. Marcel Bertram, Specialist Digital Marketing UI/UX at Porsche, explains why this project was published as an open-source project and what his team learned along this way.
author:
name: Porsche Design System
Expand Down
3 changes: 2 additions & 1 deletion content/blog/porsche-digital-campus.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
title: Research @ Porsche Digital Campus
date: 2023-12-18
image: /assets/blog/pdc/hero.jpg
image:
base: /assets/blog/pdc/hero.jpg
descriptionShort: The Porsche Digital Campus is an innovative initiative with the goal of bringing together research and real-world application in the fields of AI, machine learning, and data science.
author:
name: Porsche Digital Campus
Expand Down
25 changes: 24 additions & 1 deletion contentlayer.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import {
LocalDocument,
defineNestedType,
} from "contentlayer/source-files";
import {
MediaQueryDescriptor,
mediaQueryDescriptors,
} from "./src/config/layout";

/** @type {import('contentlayer/source-files').ComputedFields} */
const computedFields = {
Expand Down Expand Up @@ -31,6 +35,24 @@ const Author = defineNestedType(() => ({
},
}));

type ResponsiveImageFields = Partial<
Record<MediaQueryDescriptor, { type: "string"; required: false }>
>;

const ResponsiveImage = defineNestedType(() => ({
name: "ResponsiveImage",
fields: {
base: { type: "string", required: true },
...mediaQueryDescriptors.reduce<ResponsiveImageFields>(
(acc, breakpointDescriptor) => ({
...acc,
[breakpointDescriptor]: { type: "string", required: false },
}),
{},
),
},
}));

const Blog = defineDocumentType(() => ({
name: "Blog",
filePathPattern: `blog/**/*.mdx`,
Expand Down Expand Up @@ -68,7 +90,8 @@ const Blog = defineDocumentType(() => ({
required: false,
},
image: {
type: "string",
type: "nested",
of: ResponsiveImage,
description: "The image of the blog",
required: true,
},
Expand Down
Binary file modified public/assets/blog/aosp/hero.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/blog/aosp/heroL.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions src/app/blog/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Author } from "../../../components/02_molecules/author/author";
import { Section } from "../../../components/02_molecules/section/section";
import { Textblock } from "../../../components/02_molecules/textblock/Textblock";
import { BASE_URL } from "../../../config/env";
import { transformContentlayerImage } from "../../../utils/imageHelpers/transformContentlayerImage";

const getParams = (params: { slug?: string[] }): Blog | null => {
const slug = params.slug?.join("/") ?? "";
Expand Down Expand Up @@ -47,7 +48,7 @@ export async function generateMetadata({
url: `${BASE_URL}${doc.slug}`,
title,
description,
images: doc.image,
images: doc.image.base,
},
twitter: {
card: "summary_large_image",
Expand Down Expand Up @@ -81,7 +82,7 @@ const BlogPage: React.FC<PageProps> = ({ params }: PageProps) => {
title={blog.title}
description={description}
subtitle="FOSS@Porsche"
imageSrc={blog.image}
imageSrc={transformContentlayerImage(blog.image)}
imageAlt="Hero Image"
position="center center"
/>
Expand Down
2 changes: 1 addition & 1 deletion src/components/02_molecules/navigation/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export const Navigation: React.FC<NavigationProps> = () => {
className={s.navLinkImage}
>
<ExportedImage
src={firstDoc.image}
src={firstDoc.image.base}
alt={firstDoc.title}
fill
placeholder="blur"
Expand Down
5 changes: 3 additions & 2 deletions src/components/03_organisms/blogsSection/BlogsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const BlogsSection: React.FC<BlogsSectionProps> = ({
);
const firstDoc = posts[0];
const docList = isFeatured ? posts.slice(1, posts.length) : posts;

return (
<div className={s.section}>
{isFeatured && firstDoc.slug && (
Expand All @@ -31,7 +32,7 @@ export const BlogsSection: React.FC<BlogsSectionProps> = ({
title={firstDoc.title}
description={firstDoc.descriptionShort}
url={firstDoc.slug}
imageSrc={firstDoc.image}
imageSrc={firstDoc.image.base}
imageAlt=""
/>
</div>
Expand All @@ -56,7 +57,7 @@ export const BlogsSection: React.FC<BlogsSectionProps> = ({
time={blog.date}
url={blog.slug}
key={blog.slug}
imageSrc={blog.image}
imageSrc={blog.image.base}
imageAlt=""
/>
))}
Expand Down
40 changes: 29 additions & 11 deletions src/components/03_organisms/heroSection/HeroSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import {
PText,
} from "@porsche-design-system/components-react/ssr";
import s from "./heroSection.module.scss";
import {
ArtDirectionImageData,
isArtDirectionAsset,
} from "../../../utils/imageHelpers/isArtDirectionAsset";
import { ArtDirectionImage } from "../../01_atoms/artDirectionImage/ArtDirectionImage";

interface HeroSectionProps {
title?: string;
subtitle?: string;
description?: string;
imageSrc: string | StaticImageData;
imageSrc: string | StaticImageData | ArtDirectionImageData;
imageAlt: string;
position?: string;
}
Expand All @@ -27,16 +32,29 @@ export const HeroSection: React.FC<HeroSectionProps> = ({
return (
<div className={s["hero-container"]}>
<div className={s["background-image-container"]}>
<ExportedImage
className={s["hero-image"]}
style={{ objectPosition: position }}
src={imageSrc}
alt={imageAlt}
placeholder="blur"
sizes="100vw"
fill
priority
/>
{isArtDirectionAsset(imageSrc) ? (
<ArtDirectionImage
src={imageSrc}
alt={imageAlt}
className={s["hero-image"]}
placeholder="blur"
fill
priority
sizes="100vw"
style={{ objectPosition: position }}
/>
) : (
<ExportedImage
className={s["hero-image"]}
style={{ objectPosition: position }}
src={imageSrc}
alt={imageAlt}
placeholder="blur"
sizes="100vw"
fill
priority
/>
)}
<div className={s["image-overlay"]} />
</div>
<div className={s["title-container"]}>
Expand Down
1 change: 1 addition & 0 deletions src/config/env.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable n/no-process-env */
const VERCEL_URL = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: null;
Expand Down
12 changes: 7 additions & 5 deletions src/utils/imageHelpers/getArtDirectionSourceProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import { getBaseMediaQuery } from "./getBaseMediaQuery";
import type { ArtDirectionImageData } from "./isArtDirectionAsset";
import type { Entries } from "../typeHelpers/Entries";

type AllImgProps = ReturnType<typeof getImageProps>["props"];

interface SourceProps {
media: `(min-width: ${number}px)` | `(max-width: ${number}px)`;
srcSet: string | undefined;
Expand All @@ -37,9 +35,13 @@ export const getArtDirectionSourceProps = ({

const sources = srcDictEntriesSorted.map(
([breakpointDescriptor, src], index) => {
let allImageProps: AllImgProps;

allImageProps = getImageProps({ src, ...props }).props;
const allImageProps = getImageProps({
src,
...props,
// TODO: support next-export-image's loader and placeholder props
loader: (params) => params.src,
placeholder: "empty",
}).props;

let mediaQuery;
if (
Expand Down
9 changes: 6 additions & 3 deletions src/utils/imageHelpers/isArtDirectionAsset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
MediaQueryDescriptor,
mediaQueryDescriptors,
} from "../../config/layout";
import { removeContentlayerDocProps } from "./transformContentlayerImage";

export type BreakpointDescriptor = MediaQueryDescriptor | "base";

Expand All @@ -14,6 +15,8 @@ export const isArtDirectionAsset = (
src: string | StaticImageData | ArtDirectionImageData,
): src is ArtDirectionImageData =>
typeof src === "object" &&
Object.keys(src as object).every((key) =>
[...mediaQueryDescriptors, "base"].includes(key as BreakpointDescriptor),
);
Object.keys(src as object)
.filter(removeContentlayerDocProps)
.every((key) =>
[...mediaQueryDescriptors, "base"].includes(key as BreakpointDescriptor),
);
13 changes: 13 additions & 0 deletions src/utils/imageHelpers/transformContentlayerImage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ResponsiveImage } from "contentlayer/generated";
import type { ArtDirectionImageData } from "./isArtDirectionAsset";

const commonContentlayerDocProps = ["_id", "_raw", "type"];
export const removeContentlayerDocProps = (objKey: string): boolean =>
!commonContentlayerDocProps.includes(objKey);

export const transformContentlayerImage = (
image: ResponsiveImage,
): ArtDirectionImageData =>
Object.fromEntries(
Object.entries(image).filter(([key]) => removeContentlayerDocProps(key)),
);

0 comments on commit 2aff832

Please sign in to comment.