Skip to content

Commit

Permalink
refactor(BoxWithImage component) (#1569)
Browse files Browse the repository at this point in the history
* modify BoxWithImage and storybook to align with new size specifications

* add proper null check against text content

* add correct strapi props in preparation for CMS changes
  • Loading branch information
Spencer6497 authored Dec 27, 2024
1 parent 80a8cb5 commit 7935eb0
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 34 deletions.
45 changes: 24 additions & 21 deletions app/components/BoxWithImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,50 @@ import RichText from "./RichText";

export type BoxWithImageProps = {
image: ImageProps;
variant?: "default" | "ImgMTextL";
variant?: Variant;
identifier?: string;
heading?: HeadingProps;
content?: string;
};

const variantStyles = {
default: {
textSize: "1rem",
imageContainer: "basis-1/6",
columnBreakpoint: "md:flex-row",
},
ImgMTextL: {
textSize: "1.25rem",
imageContainer: "basis-1/3",
columnBreakpoint: "lg:flex-row",
},
} as const;
export type Variant = "ImgMTextL" | "XS" | "S" | "M" | "L" | "XL" | "XXL";

export const variantWidths: Record<Variant, string> = {
// TODO: Remove after new variant implementation

Check warning on line 16 in app/components/BoxWithImage.tsx

View workflow job for this annotation

GitHub Actions / code-quality / npm run lint

Complete the task associated to this "TODO" comment
ImgMTextL: "max-w-[280px]",
XS: "max-w-[80px]",
S: "max-w-[120px]",
M: "max-w-[280px]",
L: "max-w-[400px]",
XL: "max-w-[630px]",
XXL: "max-w-[848px]",
};

const BoxWithImage = ({
variant = "default",
variant = "S",
identifier,
heading,
image,
content,
}: BoxWithImageProps) => {
const shouldWrapByDefault = variant === "XL" || variant === "XXL";
const hasTextContent = Boolean(heading || content);
return (
<div
id={identifier}
className={`flex flex-col items-start gap-24 ${variantStyles[variant].columnBreakpoint}`}
style={{ fontSize: variantStyles[variant].textSize }}
className={`flex flex-wrap ${shouldWrapByDefault ? "md:flex-wrap" : "sm:flex-nowrap"} items-start gap-24 text-base`}
>
<div
className={`shrink-0 overflow-hidden max-w-[70ch] ${content ? variantStyles[variant].imageContainer : "basis-full"}`}
className={`lg:shrink-0 overflow-hidden ${hasTextContent ? variantWidths[variant] : "max-w-full"}`}
>
<Image {...image} />
</div>
<div className={"ds-stack-8 break-words basis-auto"}>
{heading && <Heading {...heading} />}
{content && <RichText markdown={content} />}
</div>
{hasTextContent && (
<div className={`ds-stack-8 break-words min-w-[120px] max-w-[696px]`}>
{heading && <Heading {...heading} />}
{content && <RichText markdown={content} />}
</div>
)}
</div>
);
};
Expand Down
57 changes: 57 additions & 0 deletions app/components/__test__/BoxWithImage.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { render } from "@testing-library/react";
import type { Variant } from "~/components/BoxWithImage";
import BoxWithImage, { variantWidths } from "~/components/BoxWithImage";

describe("BoxWithImage", () => {
const imageAltText = "Alt text";
it("should render", () => {
const imageText = "A beautiful image";
const headerText = "Some title description";
const { getByText, getByAltText } = render(
<BoxWithImage
image={{ alternativeText: imageAltText, url: "image.png" }}
content={imageText}
heading={{ text: headerText }}
/>,
);
expect(getByText(imageText)).toBeInTheDocument();
expect(getByText(headerText)).toBeInTheDocument();
expect(getByAltText(imageAltText)).toBeInTheDocument();
});

it.each(
Object.entries(variantWidths).map(([key, value]) => [
key as Variant,
value,
]),
)(
"image variant %s should have proper styling",
(imageVariant, expectedStyle) => {
const { getByAltText } = render(
<BoxWithImage
variant={imageVariant}
image={{ url: "image.png", alternativeText: imageAltText }}
content="Wow great image!"
/>,
);
const imageContainer = getByAltText(imageAltText).parentElement;
const shouldWrap = imageVariant === "XL" || imageVariant === "XXL";
const flexContainer = imageContainer?.parentElement;
expect(flexContainer).toHaveClass(
shouldWrap ? "md:flex-wrap" : "sm:flex-nowrap",
);
expect(imageContainer).toHaveClass(expectedStyle);
},
);

it("should display just an image if there is no text content", () => {
const { getByAltText } = render(
<BoxWithImage
image={{ url: "image.png", alternativeText: imageAltText }}
/>,
);
const image = getByAltText(imageAltText);
expect(image).toBeInTheDocument();
expect(image.parentElement).toHaveClass("max-w-full");
});
});
13 changes: 11 additions & 2 deletions app/services/cms/models/StrapiBoxWithImage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { z } from "zod";
import { type BoxWithImageProps } from "~/components/BoxWithImage";
import {
variantWidths,
type Variant,
type BoxWithImageProps,
} from "~/components/BoxWithImage";
import { omitNull } from "~/util/omitNull";
import { HasOptionalStrapiIdSchema } from "./HasStrapiId";
import { OptionalStrapiLinkIdentifierSchema } from "./HasStrapiLinkIdentifier";
Expand All @@ -8,13 +12,18 @@ import { StrapiContainerSchema } from "./StrapiContainer";
import { getHeadingProps, StrapiHeadingSchema } from "./StrapiHeading";
import { StrapiImageSchema, getImageProps } from "./StrapiImage";

// Necessary destructuring for zod enum type
const [firstWidth, ...widths] = Object.keys(variantWidths).map(
(key) => key as Variant,
);

const StrapiBoxWithImageSchema = z
.object({
heading: StrapiHeadingSchema.nullable(),
image: StrapiImageSchema,
content: z.string().nullable(),
outerBackground: StrapiBackgroundSchema.nullable(),
variant: z.enum(["ImgMTextL"]).nullable(),
variant: z.enum([firstWidth, ...widths]).nullable(),
container: StrapiContainerSchema,
})
.merge(HasOptionalStrapiIdSchema)
Expand Down
17 changes: 6 additions & 11 deletions stories/BoxWithImage.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,13 @@ export default meta;
export const Default: StoryObj<typeof meta> = {
args: {
image: { url: "public/og-image.png" },
variant: "ImgMTextL",
heading: {
text: "Heading text",
tagName: "h2",
look: "ds-heading-03-bold",
text: "Ein Pilotprojekt des Bundesministeriums der Justiz und der Justizministerien der Länder.",
tagName: "h1",
look: "ds-label-01-reg",
},
content: `Non blanditiis vel quis. A molestias id quia modi veniam sunt. Quia eligendi quos ut.
Totam quis saepe sed qui in. Beatae occaecati et aperiam non iusto sequi. Ut nihil similique aut magni neque.
- Laboriosam quae esse libero eum. Iure et veritatis voluptates. Fugiat voluptates sunt aperiam accusantium ab voluptatum doloribus veniam. Maiores esse et est.
- Quia distinctio earum accusamus aut ullam aut. Porro quis beatae ut rerum quas nemo itaque sed. Dolores voluptates in laborum deserunt cupiditate pariatur est.
Est perspiciatis blanditiis aliquam. Ut perferendis et illo eligendi aliquid. Est mollitia vel molestiae. Enim sed eius et saepe voluptatem occaecati quasi voluptas.`,
content:
"In diesem Projekt geht es darum, den Zugang zum Recht zu verbessern. Diese Seite befindet sich im Aufbau. Weitere Funktionen und Dienstleistungen werden mit der Zeit ergänzt.",
},
};

0 comments on commit 7935eb0

Please sign in to comment.