Skip to content

Commit

Permalink
feat: adds split image component and add it to Employee site (#926)
Browse files Browse the repository at this point in the history
* feat: adds image split module

* fix: adds style to split image

* feat: support left and right image placement

* lint

* feat: adds option to have several links

* fix: changes title and description to translateable string
  • Loading branch information
mikaelbr authored Dec 2, 2024
1 parent 2287cea commit fe6fc55
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 4 deletions.
6 changes: 3 additions & 3 deletions src/components/linkButton/LinkButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import Link from "next/link";

import { getHref } from "src/utils/link";
import { ILink } from "studio/lib/interfaces/navigation";
Expand All @@ -25,9 +25,9 @@ const LinkButton = ({ isSmall, type = "primary", link }: IButton) => {
return (
href &&
linkTitleValue && (
<a className={className} href={href}>
<Link className={className} href={href}>
{linkTitleValue}
</a>
</Link>
)
);
};
Expand Down
59 changes: 59 additions & 0 deletions src/components/sections/image-split/ImageSplit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { SanityImage } from "src/components/image/SanityImage";
import LinkButton from "src/components/linkButton/LinkButton";
import Text from "src/components/text/Text";
import { ImageSplitSection } from "studio/lib/interfaces/pages";
import { ImageAlignment } from "studio/schemas/fields/media";

import styles from "./image-split.module.css";

interface ImageSplitProps {
section: ImageSplitSection;
}

const ImageSplitComponent = ({ section }: ImageSplitProps) => {
const hasImage = section.imageExtended;
const alignment = section.imageExtended?.imageAlignment;
const showImageToLeft = hasImage && alignment == ImageAlignment.Left;
const showImageToRight = hasImage && alignment == ImageAlignment.Right;

return (
<article className={styles.imageSplit}>
{showImageToLeft && (
<div className={styles.image}>
<SanityImage image={section.imageExtended} />
</div>
)}

<div className={styles.textContainer}>
<Text type="h4" as="h2">
{section.basicTitle}
</Text>

{section.description && (
<Text type="bodyNormal">{section.description}</Text>
)}

{section.actions.length > 0 && (
<div className={styles.textContainer__link}>
{section.actions.map((action, index) => (
<LinkButton
key={action._key}
type={index === 0 ? "primary" : "secondary"}
isSmall
link={action}
/>
))}
</div>
)}
</div>

{showImageToRight && (
<div className={styles.image}>
<SanityImage image={section.imageExtended} />
</div>
)}
</article>
);
};

export default ImageSplitComponent;
36 changes: 36 additions & 0 deletions src/components/sections/image-split/ImageSplitPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use client";
import { useQuery } from "@sanity/react-loader";
import { Suspense } from "react";

import { PreviewProps } from "src/types/preview";
import { ImageSplitSection, PageBuilder } from "studio/lib/interfaces/pages";
import { PAGE_QUERY } from "studio/lib/queries/pages";

import ImageSplit from "./ImageSplit";

export default function ImageSplitComponentPreview({
sectionIndex,
initialData,
}: PreviewProps) {
const { data: newData } = useQuery<PageBuilder | null>(
PAGE_QUERY,
{ id: initialData.data._id, language: initialData.data.language },
{ initial: initialData },
);

const imageSplitSection = newData
? (newData.sections.find(
(section, index) =>
section._type === "imageSplitSection" && index === sectionIndex,
) as ImageSplitSection)
: (initialData.data.sections.find(
(section, index) =>
section._type === "imageSplitSection" && index === sectionIndex,
) as ImageSplitSection);

return (
<Suspense>
<ImageSplit section={imageSplitSection} />
</Suspense>
);
}
32 changes: 32 additions & 0 deletions src/components/sections/image-split/image-split.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.imageSplit {
max-width: var(--max-content-width-small);

display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));

gap: 3rem;
row-gap: 5.25rem;
padding: 1rem;
margin: 3.75rem auto;

box-sizing: content-box;
align-items: center;
}

.textContainer {
display: flex;
flex-direction: column;
gap: 0.5rem;
}

.textContainer__link {
margin-top: 1.5rem;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}

.image {
display: flex;
justify-content: center;
}
33 changes: 32 additions & 1 deletion src/utils/renderSection.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { QueryResponseInitial } from "@sanity/react-loader";
import React from "react";

