Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(web): Overview links - two column category card view #17428

Merged
merged 11 commits into from
Jan 17, 2025
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from 'react'

import { Image, SliceType } from '@island.is/island-ui/contentful'
import type { SliceType } from '@island.is/island-ui/contentful'
import {
Box,
BoxProps,
Button,
CategoryCard,
GridColumn,
GridContainer,
GridRow,
Expand All @@ -14,18 +14,147 @@ import {
} from '@island.is/island-ui/core'
import { BorderAbove } from '@island.is/web/components'
import { SLICE_SPACING } from '@island.is/web/constants'
import { OverviewLinks } from '@island.is/web/graphql/schema'
import {
type IntroLinkImage,
type OverviewLinks,
OverviewLinksLinkDataVariant,
} from '@island.is/web/graphql/schema'
import { LinkType, useLinkResolver } from '@island.is/web/hooks/useLinkResolver'
import { webRichText } from '@island.is/web/utils/richText'

interface SliceProps {
slice: OverviewLinks
}

interface IntroLinkImageComponentProps {
item: IntroLinkImage
slice: OverviewLinks
}

const IntroLinkImageComponent = ({
item: { leftImage, linkTitle, image, openLinkInNewTab, title, intro, link },
slice,
}: IntroLinkImageComponentProps) => {
const { linkResolver } = useLinkResolver()
return (
<GridRow direction={leftImage ? 'row' : 'rowReverse'}>
{image?.url && (
<GridColumn span={['8/8', '3/8', '4/8', '3/8']}>
<Box
width="full"
position="relative"
paddingLeft={leftImage ? undefined : [0, 0, 0, 0, 6]}
paddingRight={leftImage ? [10, 0, 0, 0, 6] : [10, 0]}
>
<img src={`${image.url}?w=774&fm=webp&q=80`} alt="" />
</Box>
RunarVestmann marked this conversation as resolved.
Show resolved Hide resolved
</GridColumn>
)}
<GridColumn span={['8/8', '5/8', '4/8', '5/8']}>
<Box
display="flex"
flexDirection="column"
justifyContent="center"
height="full"
>
<Box>
<Text
as="h2"
variant="h2"
marginBottom={2}
id={'sliceTitle-' + slice.id}
>
{title}
</Text>
{Boolean(intro) && (
<Box marginBottom={4}>
{webRichText(
[
{
__typename: 'Html',
id: intro?.id,
document: intro?.document,
},
] as SliceType[],
undefined,
)}
</Box>
)}
{link?.slug && link?.type && (
<Link
{...linkResolver(link.type as LinkType, [link.slug])}
skipTab
newTab={openLinkInNewTab ?? true}
>
<Button icon="arrowForward" variant="text">
{linkTitle}
</Button>
</Link>
)}
</Box>
</Box>
</GridColumn>
</GridRow>
)
}

interface CardViewProps {
slice: OverviewLinks
}

const CardView = ({ slice }: CardViewProps) => {
return (
<Stack space={3}>
<GridRow rowGap={3}>
{(slice.linkData?.categoryCardItems ?? [])
.filter((item) => item.href && item.title)
.map((item, index) => (
<GridColumn key={index} span={['1/1', '1/1', '1/1', '1/1', '1/2']}>
<CategoryCard
heading={item.title}
text={item.description}
href={item.href}
/>
</GridColumn>
))}
</GridRow>
{slice.link?.url && slice.link?.text && (
<Box textAlign="right">
<Link href={slice.link.url} skipTab>
<Button
icon="arrowForward"
iconType="filled"
type="button"
variant="text"
>
{slice.link.text}
</Button>
</Link>
</Box>
)}
</Stack>
)
}

interface ImageViewProps {
slice: OverviewLinks
}

const ImageView = ({ slice }: ImageViewProps) => {
return (
<Stack space={SLICE_SPACING}>
{slice.overviewLinks.map((item, index) => (
<IntroLinkImageComponent key={index} item={item} slice={slice} />
))}
</Stack>
)
}

export const OverviewLinksSlice: React.FC<
React.PropsWithChildren<SliceProps>
> = ({ slice }) => {
const { linkResolver } = useLinkResolver()
const cardView =
slice.linkData?.variant === OverviewLinksLinkDataVariant.CategoryCard

return (
<section
Expand All @@ -35,102 +164,13 @@ export const OverviewLinksSlice: React.FC<
>
<GridContainer>
{slice.hasBorderAbove && <BorderAbove />}
<Box>
<Stack space={SLICE_SPACING}>
{slice.overviewLinks.map(
(
{
title,
linkTitle,
link,
image,
leftImage,
intro,
openLinkInNewTab,
},
index,
) => {
return (
<GridRow
key={index}
direction={leftImage ? 'row' : 'rowReverse'}
>
{image?.url && (
<GridColumn span={['8/8', '3/8', '4/8', '3/8']}>
<Box
width="full"
position="relative"
paddingLeft={leftImage ? undefined : [0, 0, 0, 0, 6]}
paddingRight={leftImage ? [10, 0, 0, 0, 6] : [10, 0]}
>
<img src={`${image.url}?w=774&fm=webp&q=80`} alt="" />
</Box>
</GridColumn>
)}
<GridColumn span={['8/8', '5/8', '4/8', '5/8']}>
<Box
display="flex"
flexDirection="column"
justifyContent="center"
height="full"
>
<Box>
<Text
as="h2"
variant="h2"
marginBottom={2}
id={'sliceTitle-' + slice.id}
>
{title}
</Text>
{Boolean(intro) && (
<Box marginBottom={4}>
{webRichText(
[
{
__typename: 'Html',
id: intro?.id,
document: intro?.document,
},
] as SliceType[],
undefined,
)}{' '}
</Box>
)}
{link?.slug && link?.type && (
<Link
{...linkResolver(link.type as LinkType, [
link.slug,
])}
skipTab
newTab={openLinkInNewTab ?? true}
>
<Button icon="arrowForward" variant="text">
{linkTitle}
</Button>
</Link>
)}
</Box>
</Box>
</GridColumn>
</GridRow>
)
},
)}
</Stack>
{slice.link && (
<Link href="#">
<Button
icon="arrowForward"
iconType="filled"
type="button"
variant="text"
>
{slice.link.text}
</Button>
</Link>
<Stack space={3}>
{Boolean(slice.titleAbove) && cardView && (
<Text variant="h3">{slice.titleAbove}</Text>
)}
</Box>
{cardView && <CardView slice={slice} />}
{!cardView && <ImageView slice={slice} />}
</Stack>
</GridContainer>
</section>
)
Expand Down
9 changes: 9 additions & 0 deletions apps/web/screens/queries/fragments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,16 @@ export const slices = gql`
fragment OverviewLinksField on OverviewLinks {
__typename
id
titleAbove
hasBorderAbove
linkData {
variant
categoryCardItems {
title
description
href
}
}
overviewLinks {
title
intro {
Expand Down
8 changes: 7 additions & 1 deletion libs/cms/src/lib/generated/contentfulTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3499,9 +3499,12 @@ export interface IOrganizationTag extends Entry<IOrganizationTagFields> {
}

export interface IOverviewLinksFields {
/** Title */
/** Internal Title */
title?: string | undefined

/** Displayed Title */
displayedTitle?: string | undefined

/** Overview Links */
overviewLinks?: IIntroLinkImage[] | undefined

Expand All @@ -3510,6 +3513,9 @@ export interface IOverviewLinksFields {

/** Has Border Above */
hasBorderAbove?: boolean | undefined

/** Link Data */
linkData?: Record<string, any> | undefined
}

export interface IOverviewLinks extends Entry<IOverviewLinksFields> {
Expand Down
56 changes: 55 additions & 1 deletion libs/cms/src/lib/models/overviewLinks.model.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,49 @@
import { Field, ID, ObjectType } from '@nestjs/graphql'
import { Field, ID, ObjectType, registerEnumType } from '@nestjs/graphql'
import { CacheField } from '@island.is/nest/graphql'
import { SystemMetadata } from '@island.is/shared/types'

import { IOverviewLinks } from '../generated/contentfulTypes'
import { Link, mapLink } from './link.model'
import { IntroLinkImage, mapIntroLinkImage } from './introLinkImage.model'

enum LinkDataVariant {
IntroLinkImage = 'IntroLinkImage',
CategoryCard = 'CategoryCard',
}

registerEnumType(LinkDataVariant, {
name: 'OverviewLinksLinkDataVariant',
})

@ObjectType('OverviewLinksLinkDataCategoryCardItem')
class CategoryCardItem {
@Field()
title!: string

@Field()
description!: string

@Field()
href!: string
}

@ObjectType('OverviewLinksLinkData')
class LinkData {
@CacheField(() => LinkDataVariant)
variant!: LinkDataVariant

@CacheField(() => [CategoryCardItem])
categoryCardItems!: CategoryCardItem[]
}

@ObjectType()
export class OverviewLinks {
@Field(() => ID)
id!: string

@Field({ nullable: true })
titleAbove?: string

@CacheField(() => [IntroLinkImage])
overviewLinks!: Array<IntroLinkImage>

Expand All @@ -19,6 +52,25 @@ export class OverviewLinks {

@Field(() => Boolean, { nullable: true })
hasBorderAbove?: boolean

@CacheField(() => LinkData, { nullable: true })
linkData?: LinkData | null
}

const mapLinkData = (
linkData: IOverviewLinks['fields']['linkData'],
): LinkData => {
return {
variant:
linkData?.['variant'] === LinkDataVariant.CategoryCard
? LinkDataVariant.CategoryCard
: LinkDataVariant.IntroLinkImage,
categoryCardItems:
linkData?.['categoryCardItems']?.filter(
(item: { title?: string; href?: string }) =>
Boolean(item?.title) && Boolean(item?.href),
) ?? [],
}
}

export const mapOverviewLinks = ({
Expand All @@ -27,7 +79,9 @@ export const mapOverviewLinks = ({
}: IOverviewLinks): SystemMetadata<OverviewLinks> => ({
typename: 'OverviewLinks',
id: sys.id,
titleAbove: fields.displayedTitle ?? '',
overviewLinks: (fields.overviewLinks ?? []).map(mapIntroLinkImage),
link: fields.link ? mapLink(fields.link) : null,
hasBorderAbove: fields.hasBorderAbove ?? true,
linkData: mapLinkData(fields.linkData),
})
Loading