diff --git a/src/components/customerCases/customerCase/sections/text/TextSection.tsx b/src/components/customerCases/customerCase/sections/text/TextSection.tsx index 44d0caf17..148408288 100644 --- a/src/components/customerCases/customerCase/sections/text/TextSection.tsx +++ b/src/components/customerCases/customerCase/sections/text/TextSection.tsx @@ -1,4 +1,6 @@ +import CustomLink from "src/components/link/CustomLink"; import Text from "src/components/text/Text"; +import { LinkType } from "studio/lib/interfaces/navigation"; import { TextBlock } from "studioShared/lib/interfaces/textBlock"; import styles from "./textSection.module.css"; @@ -12,11 +14,36 @@ export default function TextSection({ section }: TextSectionProps) {
- {section.text} +
+ {section.sectionTitle && ( + + {section.sectionTitle} + + )} + {section.text} +
+
+ {section.url && ( + + )} +
diff --git a/src/components/customerCases/customerCase/sections/text/textSection.module.css b/src/components/customerCases/customerCase/sections/text/textSection.module.css index bfdc18925..94a3608ee 100644 --- a/src/components/customerCases/customerCase/sections/text/textSection.module.css +++ b/src/components/customerCases/customerCase/sections/text/textSection.module.css @@ -2,6 +2,7 @@ display: flex; flex-direction: column; align-items: center; + gap: 2rem; } .content { @@ -14,8 +15,24 @@ background-color: var(--primary-yellow-warning); } +.innerContent { + display: flex; + flex-direction: column; + justify-content: space-between; +} + .highlighted .innerContent { background-color: var(--primary-bg); border-radius: 1.25rem; padding: 1rem; + height: fit-content; +} +.header { + margin-bottom: 16px; +} + +.link { + margin-top: 2rem; + text-decoration: none; + color: var(--primary-black-darker); } diff --git a/src/components/text/Text.tsx b/src/components/text/Text.tsx index 24e32a9ce..d233234db 100644 --- a/src/components/text/Text.tsx +++ b/src/components/text/Text.tsx @@ -8,6 +8,7 @@ export type TextType = | "h5" | "h6" | "desktopLink" + | "desktopLinkBig" | "labelSmall" | "labelLight" | "labelRegular" @@ -32,6 +33,7 @@ const elementMap: { [key in TextType]: keyof JSX.IntrinsicElements } = { h5: "h5", h6: "h6", desktopLink: "p", + desktopLinkBig: "p", labelSmall: "p", labelLight: "p", labelRegular: "p", @@ -56,6 +58,7 @@ const classMap: { [key in TextType]?: string } = { h5: styles.h5, h6: styles.h6, desktopLink: styles.desktopLink, + desktopLinkBig: styles.desktopLinkBig, labelSmall: styles.labelSmall, labelLight: styles.labelLight, labelRegular: styles.labelRegular, diff --git a/src/components/text/text.module.css b/src/components/text/text.module.css index 9be875174..82e24402e 100644 --- a/src/components/text/text.module.css +++ b/src/components/text/text.module.css @@ -45,8 +45,14 @@ line-height: 120%; /* 1.8rem */ } +.h5 { + font-size: 1.25rem; + font-style: normal; + font-weight: 400; + line-height: 120%; +} + /* TODO: add font variables */ -/* .h5 */ /* .h6 */ .desktopLink { @@ -56,6 +62,14 @@ line-height: 110%; } +.desktopLinkBig { + font-size: 1.25rem; + font-style: normal; + font-weight: 300; + line-height: 120%; + text-decoration-line: underline; +} + .bodyXl { font-size: 2.125rem; font-weight: 500; diff --git a/studioShared/lib/interfaces/textBlock.ts b/studioShared/lib/interfaces/textBlock.ts index 2c5a60491..f72a44d95 100644 --- a/studioShared/lib/interfaces/textBlock.ts +++ b/studioShared/lib/interfaces/textBlock.ts @@ -1,6 +1,8 @@ export interface TextBlock { _key: string; _type: "textBlock"; + sectionTitle?: string; text: string; - highlighted?: boolean; + url?: string; + textBlockType?: string; } diff --git a/studioShared/lib/queries/customerCases.ts b/studioShared/lib/queries/customerCases.ts index 17abdf681..bd04ded3c 100644 --- a/studioShared/lib/queries/customerCases.ts +++ b/studioShared/lib/queries/customerCases.ts @@ -30,8 +30,10 @@ export const CUSTOMER_CASES_QUERY = groq` export const BASE_SECTIONS_FRAGMENT = groq` _type == "textBlock" => { + "sectionTitle": ${translatedFieldFragment("sectionTitle")}, "text": ${translatedFieldFragment("text")}, - highlighted + url, + textBlockType, }, _type == "imageBlock" => { "images": images[] { diff --git a/studioShared/schemas/fields/customerCaseProjectInfo.ts b/studioShared/schemas/fields/customerCaseProjectInfo.ts index 709c83746..c89c2d620 100644 --- a/studioShared/schemas/fields/customerCaseProjectInfo.ts +++ b/studioShared/schemas/fields/customerCaseProjectInfo.ts @@ -49,7 +49,7 @@ export const customerCaseProjectInfo = defineField({ ], preview: { select: { - item: "delivery", + delivery: "delivery", }, prepare({ delivery }) { if (!isInternationalizedString(delivery)) { diff --git a/studioShared/schemas/objects/richTextBlock.ts b/studioShared/schemas/objects/richTextBlock.ts new file mode 100644 index 000000000..109995dbe --- /dev/null +++ b/studioShared/schemas/objects/richTextBlock.ts @@ -0,0 +1,56 @@ +import { defineField } from "sanity"; + +import { isInternationalizedRichText } from "studio/lib/interfaces/global"; +import { firstTranslation } from "studio/utils/i18n"; +import { richTextPreview } from "studio/utils/preview"; + +const richTextBlock = defineField({ + name: "richTextBlock", + title: "Rich Text Block", + type: "object", + fields: [ + { + name: "title", + title: "Section Title", + type: "internationalizedArrayString", + }, + { + name: "richText", + title: "Rich Text", + type: "internationalizedArrayRichText", + }, + { + name: "highlighted", + title: "Highlighted", + type: "boolean", + description: "Display the rich text with a highlight frame", + initialValue: false, + }, + ], + preview: { + select: { + richText: "richText", + highlighted: "highlighted", + }, + prepare({ richText, highlighted }) { + if (!isInternationalizedRichText(richText)) { + throw new TypeError( + `Expected 'richText' to be InternationalizedRichText, was ${typeof richText}`, + ); + } + const translatedRichText = firstTranslation(richText); + return { + title: + translatedRichText !== null + ? richTextPreview(translatedRichText) + : undefined, + subtitle: + typeof highlighted === "boolean" && highlighted + ? "Highlighted" + : undefined, + }; + }, + }, +}); + +export default richTextBlock; diff --git a/studioShared/schemas/objects/textBlock.ts b/studioShared/schemas/objects/textBlock.ts index b3576d8b2..b4988b0dc 100644 --- a/studioShared/schemas/objects/textBlock.ts +++ b/studioShared/schemas/objects/textBlock.ts @@ -7,31 +7,98 @@ const textBlock = defineField({ title: "Text Block", type: "object", fields: [ + { + name: "sectionTitle", + title: "Section Title", + type: "internationalizedArrayString", + description: + "Section title is optional, and cannot be more than 50 characters.", + validation: (rule) => + rule.custom<{ value: string; _type: string; _key: string }[]>( + (value) => { + if (!value) return true; + + const invalidItems = value.filter( + (item) => + typeof item.value === "string" && item.value.length > 50, + ); + + if (invalidItems.length > 0) { + return invalidItems.map((item) => ({ + message: "Title cannot be more than 50 characters long.", + path: [{ _key: item._key }, "value"], + })); + } + + return true; + }, + ), + }, { name: "text", - title: "Text", + title: "Block text content", type: "internationalizedArrayText", + description: + "Please type the text content for the block. This field is required, and cannot be more than 300 characters.", + validation: (rule) => + rule.custom<{ value: string; _type: string; _key: string }[]>( + (value) => { + if (!value || value.length === 0) { + return { message: "This field is required." }; + } + const invalidItems = value.filter( + (item) => + typeof item.value === "string" && item.value.length > 300, + ); + + if (invalidItems.length > 0) { + return invalidItems.map((item) => ({ + message: "Title cannot be more than 50 characters long.", + path: [{ _key: item._key }, "value"], + })); + } + return true; + }, + ), + }, + { + name: "url", + title: "Enter an external link", + type: "url", + description: + "If the text block should include a URL, please enter the full URL, including 'https://'. For example, 'https://www.example.com'.", + validation: (rule) => + rule.uri({ + scheme: ["http", "https"], + allowRelative: false, + }), }, { - name: "highlighted", - title: "Highlighted", - type: "boolean", - description: "Display the text with a highlight frame", - initialValue: false, + name: "textBlockType", + title: "Text Block Type", + type: "string", + description: "Please choose the style you want for the text box.", + options: { + list: [ + { title: "Normal", value: "normal" }, + { title: "Highlighted", value: "highlighted" }, + { title: "Framed", value: "framed" }, + ], + layout: "radio", + direction: "vertical", + }, + initialValue: "normal", }, ], preview: { select: { text: "text", - highlighted: "highlighted", + textBlockType: "textBlockType", }, - prepare({ text, highlighted }) { + prepare({ text, textBlockType }) { return { title: firstTranslation(text) ?? undefined, - subtitle: - typeof highlighted === "boolean" && highlighted - ? "Highlighted" - : undefined, + subtitle: textBlockType ? textBlockType : undefined, }; }, },