import Article from "src/components/sections/article/Article";
import ArticlePreview from "src/components/sections/article/ArticlePreview";
Expand All @@ -13,6 +12,8 @@ import Grid from "src/components/sections/grid/Grid";
import GridPreview from "src/components/sections/grid/GridPreview";
import { Hero } from "src/components/sections/hero/Hero";
import HeroPreview from "src/components/sections/hero/HeroPreview";
import ImageSplitComponent from "src/components/sections/image-split/ImageSplit";
import ImageSplitComponentPreview from "src/components/sections/image-split/ImageSplitPreview";
import ImageSectionComponent from "src/components/sections/imageSection/ImageSectionComponent";
import ImageSectionComponentPreview from "src/components/sections/imageSection/ImageSectionComponentPreview";
import { LogoSalad } from "src/components/sections/logoSalad/LogoSalad";
Expand All @@ -26,6 +27,7 @@ import {
GridSection,
HeroSection,
ImageSection,
ImageSplitSection,
LogoSaladSection,
PageBuilder,
Section,
Expand Down Expand Up @@ -145,6 +147,26 @@ const renderImageSection = (
<ImageSectionComponent section={section} />
);
};
function ImageSplitSectionWrapper({
section,
sectionIndex,
isDraftMode,
initialData,
}: {
section: ImageSplitSection;
sectionIndex: number;
isDraftMode: boolean;
initialData: QueryResponseInitial<PageBuilder>;
}) {
return isDraftMode ? (
<ImageSplitComponentPreview
initialData={initialData}
sectionIndex={sectionIndex}
/>
) : (
<ImageSplitComponent section={section} />
);
}

const renderGridSection = (
section: GridSection,
Expand Down Expand Up @@ -218,6 +240,15 @@ const SectionRenderer = ({
isDraftMode,
initialData,
);
case "imageSplitSection":
return (
<ImageSplitSectionWrapper
section={section}
sectionIndex={sectionIndex}
isDraftMode={isDraftMode}
initialData={initialData}
/>
);
case "grid":
return renderGridSection(section, sectionIndex, isDraftMode, initialData);
case "contactBox":
Expand Down
10 changes: 10 additions & 0 deletions studio/lib/interfaces/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ export interface ImageSection {
image: IImage;
}

export interface ImageSplitSection {
_type: "imageSplitSection";
_key: string;
basicTitle: string;
imageExtended: ImageExtendedProps;
description: string;
actions: ILink[];
}

export interface GridSection {
_type: "grid";
_key: string;
Expand Down Expand Up @@ -108,6 +117,7 @@ export type Section =
| CallToActionSection
| TestimonialsSection
| ImageSection
| ImageSplitSection
| GridSection
| ContactBoxSection
| EmployeesSection;
Expand Down
9 changes: 9 additions & 0 deletions studio/lib/queries/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ const SECTIONS_FRAGMENT = groq`
${TRANSLATED_LINK_FRAGMENT}
}
},
_type == "imageSplitSection" => {
...,
"basicTitle": ${translatedFieldFragment("basicTitle")},
"description": ${translatedFieldFragment("description")},
actions[] {
...,
${TRANSLATED_LINK_FRAGMENT}
}
},
_type == "ctaSection" => {
...,
callToActions[] {
Expand Down
2 changes: 2 additions & 0 deletions studio/schemas/documents/pageBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { employees } from "studio/schemas/objects/sections/employees";
import grid from "studio/schemas/objects/sections/grid";
import hero from "studio/schemas/objects/sections/hero";
import imageSection from "studio/schemas/objects/sections/image";
import imageSplitSection from "studio/schemas/objects/sections/imagesplit";
import logoSalad from "studio/schemas/objects/sections/logoSalad";
import testimonals from "studio/schemas/objects/sections/testimonials";
import seo from "studio/schemas/objects/seo";
Expand Down Expand Up @@ -47,6 +48,7 @@ const pageBuilder = defineType({
callout,
callToAction,
testimonals,
imageSplitSection,
imageSection,
grid,
employees,
Expand Down
52 changes: 52 additions & 0 deletions studio/schemas/objects/sections/imagesplit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { defineField } from "sanity";

import { imageExtended } from "studio/schemas/fields/media";
import { titleID } from "studio/schemas/fields/text";
import { link } from "studio/schemas/objects/link";
import { firstTranslation } from "studio/utils/i18n";

export const imageID = "imageSplitSection";

export const imageSplitSection = defineField({
name: imageID,
title: "Image Split",
type: "object",
fields: [
{
name: titleID.basic,
type: "internationalizedArrayString",
title: "Title",
description:
"Enter the primary title that will be displayed at the top of the employees section.",
},

{
name: "description",
type: "internationalizedArrayString",
title: "Main content",
description:
"Enter the main content that will be displayed below the title.",
},

imageExtended,
{
name: "actions",
title: "Actions (links)",
type: "array",
of: [link],
},
],
preview: {
select: {
title: "basicTitle",
},
prepare(selection) {
const { title } = selection;
return {
title: firstTranslation(title) ?? undefined,
};
},
},
});

export default imageSplitSection;

0 comments on commit fe6fc55

Please sign in to comment.