fix: rels
Some checks failed
Deploy / deploy (push) Has been cancelled

This commit is contained in:
Benno Tielen 2026-09-22 13:32:55 +02:00
parent 84b5887934
commit efdd00d2f0
4 changed files with 77 additions and 22 deletions

View file

@ -36,6 +36,14 @@ export const ImageCardsBlock: Block = {
admin: {
condition: (_, siblingData) => !siblingData?.customLink,
},
hooks: {
beforeChange: [
// Write null instead of undefined so the Postgres adapter removes stale
// _rels rows left behind by block reordering, otherwise they resurface
// as this card's link: https://github.com/payloadcms/payload/issues/16647
({ value, siblingData }) => (siblingData?.customLink ? null : (value ?? null)),
],
},
},
{
name: 'customLink',
@ -43,9 +51,8 @@ export const ImageCardsBlock: Block = {
type: 'text',
required: false,
admin: {
condition: (_, siblingData) => !siblingData?.link,
description:
'Alternative zu "Verknüpfung". Nur eins von beiden kann gesetzt werden.',
'Alternative zu "Verknüpfung", z.B. für externe Seiten oder PDF-Dokumente.',
},
},
],

View file

@ -1,18 +1,14 @@
import { Group, Media, Page } from '@/payload-types'
import { Media } from '@/payload-types'
import { Container } from '@/components/Container/Container'
import { Section } from '@/components/Section/Section'
import { ImageCard } from '@/components/ImageCard/ImageCard'
import { getPhoto } from '@/utils/dto/gallery'
import { ImageCardLinkFields, resolveImageCardHref } from '@/utils/dto/imageCardHref'
import styles from './imageCardsBlock.module.scss'
export type ImageCardsBlockItem = {
export type ImageCardsBlockItem = ImageCardLinkFields & {
title: string
image: string | Media
link?:
| { relationTo: 'pages'; value: string | Page }
| { relationTo: 'group'; value: string | Group }
| null
customLink?: string | null
id?: string | null
}
@ -20,18 +16,6 @@ type ImageCardsBlockProps = {
items: ImageCardsBlockItem[]
}
const resolveHref = (item: ImageCardsBlockItem): string | undefined => {
if (item.link && typeof item.link.value === 'object') {
if (item.link.relationTo === 'pages' && item.link.value.slug) {
return `/${item.link.value.slug}`
}
if (item.link.relationTo === 'group' && item.link.value.slug) {
return `/gruppe/${item.link.value.slug}`
}
}
return item.customLink ?? undefined
}
export const ImageCardsBlock = ({ items }: ImageCardsBlockProps) => {
return (
<Container>
@ -46,7 +30,7 @@ export const ImageCardsBlock = ({ items }: ImageCardsBlockProps) => {
key={item.id ?? item.title}
src={photo.src}
title={item.title}
href={resolveHref(item)}
href={resolveImageCardHref(item)}
/>
)
})}

View file

@ -0,0 +1,34 @@
import { describe, expect, it } from 'vitest'
import { resolveImageCardHref } from './imageCardHref'
const page = { slug: 'chronik' } as any
const group = { slug: 'chor' } as any
describe('resolveImageCardHref', () => {
it('links to a page relationship', () => {
expect(resolveImageCardHref({ link: { relationTo: 'pages', value: page } })).toBe('/chronik')
})
it('links to a group relationship', () => {
expect(resolveImageCardHref({ link: { relationTo: 'group', value: group } })).toBe('/gruppe/chor')
})
it('returns nothing for an unpopulated relationship', () => {
expect(resolveImageCardHref({ link: { relationTo: 'group', value: 'id-only' } })).toBeUndefined()
})
it('uses the custom link when no relationship is set', () => {
expect(resolveImageCardHref({ link: null, customLink: 'https://example.org/x.pdf' }))
.toBe('https://example.org/x.pdf')
})
it('prefers the custom link when a stale relationship is also present', () => {
expect(resolveImageCardHref({ link: { relationTo: 'group', value: group }, customLink: 'https://example.org/x.pdf' }))
.toBe('https://example.org/x.pdf')
})
it('ignores a blank custom link', () => {
expect(resolveImageCardHref({ link: { relationTo: 'group', value: group }, customLink: ' ' })).toBe('/gruppe/chor')
expect(resolveImageCardHref({ customLink: '' })).toBeUndefined()
})
})

View file

@ -0,0 +1,30 @@
import type { Group, Page } from '@/payload-types'
export type ImageCardLink =
| { relationTo: 'pages'; value: string | Page }
| { relationTo: 'group'; value: string | Group }
| null
export type ImageCardLinkFields = {
link?: ImageCardLink
customLink?: string | null
}
// A custom link wins over the relationship. Both can be present at once when a
// stale relationship row survives in the database (see the beforeChange hook in
// src/collections/blocks/ImageCards.ts), and the custom link is what the editor
// actually sees and intends.
export const resolveImageCardHref = (item: ImageCardLinkFields): string | undefined => {
const customLink = item.customLink?.trim()
if (customLink) return customLink
if (item.link && typeof item.link.value === 'object') {
if (item.link.relationTo === 'pages' && item.link.value.slug) {
return `/${item.link.value.slug}`
}
if (item.link.relationTo === 'group' && item.link.value.slug) {
return `/gruppe/${item.link.value.slug}`
}
}
return undefined
}