From e81c34721e5f714e57a75c0f5ac7774f3f077feb Mon Sep 17 00:00:00 2001 From: Alexei Gorobet Date: Fri, 25 Aug 2023 16:50:58 -0600 Subject: [PATCH] feat(components): Use NextImage and NextLink components via slots. Include barebones implementation of RichTextCtf --- app/layout.tsx | 1 - components/asset-ctf/asset-ctf.tsx | 43 +++++++++ components/asset-ctf/index.ts | 1 + .../hero-banner-ctf-client.tsx | 27 ++++-- .../hero-banner-ctf/hero-banner-ctf.tsx | 11 ++- components/hooks/use-component-preview.ts | 9 +- components/image-ctf/image-ctf.tsx | 50 +++++++++++ components/image-ctf/index.tsx | 1 + components/page/page.tsx | 34 ++++++-- components/rich-text-ctf/rich-text-ctf.tsx | 87 ++++++++++++++++++- components/ui/README.md | 19 ++++ .../ui/hero-banner/hero-banner.stories.tsx | 12 +-- components/ui/hero-banner/hero-banner.tsx | 33 +++---- components/ui/image/image.tsx | 14 +++ components/ui/image/index.ts | 1 + components/ui/link/index.ts | 1 + components/ui/link/link.tsx | 14 +++ gql/gql.ts | 9 +- gql/graphql.ts | 17 +++- lib/utils.ts | 49 ++++++++++- package.json | 5 +- yarn.lock | 12 +++ 22 files changed, 389 insertions(+), 61 deletions(-) create mode 100644 components/asset-ctf/asset-ctf.tsx create mode 100644 components/asset-ctf/index.ts create mode 100644 components/image-ctf/image-ctf.tsx create mode 100644 components/image-ctf/index.tsx create mode 100644 components/ui/README.md create mode 100644 components/ui/image/image.tsx create mode 100644 components/ui/image/index.ts create mode 100644 components/ui/link/index.ts create mode 100644 components/ui/link/link.tsx diff --git a/app/layout.tsx b/app/layout.tsx index 51c6c1c..f0ee9c7 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,7 +2,6 @@ import { graphqlClient } from "../lib/graphqlClient"; import { draftMode } from "next/headers"; import "./globals.css"; import { graphql } from "#/gql"; -import { Navigation } from "#/components/navigation"; import "@contentful/live-preview/style.css"; import { ContentfulPreviewProvider } from "#/components/contentful-preview-provider"; import { cn } from "#/lib/utils"; diff --git a/components/asset-ctf/asset-ctf.tsx b/components/asset-ctf/asset-ctf.tsx new file mode 100644 index 0000000..15c126a --- /dev/null +++ b/components/asset-ctf/asset-ctf.tsx @@ -0,0 +1,43 @@ +import { ImageCtf } from "#/components/image-ctf"; +import { FragmentType, getFragmentData, graphql } from "#/gql"; + +export const AssetFieldsFragment = graphql(/* GraphQL */ ` + fragment AssetFields on Asset { + __typename + sys { + id + } + contentType + title + url + width + height + description + } +`); + +export type AssetCtfProps = { + data: FragmentType; +}; + +export const AssetCtf = (props: AssetCtfProps) => { + const data = getFragmentData(AssetFieldsFragment, props.data); + + const { url, contentType } = data; + + if (!url) { + return null; + } + + if (!contentType || !url) { + return null; + } + + if (contentType.startsWith("image")) { + return ; + } + + // TODO: Handle other file types. + + return null; +}; diff --git a/components/asset-ctf/index.ts b/components/asset-ctf/index.ts new file mode 100644 index 0000000..830646d --- /dev/null +++ b/components/asset-ctf/index.ts @@ -0,0 +1 @@ +export * from "./asset-ctf"; diff --git a/components/hero-banner-ctf/hero-banner-ctf-client.tsx b/components/hero-banner-ctf/hero-banner-ctf-client.tsx index ade50bf..f5a9957 100644 --- a/components/hero-banner-ctf/hero-banner-ctf-client.tsx +++ b/components/hero-banner-ctf/hero-banner-ctf-client.tsx @@ -3,21 +3,36 @@ import { HeroBanner } from "../ui/hero-banner"; import { ComponentHeroBannerFieldsFragment } from "#/gql/graphql"; import { RichTextCtf } from "#/components/rich-text-ctf"; -import { getPageLinkProps } from "../page"; +import { getPageLinkChildProps } from "../page"; import { useComponentPreview } from "../hooks/use-component-preview"; +import { getImageChildProps } from "../image-ctf"; export const HeroBannerCtfClient: React.FC<{ data: ComponentHeroBannerFieldsFragment; }> = (props) => { const { data: originalData } = props; - const { data, addAttributes } = useComponentPreview(originalData); + const { data, addAttributes } = + useComponentPreview(originalData); + return ( } - ctaText={data.ctaText} - ctaLink={data.targetPage && getPageLinkProps(data.targetPage)?.href} - imageUrl={data.image?.url} + bodyText={data.bodyText && } + cta={ + data.targetPage && + getPageLinkChildProps({ + data: data.targetPage, + children: data.ctaText, + }) + } + image={ + data.image && + getImageChildProps({ + data: data.image, + sizes: "100vw", + priority: true, + }) + } addAttributes={addAttributes} /> ); diff --git a/components/hero-banner-ctf/hero-banner-ctf.tsx b/components/hero-banner-ctf/hero-banner-ctf.tsx index f96bfc6..b7c713f 100644 --- a/components/hero-banner-ctf/hero-banner-ctf.tsx +++ b/components/hero-banner-ctf/hero-banner-ctf.tsx @@ -10,15 +10,20 @@ export const ComponentHeroBannerFieldsFragment = graphql(/* GraphQL */ ` headline bodyText { json + links { + assets { + block { + ...AssetFields + } + } + } } ctaText targetPage { ...PageLinkFields } image { - url - width - height + ...AssetFields } imageStyle heroSize diff --git a/components/hooks/use-component-preview.ts b/components/hooks/use-component-preview.ts index b3cb18d..fc49816 100644 --- a/components/hooks/use-component-preview.ts +++ b/components/hooks/use-component-preview.ts @@ -1,13 +1,14 @@ "use client"; - -import { Entity } from "@contentful/live-preview/dist/types"; +import { Argument, Entity } from "@contentful/live-preview/dist/types"; import { useContentfulInspectorMode, useContentfulLiveUpdates, } from "@contentful/live-preview/react"; -export const useComponentPreview = (data: Entity) => { - const previewData = useContentfulLiveUpdates(data); +export const useComponentPreview = ( + data: (typeof useContentfulLiveUpdates)["arguments"][0] +) => { + const previewData = useContentfulLiveUpdates(data); const inspectorProps = useContentfulInspectorMode({ entryId: data.sys.id, }); diff --git a/components/image-ctf/image-ctf.tsx b/components/image-ctf/image-ctf.tsx new file mode 100644 index 0000000..dfbdaa3 --- /dev/null +++ b/components/image-ctf/image-ctf.tsx @@ -0,0 +1,50 @@ +import { getFragmentData } from "#/gql"; +import { default as NextImage, ImageProps } from "next/image"; +import React from "react"; +import { AssetCtfProps, AssetFieldsFragment } from "../asset-ctf"; + +// Omit src and alt from ImageProps because we're validating them at runtime. +// Apparently typescript doesn't like that ImageProps has src and alt as required but we're spreading props after we map them from Contentful fields, +// it basically thinks we'll always override those props via the spread. +export type ImageCtfProps = AssetCtfProps & Omit; + +export const getImageProps = ({ + data: fragmentData, + ...props +}: ImageCtfProps) => { + const data = getFragmentData(AssetFieldsFragment, fragmentData); + if (!data.url) { + return null; + } + return { + src: data?.url, + alt: data?.description || "", + width: data.width || undefined, + height: data.height || undefined, + ...props, + }; +}; + +export const getImageChildProps = (props: ImageCtfProps) => { + const imageProps = getImageProps(props); + + if (!imageProps) { + return null; + } + + return { + ...imageProps, + children: , + asChild: true, + }; +}; + +export const ImageCtf = (props: ImageCtfProps) => { + const imageProps = getImageProps(props); + + if (!imageProps) { + return null; + } + + return ; +}; diff --git a/components/image-ctf/index.tsx b/components/image-ctf/index.tsx new file mode 100644 index 0000000..34df6be --- /dev/null +++ b/components/image-ctf/index.tsx @@ -0,0 +1 @@ +export * from "./image-ctf"; diff --git a/components/page/page.tsx b/components/page/page.tsx index 4089840..f095e72 100644 --- a/components/page/page.tsx +++ b/components/page/page.tsx @@ -1,4 +1,5 @@ import { FragmentType, getFragmentData, graphql } from "#/gql"; +import { default as NextLink } from "next/link"; export const PageLinkFieldsFragment = graphql(/* GraphQL */ ` fragment PageLinkFields on Page { @@ -19,15 +20,34 @@ export const PageLinkFieldsFragment = graphql(/* GraphQL */ ` } `); -export const getPageLinkProps = ( - data: FragmentType -) => { - const fragment = getFragmentData(PageLinkFieldsFragment, data); +export interface PageLinkProps { + data: FragmentType; + children?: React.ReactNode; + [key: string]: any; +} + +export const getPageLinkProps = ({ + data: fragmentData, + children, + ...props +}: PageLinkProps) => { + const data = getFragmentData(PageLinkFieldsFragment, fragmentData); + + return { + href: `/${data.slug}`, + as: `/${data.slug}`, + children: children ?? data.pageName, + ...props, + }; +}; + +export const getPageLinkChildProps = (props: PageLinkProps) => { + const linkProps = getPageLinkProps(props); return { - href: `/${fragment.slug}`, - as: `/${fragment.slug}`, - children: fragment.pageName, + ...linkProps, + children: , + asChild: true, }; }; diff --git a/components/rich-text-ctf/rich-text-ctf.tsx b/components/rich-text-ctf/rich-text-ctf.tsx index d6af91c..21a10d1 100644 --- a/components/rich-text-ctf/rich-text-ctf.tsx +++ b/components/rich-text-ctf/rich-text-ctf.tsx @@ -1,3 +1,86 @@ -export const RichTextCtf = (props: any) => { - return <>{"TODO: Implement CtfRichtext"}; +import { useMemo } from "react"; +import { + Block as RichtextBlock, + BLOCKS, + INLINES, +} from "@contentful/rich-text-types"; +import { + documentToReactComponents, + Options, +} from "@contentful/rich-text-react-renderer"; +import { OmitRecursive, tryget } from "#/lib/utils"; +import { AssetFieldsFragment } from "#/gql/graphql"; +import { AssetCtf } from "../asset-ctf"; + +export interface RichTextProps { + json: any; + links?: { + entries?: { + block?: any; + inline?: any; + } | null; + assets?: { + block?: any; + } | null; + } | null; +} + +interface Block extends RichtextBlock { + __typename: string; + sys: { id: string }; +} + +type Asset = OmitRecursive; + +export const RichTextCtf = (props: RichTextProps) => { + const { json, links } = props; + + const entryBlocks = useMemo( + () => tryget(() => links!.entries!.block!.filter((b: any) => !!b), [])!, + [links] + ); + + const assetBlocks = useMemo( + () => tryget(() => links!.assets!.block!.filter((b: any) => !!b), [])!, + [links] + ); + + const options = useMemo(() => { + const opts: Options = {}; + opts.renderNode = { + [INLINES.EMBEDDED_ENTRY]: (node) => { + const id = tryget(() => node.data.target.sys.id); + return <>{`${node.nodeType} ${id}`}; + }, + [BLOCKS.EMBEDDED_ENTRY]: (node) => { + const id = tryget(() => node.data.target.sys.id); + if (id) { + const entry = entryBlocks.find((block: any) => block!.sys.id === id); + + if (entry) { + return ( + <> +
Entry:
+
{JSON.stringify(entry, null, 2)}
+ + ); + } + } + return <>{`${node.nodeType} ${id}`}; + }, + [BLOCKS.EMBEDDED_ASSET]: (node) => { + const id = tryget(() => node.data.target.sys.id); + if (id) { + const asset = assetBlocks.find((block: any) => block!.sys.id === id); + return asset && ; + } + + return <>{`${node.nodeType} ${id}`}; + }, + }; + + return opts; + }, [entryBlocks, assetBlocks]); + + return <>{documentToReactComponents(json, options)}; }; diff --git a/components/ui/README.md b/components/ui/README.md new file mode 100644 index 0000000..11a4b9c --- /dev/null +++ b/components/ui/README.md @@ -0,0 +1,19 @@ +## UI Components + +This folder is meant to contain UI components that can be used to compose any other components in NextJS or other React frameworks. Although this folder is within NextJS App itself (for simplicity), you should think of it as external component library that either lives outside NextJS in a monorepo, or even lives in another repo for some cases. + +### Conventions + + +#### Pure UI Components +There are a few boundaries and principles we set with this UI folder that we should maintain: +- UI folder has no knowledge of Contentful. In other words UI components could work in Contentful or outside. There shouldn't be any data-fetching activities or knowledge of contentful-specific things except when needed to hint to Contentful with external escape hatches (see [addAttributes](./hero-banner/hero-banner.tsx) function as an example) +- UI folder should not have any knowledge of NextJS either. It's easy to put NextLink and NextImage in your UI components, but that makes them not possible to reuse outside of NextJS. For this reason alone we have a demonstated path to register NextJS-specific components via ComponentsProvider/useComponents technique. See [here](./hooks/useComponents.ts) +- Pure UI components in same folder makes it easy to understand when you violated that principle. If you see a NextJS or Contentful implementation detail in this folder, you did this wrong. +- This folder is ready for copy/paste into other projects or from other projects/libraries. Especially using shadcn/ui CLI, you can download components into this folder with commands like `npx shadcn-ui@latest add [component]`. See [components.json](../../components.json) for configuration for shadcn/ui. + +#### Storybook + +Storybook should be used to demonstrate UI components visuals, variations and interactions and it's much easier to do in isolation without the need to mock real API responses and data-fetching. + + diff --git a/components/ui/hero-banner/hero-banner.stories.tsx b/components/ui/hero-banner/hero-banner.stories.tsx index ce6ffb0..218f30a 100644 --- a/components/ui/hero-banner/hero-banner.stories.tsx +++ b/components/ui/hero-banner/hero-banner.stories.tsx @@ -1,6 +1,4 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { Button } from "../button"; - import { HeroBanner } from "./hero-banner"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export @@ -16,8 +14,8 @@ const meta = { // More on argTypes: https://storybook.js.org/docs/react/api/argtypes argTypes: { headline: { control: "text" }, - imageUrl: { control: "text" }, bodyText: { control: "text" }, + image: { control: "object" }, }, } satisfies Meta; @@ -26,7 +24,10 @@ type Story = StoryObj; const defaultArgs = { headline: "With great power comes great responsibility", - imageUrl: "https://picsum.photos/seed/picsum/1920/1080", + image: { + src: "https://picsum.photos/seed/picsum/1920/1080", + alt: "Placeholder image", + }, }; // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args export const Default: Story = { @@ -51,7 +52,6 @@ export const WithReactComponent: Story = { export const WithCta: Story = { args: { ...defaultArgs, - ctaText: "Learn more", - ctaLink: "https://google.com", + cta: { children: "Learn more", href: "https://google.com" }, }, }; diff --git a/components/ui/hero-banner/hero-banner.tsx b/components/ui/hero-banner/hero-banner.tsx index c936f1c..7c135b6 100644 --- a/components/ui/hero-banner/hero-banner.tsx +++ b/components/ui/hero-banner/hero-banner.tsx @@ -1,39 +1,33 @@ import { ReactNode } from "react"; import { Button } from "../button/button"; +import { Link, LinkProps } from "../link"; +import { Image, ImageProps } from "../image"; interface HeroBannerProps { headline?: string | null; bodyText?: ReactNode; - imageUrl?: string | null; - imageAlt?: string | null; - ctaText?: string | null; - ctaLink?: string | null; + image?: ImageProps | null; + cta?: LinkProps | null; addAttributes?: (name: string) => object | null; } export function HeroBanner(props: HeroBannerProps) { const { - imageUrl, - imageAlt = "", + image, headline, bodyText, - ctaText, - ctaLink, + cta, addAttributes = () => ({}), // Default to no-op. } = props; return (
- {imageUrl && ( - {imageAlt )}
@@ -47,12 +41,11 @@ export function HeroBanner(props: HeroBannerProps) { {headline} )} - {bodyText} - {ctaLink && ctaText && ( + {bodyText &&
{bodyText}
} + {cta?.href && cta?.children && (
)} diff --git a/components/ui/image/image.tsx b/components/ui/image/image.tsx new file mode 100644 index 0000000..de55001 --- /dev/null +++ b/components/ui/image/image.tsx @@ -0,0 +1,14 @@ +import { Slot } from "@radix-ui/react-slot"; + +export interface ImageProps { + src: string; + alt: string; + asChild?: boolean; + [key: string]: any; +} + +export const Image = ({ asChild = false, ...props }: ImageProps) => { + const Component = asChild ? Slot : "img"; + + return ; +}; diff --git a/components/ui/image/index.ts b/components/ui/image/index.ts new file mode 100644 index 0000000..0aa9b4b --- /dev/null +++ b/components/ui/image/index.ts @@ -0,0 +1 @@ +export * from "./image"; diff --git a/components/ui/link/index.ts b/components/ui/link/index.ts new file mode 100644 index 0000000..6fc151a --- /dev/null +++ b/components/ui/link/index.ts @@ -0,0 +1 @@ +export * from "./link"; diff --git a/components/ui/link/link.tsx b/components/ui/link/link.tsx new file mode 100644 index 0000000..78ab413 --- /dev/null +++ b/components/ui/link/link.tsx @@ -0,0 +1,14 @@ +import { ReactNode } from "react"; +import { Slot } from "@radix-ui/react-slot"; + +export interface LinkProps { + children: ReactNode; + href: string; + [key: string]: any; +} + +export const Link = ({ asChild = false, ...props }: LinkProps) => { + const Component = asChild ? Slot : "a"; + + return ; +}; diff --git a/gql/gql.ts b/gql/gql.ts index e1d6f8d..45efbb2 100644 --- a/gql/gql.ts +++ b/gql/gql.ts @@ -15,8 +15,9 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/ const documents = { "\n query PageQuery($slug: String, $locale: String, $preview: Boolean) {\n pageCollection(\n locale: $locale\n preview: $preview\n limit: 1\n where: { slug: $slug }\n ) {\n items {\n topSectionCollection(limit: 10) {\n items {\n ...ComponentHeroBannerFields\n ...ComponentDuplexFields\n }\n }\n }\n }\n }\n ": types.PageQueryDocument, "\n query Layout($locale: String, $preview: Boolean) {\n navigationMenuCollection(locale: $locale, preview: $preview, limit: 1) {\n ...NavigationFields\n }\n }\n ": types.LayoutDocument, + "\n fragment AssetFields on Asset {\n __typename\n sys {\n id\n }\n contentType\n title\n url\n width\n height\n description\n }\n": types.AssetFieldsFragmentDoc, "\n fragment ComponentDuplexFields on ComponentDuplex {\n __typename\n sys {\n id\n }\n headline\n bodyText {\n json\n }\n ctaText\n targetPage {\n ...PageLinkFields\n }\n image {\n url\n }\n imageStyle\n colorPalette\n }\n": types.ComponentDuplexFieldsFragmentDoc, - "\n fragment ComponentHeroBannerFields on ComponentHeroBanner {\n __typename\n sys {\n id\n }\n headline\n bodyText {\n json\n }\n ctaText\n targetPage {\n ...PageLinkFields\n }\n image {\n url\n width\n height\n }\n imageStyle\n heroSize\n colorPalette\n }\n": types.ComponentHeroBannerFieldsFragmentDoc, + "\n fragment ComponentHeroBannerFields on ComponentHeroBanner {\n __typename\n sys {\n id\n }\n headline\n bodyText {\n json\n links {\n assets {\n block {\n ...AssetFields\n }\n }\n }\n }\n ctaText\n targetPage {\n ...PageLinkFields\n }\n image {\n ...AssetFields\n }\n imageStyle\n heroSize\n colorPalette\n }\n": types.ComponentHeroBannerFieldsFragmentDoc, "\n fragment MenuGroupFields on MenuGroupFeaturedPagesCollection {\n items {\n ...PageLinkFields\n }\n }\n": types.MenuGroupFieldsFragmentDoc, "\n fragment NavigationFields on NavigationMenuCollection {\n items {\n menuItemsCollection {\n items {\n __typename\n sys {\n id\n }\n groupName\n link: groupLink {\n ...PageLinkFields\n }\n children: featuredPagesCollection {\n ...MenuGroupFields\n }\n }\n }\n }\n }\n": types.NavigationFieldsFragmentDoc, "\n fragment PageLinkFields on Page {\n __typename\n slug\n sys {\n id\n }\n pageName\n pageContent(locale: $locale, preview: $preview) {\n ... on Entry {\n __typename\n sys {\n id\n }\n }\n }\n }\n": types.PageLinkFieldsFragmentDoc, @@ -45,6 +46,10 @@ export function graphql(source: "\n query PageQuery($slug: String, $locale: S * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "\n query Layout($locale: String, $preview: Boolean) {\n navigationMenuCollection(locale: $locale, preview: $preview, limit: 1) {\n ...NavigationFields\n }\n }\n "): (typeof documents)["\n query Layout($locale: String, $preview: Boolean) {\n navigationMenuCollection(locale: $locale, preview: $preview, limit: 1) {\n ...NavigationFields\n }\n }\n "]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n fragment AssetFields on Asset {\n __typename\n sys {\n id\n }\n contentType\n title\n url\n width\n height\n description\n }\n"): (typeof documents)["\n fragment AssetFields on Asset {\n __typename\n sys {\n id\n }\n contentType\n title\n url\n width\n height\n description\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -52,7 +57,7 @@ export function graphql(source: "\n fragment ComponentDuplexFields on Component /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n fragment ComponentHeroBannerFields on ComponentHeroBanner {\n __typename\n sys {\n id\n }\n headline\n bodyText {\n json\n }\n ctaText\n targetPage {\n ...PageLinkFields\n }\n image {\n url\n width\n height\n }\n imageStyle\n heroSize\n colorPalette\n }\n"): (typeof documents)["\n fragment ComponentHeroBannerFields on ComponentHeroBanner {\n __typename\n sys {\n id\n }\n headline\n bodyText {\n json\n }\n ctaText\n targetPage {\n ...PageLinkFields\n }\n image {\n url\n width\n height\n }\n imageStyle\n heroSize\n colorPalette\n }\n"]; +export function graphql(source: "\n fragment ComponentHeroBannerFields on ComponentHeroBanner {\n __typename\n sys {\n id\n }\n headline\n bodyText {\n json\n links {\n assets {\n block {\n ...AssetFields\n }\n }\n }\n }\n ctaText\n targetPage {\n ...PageLinkFields\n }\n image {\n ...AssetFields\n }\n imageStyle\n heroSize\n colorPalette\n }\n"): (typeof documents)["\n fragment ComponentHeroBannerFields on ComponentHeroBanner {\n __typename\n sys {\n id\n }\n headline\n bodyText {\n json\n links {\n assets {\n block {\n ...AssetFields\n }\n }\n }\n }\n ctaText\n targetPage {\n ...PageLinkFields\n }\n image {\n ...AssetFields\n }\n imageStyle\n heroSize\n colorPalette\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/gql/graphql.ts b/gql/graphql.ts index c6efc04..6b8f21b 100644 --- a/gql/graphql.ts +++ b/gql/graphql.ts @@ -4573,15 +4573,23 @@ export type LayoutQuery = { __typename?: 'Query', navigationMenuCollection?: ( & { ' $fragmentRefs'?: { 'NavigationFieldsFragment': NavigationFieldsFragment } } ) | null }; +export type AssetFieldsFragment = { __typename: 'Asset', contentType?: string | null, title?: string | null, url?: string | null, width?: number | null, height?: number | null, description?: string | null, sys: { __typename?: 'Sys', id: string } } & { ' $fragmentName'?: 'AssetFieldsFragment' }; + export type ComponentDuplexFieldsFragment = { __typename: 'ComponentDuplex', headline?: string | null, ctaText?: string | null, imageStyle?: boolean | null, colorPalette?: string | null, sys: { __typename?: 'Sys', id: string }, bodyText?: { __typename?: 'ComponentDuplexBodyText', json: any } | null, targetPage?: ( { __typename?: 'Page' } & { ' $fragmentRefs'?: { 'PageLinkFieldsFragment': PageLinkFieldsFragment } } ) | null, image?: { __typename?: 'Asset', url?: string | null } | null } & { ' $fragmentName'?: 'ComponentDuplexFieldsFragment' }; -export type ComponentHeroBannerFieldsFragment = { __typename: 'ComponentHeroBanner', headline?: string | null, ctaText?: string | null, imageStyle?: boolean | null, heroSize?: boolean | null, colorPalette?: string | null, sys: { __typename?: 'Sys', id: string }, bodyText?: { __typename?: 'ComponentHeroBannerBodyText', json: any } | null, targetPage?: ( +export type ComponentHeroBannerFieldsFragment = { __typename: 'ComponentHeroBanner', headline?: string | null, ctaText?: string | null, imageStyle?: boolean | null, heroSize?: boolean | null, colorPalette?: string | null, sys: { __typename?: 'Sys', id: string }, bodyText?: { __typename?: 'ComponentHeroBannerBodyText', json: any, links: { __typename?: 'ComponentHeroBannerBodyTextLinks', assets: { __typename?: 'ComponentHeroBannerBodyTextAssets', block: Array<( + { __typename?: 'Asset' } + & { ' $fragmentRefs'?: { 'AssetFieldsFragment': AssetFieldsFragment } } + ) | null> } } } | null, targetPage?: ( { __typename?: 'Page' } & { ' $fragmentRefs'?: { 'PageLinkFieldsFragment': PageLinkFieldsFragment } } - ) | null, image?: { __typename?: 'Asset', url?: string | null, width?: number | null, height?: number | null } | null } & { ' $fragmentName'?: 'ComponentHeroBannerFieldsFragment' }; + ) | null, image?: ( + { __typename?: 'Asset' } + & { ' $fragmentRefs'?: { 'AssetFieldsFragment': AssetFieldsFragment } } + ) | null } & { ' $fragmentName'?: 'ComponentHeroBannerFieldsFragment' }; export type MenuGroupFieldsFragment = { __typename?: 'MenuGroupFeaturedPagesCollection', items: Array<( { __typename?: 'Page' } @@ -4609,9 +4617,10 @@ export type PageSlugQueryQuery = { __typename?: 'Query', pageCollection?: { __ty export const PageLinkFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLinkFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageName"}},{"kind":"Field","name":{"kind":"Name","value":"pageContent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Entry"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const ComponentDuplexFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ComponentDuplexFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ComponentDuplex"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"bodyText"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"json"}}]}},{"kind":"Field","name":{"kind":"Name","value":"ctaText"}},{"kind":"Field","name":{"kind":"Name","value":"targetPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLinkFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"imageStyle"}},{"kind":"Field","name":{"kind":"Name","value":"colorPalette"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLinkFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageName"}},{"kind":"Field","name":{"kind":"Name","value":"pageContent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Entry"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode; -export const ComponentHeroBannerFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ComponentHeroBannerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ComponentHeroBanner"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"bodyText"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"json"}}]}},{"kind":"Field","name":{"kind":"Name","value":"ctaText"}},{"kind":"Field","name":{"kind":"Name","value":"targetPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLinkFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"Field","name":{"kind":"Name","value":"imageStyle"}},{"kind":"Field","name":{"kind":"Name","value":"heroSize"}},{"kind":"Field","name":{"kind":"Name","value":"colorPalette"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLinkFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageName"}},{"kind":"Field","name":{"kind":"Name","value":"pageContent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Entry"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const AssetFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AssetFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Asset"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"contentType"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]} as unknown as DocumentNode; +export const ComponentHeroBannerFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ComponentHeroBannerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ComponentHeroBanner"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"bodyText"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"json"}},{"kind":"Field","name":{"kind":"Name","value":"links"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"block"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AssetFields"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"ctaText"}},{"kind":"Field","name":{"kind":"Name","value":"targetPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLinkFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AssetFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"imageStyle"}},{"kind":"Field","name":{"kind":"Name","value":"heroSize"}},{"kind":"Field","name":{"kind":"Name","value":"colorPalette"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AssetFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Asset"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"contentType"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLinkFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageName"}},{"kind":"Field","name":{"kind":"Name","value":"pageContent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Entry"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const MenuGroupFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MenuGroupFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MenuGroupFeaturedPagesCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLinkFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLinkFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageName"}},{"kind":"Field","name":{"kind":"Name","value":"pageContent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Entry"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const NavigationFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NavigationFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NavigationMenuCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"menuItemsCollection"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"groupName"}},{"kind":"Field","alias":{"kind":"Name","value":"link"},"name":{"kind":"Name","value":"groupLink"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLinkFields"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"children"},"name":{"kind":"Name","value":"featuredPagesCollection"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MenuGroupFields"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLinkFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageName"}},{"kind":"Field","name":{"kind":"Name","value":"pageContent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Entry"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MenuGroupFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MenuGroupFeaturedPagesCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLinkFields"}}]}}]}}]} as unknown as DocumentNode; -export const PageQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PageQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"locale"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"preview"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pageCollection"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"topSectionCollection"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"10"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ComponentHeroBannerFields"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ComponentDuplexFields"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLinkFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageName"}},{"kind":"Field","name":{"kind":"Name","value":"pageContent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Entry"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ComponentHeroBannerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ComponentHeroBanner"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"bodyText"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"json"}}]}},{"kind":"Field","name":{"kind":"Name","value":"ctaText"}},{"kind":"Field","name":{"kind":"Name","value":"targetPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLinkFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}}]}},{"kind":"Field","name":{"kind":"Name","value":"imageStyle"}},{"kind":"Field","name":{"kind":"Name","value":"heroSize"}},{"kind":"Field","name":{"kind":"Name","value":"colorPalette"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ComponentDuplexFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ComponentDuplex"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"bodyText"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"json"}}]}},{"kind":"Field","name":{"kind":"Name","value":"ctaText"}},{"kind":"Field","name":{"kind":"Name","value":"targetPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLinkFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"imageStyle"}},{"kind":"Field","name":{"kind":"Name","value":"colorPalette"}}]}}]} as unknown as DocumentNode; +export const PageQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PageQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"locale"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"preview"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pageCollection"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"topSectionCollection"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"10"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ComponentHeroBannerFields"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ComponentDuplexFields"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AssetFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Asset"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"contentType"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"width"}},{"kind":"Field","name":{"kind":"Name","value":"height"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLinkFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageName"}},{"kind":"Field","name":{"kind":"Name","value":"pageContent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Entry"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ComponentHeroBannerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ComponentHeroBanner"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"bodyText"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"json"}},{"kind":"Field","name":{"kind":"Name","value":"links"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"block"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AssetFields"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"ctaText"}},{"kind":"Field","name":{"kind":"Name","value":"targetPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLinkFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AssetFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"imageStyle"}},{"kind":"Field","name":{"kind":"Name","value":"heroSize"}},{"kind":"Field","name":{"kind":"Name","value":"colorPalette"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ComponentDuplexFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ComponentDuplex"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"headline"}},{"kind":"Field","name":{"kind":"Name","value":"bodyText"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"json"}}]}},{"kind":"Field","name":{"kind":"Name","value":"ctaText"}},{"kind":"Field","name":{"kind":"Name","value":"targetPage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLinkFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"image"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"imageStyle"}},{"kind":"Field","name":{"kind":"Name","value":"colorPalette"}}]}}]} as unknown as DocumentNode; export const LayoutDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Layout"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"locale"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"preview"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"navigationMenuCollection"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"NavigationFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PageLinkFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Page"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageName"}},{"kind":"Field","name":{"kind":"Name","value":"pageContent"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Entry"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MenuGroupFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MenuGroupFeaturedPagesCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLinkFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NavigationFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NavigationMenuCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"menuItemsCollection"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"sys"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"groupName"}},{"kind":"Field","alias":{"kind":"Name","value":"link"},"name":{"kind":"Name","value":"groupLink"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PageLinkFields"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"children"},"name":{"kind":"Name","value":"featuredPagesCollection"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MenuGroupFields"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const PageSlugQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PageSlugQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"locale"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"preview"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pageCollection"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"locale"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locale"}}},{"kind":"Argument","name":{"kind":"Name","value":"preview"},"value":{"kind":"Variable","name":{"kind":"Name","value":"preview"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/lib/utils.ts b/lib/utils.ts index ec79801..1f71cf9 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,6 +1,47 @@ -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" - +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); } + +type OmitCustom = Pick>; +export type OmitDistributive = T extends any + ? T extends object + ? Id> + : T + : never; + +export type Id = Record & { [P in keyof T]: T[P] }; +export type OmitRecursive = OmitCustom< + { [P in keyof T]: OmitDistributive }, + K +>; + +// eslint-disable-next-line arrow-parens +export const tryget = ( + exp: () => T, + d: T | undefined | null = undefined +) => { + try { + const val = exp(); + if (val != null) { + return val; + } + } catch { + /* noop */ + } + return d; +}; + +export const optimizeLineBreak = (str: string): string => { + const tokens = str.split(" "); + + if (tokens.length < 3) { + return str; + } + + const lastToken = tokens.pop(); + + return `${tokens.join(" ")}\u00A0${lastToken}`; +}; diff --git a/package.json b/package.json index c69153a..13c338b 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "@contentful/live-preview": "^2.6.2", + "@contentful/rich-text-react-renderer": "^15.17.1", "@radix-ui/react-slot": "^1.0.2", "@types/node": "18.11.11", "@types/react": "^18.2.14", @@ -57,13 +58,13 @@ "cypress": "^12.2.0", "cypress-axe": "^1.2.0", "cypress-image-snapshot": "^4.0.1", + "esbuild": "0.19.1", "eslint-plugin-storybook": "^0.6.13", "postcss": "^8.4.27", "storybook": "^7.2.3", "tailwindcss": "^3.3.3", "ts-loader": "^9.4.2", - "webpack": "^5.75.0", - "esbuild": "0.19.1" + "webpack": "^5.75.0" }, "cypress-cucumber-preprocessor": { "stepDefinitions": [ diff --git a/yarn.lock b/yarn.lock index 3bd3c6d..e538ade 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1662,11 +1662,23 @@ "@contentful/visual-sdk" "^1.0.0-alpha.7" lodash.isequal "^4.5.0" +"@contentful/rich-text-react-renderer@^15.17.1": + version "15.17.1" + resolved "https://registry.npmjs.org/@contentful/rich-text-react-renderer/-/rich-text-react-renderer-15.17.1.tgz#e173746e58f505fe8dd5335abf1e9ebc58f7157f" + integrity sha512-LSsYBYYAzkSCWmAbbVCzxZhcrUNBWdgOo9YY1WgvANmzF9zEUODxml3lCLN7PQ08nGMAjzhJBV5wpyp5YReRjg== + dependencies: + "@contentful/rich-text-types" "^16.2.1" + "@contentful/rich-text-types@^16.2.0": version "16.2.0" resolved "https://registry.npmjs.org/@contentful/rich-text-types/-/rich-text-types-16.2.0.tgz#5a98cb119340f4da46555216913e96d7ce58df70" integrity sha512-4BHC+mfa50JB70epzpnas4EkiuB3mGdBZ5Rp8w7R5wXvswEf8TLg5yGau2FxmZeEjrAkDrt4vzBLVQ8v3Y//Jw== +"@contentful/rich-text-types@^16.2.1": + version "16.2.1" + resolved "https://registry.npmjs.org/@contentful/rich-text-types/-/rich-text-types-16.2.1.tgz#27236f276f473500e0dd5e330a074a98fc2d223b" + integrity sha512-fBxqYcyG3Nw33DhifuE7Ndw3xlrWlOMjwsOvhLmVP4LO475Qyy9ioRChtEt6DPUdH2SFpobvM0qqC/fVe0MNDA== + "@contentful/visual-sdk@^1.0.0-alpha.7": version "1.0.0-alpha.7" resolved "https://registry.npmjs.org/@contentful/visual-sdk/-/visual-sdk-1.0.0-alpha.7.tgz#6c886978d018cdcefff99a2e71c135efbfed1d91"