fix(live-preview): client-side live preview failed to populate localized fields (#13794)
Fixes #13756 The findByID endpoint, by default, expects the data for localized fields to be an object, values mapped to the locale. This is not the case for client-side live preview, as we send already-flattened data to the findByID endpoint. For localized fields where the value is an object (richText/json/group), the afterRead hook handler would attempt to flatten the field value, even though it was already flattened. ## Solution The solution is to expose a `flattenLocales` arg to the findByID endpoint (default: true) and pass `flattenLocales: false` from the client-side live preview request handler. --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211334752795627
This commit is contained in:
@@ -48,6 +48,8 @@ export const mergeData = async <T extends Record<string, any>>(args: {
|
||||
data: {
|
||||
data: incomingData,
|
||||
depth,
|
||||
// The incoming data already has had its locales flattened
|
||||
flattenLocales: false,
|
||||
locale,
|
||||
},
|
||||
endpoint: encodeURI(
|
||||
|
||||
@@ -15,6 +15,12 @@ export const findByIDHandler: PayloadHandler = async (req) => {
|
||||
const { id, collection } = getRequestCollectionWithID(req)
|
||||
const depth = data ? data.depth : searchParams.get('depth')
|
||||
const trash = data ? data.trash : searchParams.get('trash') === 'true'
|
||||
const flattenLocales = data
|
||||
? data.flattenLocales
|
||||
: searchParams.has('flattenLocales')
|
||||
? searchParams.get('flattenLocales') === 'true'
|
||||
: // flattenLocales should be undefined if not provided, so that the default (true) is applied in the operation
|
||||
undefined
|
||||
|
||||
const result = await findByIDOperation({
|
||||
id,
|
||||
@@ -26,6 +32,7 @@ export const findByIDHandler: PayloadHandler = async (req) => {
|
||||
: undefined,
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
draft: data ? data.draft : searchParams.get('draft') === 'true',
|
||||
flattenLocales,
|
||||
joins: sanitizeJoinParams(req.query.joins as JoinParams),
|
||||
populate: sanitizePopulateParam(req.query.populate),
|
||||
req,
|
||||
|
||||
@@ -20,7 +20,7 @@ import { combineQueries } from '../../database/combineQueries.js'
|
||||
import { sanitizeJoinQuery } from '../../database/sanitizeJoinQuery.js'
|
||||
import { sanitizeWhereQuery } from '../../database/sanitizeWhereQuery.js'
|
||||
import { NotFound } from '../../errors/index.js'
|
||||
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||
import { afterRead, type AfterReadArgs } from '../../fields/hooks/afterRead/index.js'
|
||||
import { validateQueryPaths } from '../../index.js'
|
||||
import { lockedDocumentsCollectionSlug } from '../../locked-documents/config.js'
|
||||
import { appendNonTrashedFilter } from '../../utilities/appendNonTrashedFilter.js'
|
||||
@@ -29,7 +29,7 @@ import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||
import { replaceWithDraftIfAvailable } from '../../versions/drafts/replaceWithDraftIfAvailable.js'
|
||||
import { buildAfterOperation } from './utils.js'
|
||||
|
||||
export type Arguments = {
|
||||
export type FindByIDArgs = {
|
||||
collection: Collection
|
||||
currentDepth?: number
|
||||
/**
|
||||
@@ -49,14 +49,14 @@ export type Arguments = {
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
trash?: boolean
|
||||
}
|
||||
} & Pick<AfterReadArgs<JsonObject>, 'flattenLocales'>
|
||||
|
||||
export const findByIDOperation = async <
|
||||
TSlug extends CollectionSlug,
|
||||
TDisableErrors extends boolean,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
incomingArgs: Arguments,
|
||||
incomingArgs: FindByIDArgs,
|
||||
): Promise<ApplyDisableErrors<TransformCollectionWithSelect<TSlug, TSelect>, TDisableErrors>> => {
|
||||
let args = incomingArgs
|
||||
|
||||
@@ -85,6 +85,7 @@ export const findByIDOperation = async <
|
||||
depth,
|
||||
disableErrors,
|
||||
draft: draftEnabled = false,
|
||||
flattenLocales,
|
||||
includeLockStatus,
|
||||
joins,
|
||||
overrideAccess = false,
|
||||
@@ -279,6 +280,7 @@ export const findByIDOperation = async <
|
||||
doc: result,
|
||||
draft: draftEnabled,
|
||||
fallbackLocale: fallbackLocale!,
|
||||
flattenLocales,
|
||||
global: null,
|
||||
locale: locale!,
|
||||
overrideAccess,
|
||||
|
||||
@@ -18,7 +18,7 @@ import type { SelectFromCollectionSlug } from '../../config/types.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||
import { findByIDOperation } from '../findByID.js'
|
||||
import { type FindByIDArgs, findByIDOperation } from '../findByID.js'
|
||||
|
||||
export type Options<
|
||||
TSlug extends CollectionSlug,
|
||||
@@ -117,7 +117,7 @@ export type Options<
|
||||
* If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks.
|
||||
*/
|
||||
user?: Document
|
||||
}
|
||||
} & Pick<FindByIDArgs, 'flattenLocales'>
|
||||
|
||||
export async function findByIDLocal<
|
||||
TSlug extends CollectionSlug,
|
||||
@@ -135,6 +135,7 @@ export async function findByIDLocal<
|
||||
depth,
|
||||
disableErrors = false,
|
||||
draft = false,
|
||||
flattenLocales,
|
||||
includeLockStatus,
|
||||
joins,
|
||||
overrideAccess = true,
|
||||
@@ -160,6 +161,7 @@ export async function findByIDLocal<
|
||||
depth,
|
||||
disableErrors,
|
||||
draft,
|
||||
flattenLocales,
|
||||
includeLockStatus,
|
||||
joins,
|
||||
overrideAccess,
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { JsonObject, PayloadRequest, PopulateType, SelectType } from '../..
|
||||
import { getSelectMode } from '../../../utilities/getSelectMode.js'
|
||||
import { traverseFields } from './traverseFields.js'
|
||||
|
||||
type Args<T extends JsonObject> = {
|
||||
export type AfterReadArgs<T extends JsonObject> = {
|
||||
collection: null | SanitizedCollectionConfig
|
||||
context: RequestContext
|
||||
currentDepth?: number
|
||||
@@ -15,6 +15,12 @@ type Args<T extends JsonObject> = {
|
||||
draft: boolean
|
||||
fallbackLocale: null | string
|
||||
findMany?: boolean
|
||||
/**
|
||||
* Controls whether locales should be flattened into the requested locale.
|
||||
* E.g.: { [locale]: fields } -> fields
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
flattenLocales?: boolean
|
||||
global: null | SanitizedGlobalConfig
|
||||
locale: string
|
||||
@@ -35,7 +41,7 @@ type Args<T extends JsonObject> = {
|
||||
* - Populate relationships
|
||||
*/
|
||||
|
||||
export async function afterRead<T extends JsonObject>(args: Args<T>): Promise<T> {
|
||||
export async function afterRead<T extends JsonObject>(args: AfterReadArgs<T>): Promise<T> {
|
||||
const {
|
||||
collection,
|
||||
context,
|
||||
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
SelectType,
|
||||
} from '../../../types/index.js'
|
||||
import type { Block, Field, TabAsField } from '../../config/types.js'
|
||||
import type { AfterReadArgs } from './index.js'
|
||||
|
||||
import { MissingEditorProp } from '../../../errors/index.js'
|
||||
import { type RequestContext } from '../../../index.js'
|
||||
@@ -40,7 +41,6 @@ type Args = {
|
||||
*/
|
||||
fieldPromises: Promise<void>[]
|
||||
findMany: boolean
|
||||
flattenLocales: boolean
|
||||
global: null | SanitizedGlobalConfig
|
||||
locale: null | string
|
||||
overrideAccess: boolean
|
||||
@@ -61,7 +61,7 @@ type Args = {
|
||||
siblingFields?: (Field | TabAsField)[]
|
||||
triggerAccessControl?: boolean
|
||||
triggerHooks?: boolean
|
||||
}
|
||||
} & Required<Pick<AfterReadArgs<JsonObject>, 'flattenLocales'>>
|
||||
|
||||
// This function is responsible for the following actions, in order:
|
||||
// - Remove hidden fields from response
|
||||
@@ -236,15 +236,23 @@ export const promise = async ({
|
||||
}
|
||||
}
|
||||
|
||||
// If locale is `all`, siblingDoc[field.name] will be an object mapping locales to values - locales won't be flattened.
|
||||
// In this case, run the hook for each locale and value pair
|
||||
const shouldRunHookOnAllLocales =
|
||||
locale === 'all' &&
|
||||
'name' in field &&
|
||||
typeof field.name === 'string' &&
|
||||
// If localized values were hoisted, siblingDoc[field.name] will not be an object mapping locales to values
|
||||
// => Object.entries(siblingDoc[field.name]) will be the value of a single locale, not all locales
|
||||
// => do not run the hook for each locale
|
||||
!shouldHoistLocalizedValue &&
|
||||
fieldShouldBeLocalized({ field, parentIsLocalized: parentIsLocalized! }) &&
|
||||
typeof siblingDoc[field.name] === 'object'
|
||||
|
||||
if (fieldAffectsDataResult) {
|
||||
// Execute hooks
|
||||
if (triggerHooks && 'hooks' in field && field.hooks?.afterRead) {
|
||||
for (const hook of field.hooks.afterRead) {
|
||||
const shouldRunHookOnAllLocales =
|
||||
fieldShouldBeLocalized({ field, parentIsLocalized: parentIsLocalized! }) &&
|
||||
(locale === 'all' || !flattenLocales) &&
|
||||
typeof siblingDoc[field.name] === 'object'
|
||||
|
||||
if (shouldRunHookOnAllLocales) {
|
||||
const localesAndValues = Object.entries(siblingDoc[field.name])
|
||||
await Promise.all(
|
||||
@@ -711,11 +719,6 @@ export const promise = async ({
|
||||
|
||||
if (editor?.hooks?.afterRead?.length) {
|
||||
for (const hook of editor.hooks.afterRead) {
|
||||
const shouldRunHookOnAllLocales =
|
||||
fieldShouldBeLocalized({ field, parentIsLocalized: parentIsLocalized! }) &&
|
||||
(locale === 'all' || !flattenLocales) &&
|
||||
typeof siblingDoc[field.name] === 'object'
|
||||
|
||||
if (shouldRunHookOnAllLocales) {
|
||||
const localesAndValues = Object.entries(siblingDoc[field.name])
|
||||
|
||||
|
||||
@@ -13,6 +13,12 @@ export const findOneHandler: PayloadHandler = async (req) => {
|
||||
const globalConfig = getRequestGlobal(req)
|
||||
const { data, searchParams } = req
|
||||
const depth = data ? data.depth : searchParams.get('depth')
|
||||
const flattenLocales = data
|
||||
? data.flattenLocales
|
||||
: searchParams.has('flattenLocales')
|
||||
? searchParams.get('flattenLocales') === 'true'
|
||||
: // flattenLocales should be undfined if not provided, so that the default (true) is applied in the operation
|
||||
undefined
|
||||
|
||||
const result = await findOneOperation({
|
||||
slug: globalConfig.slug,
|
||||
@@ -23,6 +29,7 @@ export const findOneHandler: PayloadHandler = async (req) => {
|
||||
: undefined,
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
draft: data ? data.draft : searchParams.get('draft') === 'true',
|
||||
flattenLocales,
|
||||
globalConfig,
|
||||
populate: sanitizePopulateParam(req.query.populate),
|
||||
req,
|
||||
|
||||
@@ -9,14 +9,14 @@ import type {
|
||||
import type { SanitizedGlobalConfig } from '../config/types.js'
|
||||
|
||||
import { executeAccess } from '../../auth/executeAccess.js'
|
||||
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||
import { afterRead, type AfterReadArgs } from '../../fields/hooks/afterRead/index.js'
|
||||
import { lockedDocumentsCollectionSlug } from '../../locked-documents/config.js'
|
||||
import { getSelectMode } from '../../utilities/getSelectMode.js'
|
||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||
import { replaceWithDraftIfAvailable } from '../../versions/drafts/replaceWithDraftIfAvailable.js'
|
||||
|
||||
type Args = {
|
||||
export type GlobalFindOneArgs = {
|
||||
/**
|
||||
* You may pass the document data directly which will skip the `db.findOne` database query.
|
||||
* This is useful if you want to use this endpoint solely for running hooks and populating data.
|
||||
@@ -32,15 +32,16 @@ type Args = {
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
slug: string
|
||||
}
|
||||
} & Pick<AfterReadArgs<JsonObject>, 'flattenLocales'>
|
||||
|
||||
export const findOneOperation = async <T extends Record<string, unknown>>(
|
||||
args: Args,
|
||||
args: GlobalFindOneArgs,
|
||||
): Promise<T> => {
|
||||
const {
|
||||
slug,
|
||||
depth,
|
||||
draft: draftEnabled = false,
|
||||
flattenLocales,
|
||||
globalConfig,
|
||||
includeLockStatus,
|
||||
overrideAccess = false,
|
||||
@@ -206,6 +207,7 @@ export const findOneOperation = async <T extends Record<string, unknown>>(
|
||||
doc,
|
||||
draft: draftEnabled,
|
||||
fallbackLocale: fallbackLocale!,
|
||||
flattenLocales,
|
||||
global: globalConfig,
|
||||
locale: locale!,
|
||||
overrideAccess,
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { SelectFromGlobalSlug } from '../../config/types.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||
import { findOneOperation } from '../findOne.js'
|
||||
import { findOneOperation, type GlobalFindOneArgs } from '../findOne.js'
|
||||
|
||||
export type Options<TSlug extends GlobalSlug, TSelect extends SelectType> = {
|
||||
/**
|
||||
@@ -78,7 +78,7 @@ export type Options<TSlug extends GlobalSlug, TSelect extends SelectType> = {
|
||||
* If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks.
|
||||
*/
|
||||
user?: Document
|
||||
}
|
||||
} & Pick<GlobalFindOneArgs, 'flattenLocales'>
|
||||
|
||||
export async function findOneGlobalLocal<
|
||||
TSlug extends GlobalSlug,
|
||||
@@ -92,6 +92,7 @@ export async function findOneGlobalLocal<
|
||||
data,
|
||||
depth,
|
||||
draft = false,
|
||||
flattenLocales,
|
||||
includeLockStatus,
|
||||
overrideAccess = true,
|
||||
populate,
|
||||
@@ -110,6 +111,7 @@ export async function findOneGlobalLocal<
|
||||
data,
|
||||
depth,
|
||||
draft,
|
||||
flattenLocales,
|
||||
globalConfig,
|
||||
includeLockStatus,
|
||||
overrideAccess,
|
||||
|
||||
@@ -31,6 +31,12 @@ export const RelationshipsBlock: React.FC<RelationshipsBlockProps> = (props) =>
|
||||
{data?.richTextLexical && (
|
||||
<RichText content={data.richTextLexical} renderUploadFilenameOnly />
|
||||
)}
|
||||
<p>
|
||||
<b>Rich Text — Lexical — Localized:</b>
|
||||
</p>
|
||||
{data?.richTextLexicalLocalized && (
|
||||
<RichText content={data.richTextLexicalLocalized} renderUploadFilenameOnly />
|
||||
)}
|
||||
<p>
|
||||
<b>Upload:</b>
|
||||
</p>
|
||||
|
||||
@@ -92,6 +92,18 @@ export const Pages: CollectionConfig = {
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: 'Rich Text — Lexical — Localized',
|
||||
type: 'richText',
|
||||
name: 'richTextLexicalLocalized',
|
||||
localized: true,
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
BlocksFeature({ blocks: ['mediaBlock'] }),
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'relationshipAsUpload',
|
||||
type: 'upload',
|
||||
|
||||
@@ -129,7 +129,13 @@ describe('Collections - Live Preview', () => {
|
||||
} as MessageEvent as LivePreviewMessageEvent<Page>,
|
||||
initialData: {
|
||||
title: 'Test Page',
|
||||
id: 1,
|
||||
id: 1 as number | string,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
hero: {
|
||||
type: 'highImpact',
|
||||
},
|
||||
slug: 'testPage',
|
||||
} as Page,
|
||||
})
|
||||
|
||||
@@ -177,6 +183,24 @@ describe('Collections - Live Preview', () => {
|
||||
expect(mergedData.title).toEqual('Test Page (Changed)')
|
||||
})
|
||||
|
||||
it('— strings - merges localized data', async () => {
|
||||
const initialData = await createPageWithInitialData({
|
||||
title: 'Test Page',
|
||||
})
|
||||
|
||||
const mergedData = await mergeData({
|
||||
depth: 1,
|
||||
incomingData: {
|
||||
...initialData,
|
||||
localizedTitle: 'Test Page (Changed)',
|
||||
},
|
||||
initialData,
|
||||
collectionSlug: pagesSlug,
|
||||
})
|
||||
|
||||
expect(mergedData.localizedTitle).toEqual('Test Page (Changed)')
|
||||
})
|
||||
|
||||
it('— arrays - can clear all rows', async () => {
|
||||
const initialData = await createPageWithInitialData({
|
||||
title: 'Test Page',
|
||||
@@ -751,7 +775,7 @@ describe('Collections - Live Preview', () => {
|
||||
expect(merge2.richTextSlate[3].value).toMatchObject(media)
|
||||
})
|
||||
|
||||
it('— relationships - populates within Lexical rich text editor', async () => {
|
||||
async function lexicalTest(fieldName: string) {
|
||||
const initialData = await createPageWithInitialData({
|
||||
title: 'Test Page',
|
||||
})
|
||||
@@ -762,7 +786,7 @@ describe('Collections - Live Preview', () => {
|
||||
collectionSlug: pagesSlug,
|
||||
incomingData: {
|
||||
...initialData,
|
||||
richTextLexical: {
|
||||
[fieldName]: {
|
||||
root: {
|
||||
type: 'root',
|
||||
format: '',
|
||||
@@ -804,12 +828,12 @@ describe('Collections - Live Preview', () => {
|
||||
initialData,
|
||||
})
|
||||
|
||||
expect(merge1.richTextLexical.root.children).toHaveLength(3)
|
||||
expect(merge1.richTextLexical.root.children[0].type).toEqual('relationship')
|
||||
expect(merge1.richTextLexical.root.children[0].value).toMatchObject(testPost)
|
||||
expect(merge1.richTextLexical.root.children[1].type).toEqual('paragraph')
|
||||
expect(merge1.richTextLexical.root.children[2].type).toEqual('upload')
|
||||
expect(merge1.richTextLexical.root.children[2].value).toMatchObject(media)
|
||||
expect(merge1[fieldName].root.children).toHaveLength(3)
|
||||
expect(merge1[fieldName].root.children[0].type).toEqual('relationship')
|
||||
expect(merge1[fieldName].root.children[0].value).toMatchObject(testPost)
|
||||
expect(merge1[fieldName].root.children[1].type).toEqual('paragraph')
|
||||
expect(merge1[fieldName].root.children[2].type).toEqual('upload')
|
||||
expect(merge1[fieldName].root.children[2].value).toMatchObject(media)
|
||||
|
||||
// Add a node before the populated one
|
||||
const merge2 = await mergeData({
|
||||
@@ -817,7 +841,7 @@ describe('Collections - Live Preview', () => {
|
||||
collectionSlug: pagesSlug,
|
||||
incomingData: {
|
||||
...merge1,
|
||||
richTextLexical: {
|
||||
[fieldName]: {
|
||||
root: {
|
||||
type: 'root',
|
||||
format: '',
|
||||
@@ -867,13 +891,23 @@ describe('Collections - Live Preview', () => {
|
||||
initialData: merge1,
|
||||
})
|
||||
|
||||
expect(merge2.richTextLexical.root.children).toHaveLength(4)
|
||||
expect(merge2.richTextLexical.root.children[0].type).toEqual('relationship')
|
||||
expect(merge2.richTextLexical.root.children[0].value).toMatchObject(testPost)
|
||||
expect(merge2.richTextLexical.root.children[1].type).toEqual('paragraph')
|
||||
expect(merge2.richTextLexical.root.children[2].type).toEqual('paragraph')
|
||||
expect(merge2.richTextLexical.root.children[3].type).toEqual('upload')
|
||||
expect(merge2.richTextLexical.root.children[3].value).toMatchObject(media)
|
||||
expect(merge2[fieldName].root.children).toHaveLength(4)
|
||||
expect(merge2[fieldName].root.children[0].type).toEqual('relationship')
|
||||
expect(merge2[fieldName].root.children[0].value).toMatchObject(testPost)
|
||||
expect(merge2[fieldName].root.children[1].type).toEqual('paragraph')
|
||||
expect(merge2[fieldName].root.children[2].type).toEqual('paragraph')
|
||||
expect(merge2[fieldName].root.children[3].type).toEqual('upload')
|
||||
expect(merge2[fieldName].root.children[3].value).toMatchObject(media)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('— relationships - populates within Lexical rich text editor', async () => {
|
||||
await lexicalTest('richTextLexical')
|
||||
})
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('— relationships - populates within Localized Lexical rich text editor', async () => {
|
||||
await lexicalTest('richTextLexicalLocalized')
|
||||
})
|
||||
|
||||
it('— relationships - re-populates externally updated relationships', async () => {
|
||||
|
||||
@@ -98,7 +98,7 @@ export interface Config {
|
||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: number;
|
||||
defaultIDType: string;
|
||||
};
|
||||
globals: {
|
||||
header: Header;
|
||||
@@ -142,7 +142,7 @@ export interface UserAuthOperations {
|
||||
export interface MediaBlock {
|
||||
invertBackground?: boolean | null;
|
||||
position?: ('default' | 'fullscreen') | null;
|
||||
media: number | Media;
|
||||
media: string | Media;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'mediaBlock';
|
||||
@@ -152,7 +152,7 @@ export interface MediaBlock {
|
||||
* via the `definition` "media".
|
||||
*/
|
||||
export interface Media {
|
||||
id: number;
|
||||
id: string;
|
||||
alt: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -171,7 +171,7 @@ export interface Media {
|
||||
* via the `definition` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: number;
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
email: string;
|
||||
@@ -195,9 +195,9 @@ export interface User {
|
||||
* via the `definition` "pages".
|
||||
*/
|
||||
export interface Page {
|
||||
id: number;
|
||||
id: string;
|
||||
slug: string;
|
||||
tenant?: (number | null) | Tenant;
|
||||
tenant?: (string | null) | Tenant;
|
||||
title: string;
|
||||
hero: {
|
||||
type: 'none' | 'highImpact' | 'lowImpact';
|
||||
@@ -206,7 +206,7 @@ export interface Page {
|
||||
[k: string]: unknown;
|
||||
}[]
|
||||
| null;
|
||||
media?: (number | null) | Media;
|
||||
media?: (string | null) | Media;
|
||||
};
|
||||
layout?:
|
||||
| (
|
||||
@@ -225,11 +225,11 @@ export interface Page {
|
||||
reference?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'pages';
|
||||
value: number | Page;
|
||||
value: string | Page;
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
@@ -262,11 +262,11 @@ export interface Page {
|
||||
reference?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'pages';
|
||||
value: number | Page;
|
||||
value: string | Page;
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
@@ -285,7 +285,7 @@ export interface Page {
|
||||
| {
|
||||
invertBackground?: boolean | null;
|
||||
position?: ('default' | 'fullscreen') | null;
|
||||
media: number | Media;
|
||||
media: string | Media;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'mediaBlock';
|
||||
@@ -298,12 +298,12 @@ export interface Page {
|
||||
| null;
|
||||
populateBy?: ('collection' | 'selection') | null;
|
||||
relationTo?: 'posts' | null;
|
||||
categories?: (number | Category)[] | null;
|
||||
categories?: (string | Category)[] | null;
|
||||
limit?: number | null;
|
||||
selectedDocs?:
|
||||
| {
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
@@ -312,7 +312,7 @@ export interface Page {
|
||||
populatedDocs?:
|
||||
| {
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
@@ -326,7 +326,7 @@ export interface Page {
|
||||
)[]
|
||||
| null;
|
||||
localizedTitle?: string | null;
|
||||
relationToLocalized?: (number | null) | Post;
|
||||
relationToLocalized?: (string | null) | Post;
|
||||
richTextSlate?:
|
||||
| {
|
||||
[k: string]: unknown;
|
||||
@@ -347,22 +347,37 @@ export interface Page {
|
||||
};
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
relationshipAsUpload?: (number | null) | Media;
|
||||
relationshipMonoHasOne?: (number | null) | Post;
|
||||
relationshipMonoHasMany?: (number | Post)[] | null;
|
||||
richTextLexicalLocalized?: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: string;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
relationshipAsUpload?: (string | null) | Media;
|
||||
relationshipMonoHasOne?: (string | null) | Post;
|
||||
relationshipMonoHasMany?: (string | Post)[] | null;
|
||||
relationshipPolyHasOne?: {
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null;
|
||||
relationshipPolyHasMany?:
|
||||
| {
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
}[]
|
||||
| null;
|
||||
arrayOfRelationships?:
|
||||
| {
|
||||
uploadInArray?: (number | null) | Media;
|
||||
uploadInArray?: (string | null) | Media;
|
||||
richTextInArray?: {
|
||||
root: {
|
||||
type: string;
|
||||
@@ -378,28 +393,28 @@ export interface Page {
|
||||
};
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
relationshipInArrayMonoHasOne?: (number | null) | Post;
|
||||
relationshipInArrayMonoHasMany?: (number | Post)[] | null;
|
||||
relationshipInArrayMonoHasOne?: (string | null) | Post;
|
||||
relationshipInArrayMonoHasMany?: (string | Post)[] | null;
|
||||
relationshipInArrayPolyHasOne?: {
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null;
|
||||
relationshipInArrayPolyHasMany?:
|
||||
| {
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
}[]
|
||||
| null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
tab?: {
|
||||
relationshipInTab?: (number | null) | Post;
|
||||
relationshipInTab?: (string | null) | Post;
|
||||
};
|
||||
meta?: {
|
||||
title?: string | null;
|
||||
description?: string | null;
|
||||
image?: (number | null) | Media;
|
||||
image?: (string | null) | Media;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -409,7 +424,7 @@ export interface Page {
|
||||
* via the `definition` "tenants".
|
||||
*/
|
||||
export interface Tenant {
|
||||
id: number;
|
||||
id: string;
|
||||
title: string;
|
||||
clientURL: string;
|
||||
updatedAt: string;
|
||||
@@ -420,9 +435,9 @@ export interface Tenant {
|
||||
* via the `definition` "posts".
|
||||
*/
|
||||
export interface Post {
|
||||
id: number;
|
||||
id: string;
|
||||
slug: string;
|
||||
tenant?: (number | null) | Tenant;
|
||||
tenant?: (string | null) | Tenant;
|
||||
title: string;
|
||||
hero: {
|
||||
type: 'none' | 'highImpact' | 'lowImpact';
|
||||
@@ -431,7 +446,7 @@ export interface Post {
|
||||
[k: string]: unknown;
|
||||
}[]
|
||||
| null;
|
||||
media?: (number | null) | Media;
|
||||
media?: (string | null) | Media;
|
||||
};
|
||||
layout?:
|
||||
| (
|
||||
@@ -450,11 +465,11 @@ export interface Post {
|
||||
reference?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'pages';
|
||||
value: number | Page;
|
||||
value: string | Page;
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
@@ -487,11 +502,11 @@ export interface Post {
|
||||
reference?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'pages';
|
||||
value: number | Page;
|
||||
value: string | Page;
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
@@ -510,7 +525,7 @@ export interface Post {
|
||||
| {
|
||||
invertBackground?: boolean | null;
|
||||
position?: ('default' | 'fullscreen') | null;
|
||||
media: number | Media;
|
||||
media: string | Media;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'mediaBlock';
|
||||
@@ -523,12 +538,12 @@ export interface Post {
|
||||
| null;
|
||||
populateBy?: ('collection' | 'selection') | null;
|
||||
relationTo?: 'posts' | null;
|
||||
categories?: (number | Category)[] | null;
|
||||
categories?: (string | Category)[] | null;
|
||||
limit?: number | null;
|
||||
selectedDocs?:
|
||||
| {
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
@@ -537,7 +552,7 @@ export interface Post {
|
||||
populatedDocs?:
|
||||
| {
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
@@ -550,12 +565,12 @@ export interface Post {
|
||||
}
|
||||
)[]
|
||||
| null;
|
||||
relatedPosts?: (number | Post)[] | null;
|
||||
relatedPosts?: (string | Post)[] | null;
|
||||
localizedTitle?: string | null;
|
||||
meta?: {
|
||||
title?: string | null;
|
||||
description?: string | null;
|
||||
image?: (number | null) | Media;
|
||||
image?: (string | null) | Media;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -566,7 +581,7 @@ export interface Post {
|
||||
* via the `definition` "categories".
|
||||
*/
|
||||
export interface Category {
|
||||
id: number;
|
||||
id: string;
|
||||
title?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -576,9 +591,9 @@ export interface Category {
|
||||
* via the `definition` "ssr".
|
||||
*/
|
||||
export interface Ssr {
|
||||
id: number;
|
||||
id: string;
|
||||
slug: string;
|
||||
tenant?: (number | null) | Tenant;
|
||||
tenant?: (string | null) | Tenant;
|
||||
title: string;
|
||||
hero: {
|
||||
type: 'none' | 'highImpact' | 'lowImpact';
|
||||
@@ -587,7 +602,7 @@ export interface Ssr {
|
||||
[k: string]: unknown;
|
||||
}[]
|
||||
| null;
|
||||
media?: (number | null) | Media;
|
||||
media?: (string | null) | Media;
|
||||
};
|
||||
layout?:
|
||||
| (
|
||||
@@ -606,11 +621,11 @@ export interface Ssr {
|
||||
reference?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'pages';
|
||||
value: number | Page;
|
||||
value: string | Page;
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
@@ -643,11 +658,11 @@ export interface Ssr {
|
||||
reference?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'pages';
|
||||
value: number | Page;
|
||||
value: string | Page;
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
@@ -666,7 +681,7 @@ export interface Ssr {
|
||||
| {
|
||||
invertBackground?: boolean | null;
|
||||
position?: ('default' | 'fullscreen') | null;
|
||||
media: number | Media;
|
||||
media: string | Media;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'mediaBlock';
|
||||
@@ -679,12 +694,12 @@ export interface Ssr {
|
||||
| null;
|
||||
populateBy?: ('collection' | 'selection') | null;
|
||||
relationTo?: 'posts' | null;
|
||||
categories?: (number | Category)[] | null;
|
||||
categories?: (string | Category)[] | null;
|
||||
limit?: number | null;
|
||||
selectedDocs?:
|
||||
| {
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
@@ -693,7 +708,7 @@ export interface Ssr {
|
||||
populatedDocs?:
|
||||
| {
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
@@ -709,7 +724,7 @@ export interface Ssr {
|
||||
meta?: {
|
||||
title?: string | null;
|
||||
description?: string | null;
|
||||
image?: (number | null) | Media;
|
||||
image?: (string | null) | Media;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -719,9 +734,9 @@ export interface Ssr {
|
||||
* via the `definition` "ssr-autosave".
|
||||
*/
|
||||
export interface SsrAutosave {
|
||||
id: number;
|
||||
id: string;
|
||||
slug: string;
|
||||
tenant?: (number | null) | Tenant;
|
||||
tenant?: (string | null) | Tenant;
|
||||
title: string;
|
||||
hero: {
|
||||
type: 'none' | 'highImpact' | 'lowImpact';
|
||||
@@ -730,7 +745,7 @@ export interface SsrAutosave {
|
||||
[k: string]: unknown;
|
||||
}[]
|
||||
| null;
|
||||
media?: (number | null) | Media;
|
||||
media?: (string | null) | Media;
|
||||
};
|
||||
layout?:
|
||||
| (
|
||||
@@ -749,11 +764,11 @@ export interface SsrAutosave {
|
||||
reference?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'pages';
|
||||
value: number | Page;
|
||||
value: string | Page;
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
@@ -786,11 +801,11 @@ export interface SsrAutosave {
|
||||
reference?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'pages';
|
||||
value: number | Page;
|
||||
value: string | Page;
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
@@ -809,7 +824,7 @@ export interface SsrAutosave {
|
||||
| {
|
||||
invertBackground?: boolean | null;
|
||||
position?: ('default' | 'fullscreen') | null;
|
||||
media: number | Media;
|
||||
media: string | Media;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'mediaBlock';
|
||||
@@ -822,12 +837,12 @@ export interface SsrAutosave {
|
||||
| null;
|
||||
populateBy?: ('collection' | 'selection') | null;
|
||||
relationTo?: 'posts' | null;
|
||||
categories?: (number | Category)[] | null;
|
||||
categories?: (string | Category)[] | null;
|
||||
limit?: number | null;
|
||||
selectedDocs?:
|
||||
| {
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
@@ -836,7 +851,7 @@ export interface SsrAutosave {
|
||||
populatedDocs?:
|
||||
| {
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
}[]
|
||||
| null;
|
||||
/**
|
||||
@@ -852,7 +867,7 @@ export interface SsrAutosave {
|
||||
meta?: {
|
||||
title?: string | null;
|
||||
description?: string | null;
|
||||
image?: (number | null) | Media;
|
||||
image?: (string | null) | Media;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -865,7 +880,7 @@ export interface SsrAutosave {
|
||||
* via the `definition` "collection-level-config".
|
||||
*/
|
||||
export interface CollectionLevelConfig {
|
||||
id: number;
|
||||
id: string;
|
||||
title?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -875,48 +890,48 @@ export interface CollectionLevelConfig {
|
||||
* via the `definition` "payload-locked-documents".
|
||||
*/
|
||||
export interface PayloadLockedDocument {
|
||||
id: number;
|
||||
id: string;
|
||||
document?:
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
value: string | User;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'pages';
|
||||
value: number | Page;
|
||||
value: string | Page;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'ssr';
|
||||
value: number | Ssr;
|
||||
value: string | Ssr;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'ssr-autosave';
|
||||
value: number | SsrAutosave;
|
||||
value: string | SsrAutosave;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'tenants';
|
||||
value: number | Tenant;
|
||||
value: string | Tenant;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'categories';
|
||||
value: number | Category;
|
||||
value: string | Category;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'media';
|
||||
value: number | Media;
|
||||
value: string | Media;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'collection-level-config';
|
||||
value: number | CollectionLevelConfig;
|
||||
value: string | CollectionLevelConfig;
|
||||
} | null);
|
||||
globalSlug?: string | null;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
value: string | User;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
@@ -926,10 +941,10 @@ export interface PayloadLockedDocument {
|
||||
* via the `definition` "payload-preferences".
|
||||
*/
|
||||
export interface PayloadPreference {
|
||||
id: number;
|
||||
id: string;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: number | User;
|
||||
value: string | User;
|
||||
};
|
||||
key?: string | null;
|
||||
value?:
|
||||
@@ -949,7 +964,7 @@ export interface PayloadPreference {
|
||||
* via the `definition` "payload-migrations".
|
||||
*/
|
||||
export interface PayloadMigration {
|
||||
id: number;
|
||||
id: string;
|
||||
name?: string | null;
|
||||
batch?: number | null;
|
||||
updatedAt: string;
|
||||
@@ -1071,6 +1086,7 @@ export interface PagesSelect<T extends boolean = true> {
|
||||
relationToLocalized?: T;
|
||||
richTextSlate?: T;
|
||||
richTextLexical?: T;
|
||||
richTextLexicalLocalized?: T;
|
||||
relationshipAsUpload?: T;
|
||||
relationshipMonoHasOne?: T;
|
||||
relationshipMonoHasMany?: T;
|
||||
@@ -1489,7 +1505,7 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
|
||||
* via the `definition` "header".
|
||||
*/
|
||||
export interface Header {
|
||||
id: number;
|
||||
id: string;
|
||||
navItems?:
|
||||
| {
|
||||
link: {
|
||||
@@ -1498,11 +1514,11 @@ export interface Header {
|
||||
reference?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'pages';
|
||||
value: number | Page;
|
||||
value: string | Page;
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
@@ -1522,7 +1538,7 @@ export interface Header {
|
||||
* via the `definition` "footer".
|
||||
*/
|
||||
export interface Footer {
|
||||
id: number;
|
||||
id: string;
|
||||
navItems?:
|
||||
| {
|
||||
link: {
|
||||
@@ -1531,11 +1547,11 @@ export interface Footer {
|
||||
reference?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: number | Post;
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'pages';
|
||||
value: number | Page;
|
||||
value: string | Page;
|
||||
} | null);
|
||||
url?: string | null;
|
||||
label: string;
|
||||
|
||||
@@ -9,6 +9,7 @@ export const home: Omit<Page, 'createdAt' | 'id' | 'updatedAt'> = {
|
||||
description: 'This is an example of live preview on a page.',
|
||||
},
|
||||
tenant: '{{TENANT_1_ID}}',
|
||||
localizedTitle: 'Localized Title',
|
||||
hero: {
|
||||
type: 'highImpact',
|
||||
richText: [
|
||||
@@ -169,6 +170,35 @@ export const home: Omit<Page, 'createdAt' | 'id' | 'updatedAt'> = {
|
||||
direction: null,
|
||||
},
|
||||
},
|
||||
richTextLexicalLocalized: {
|
||||
root: {
|
||||
type: 'root',
|
||||
format: '',
|
||||
indent: 0,
|
||||
version: 1,
|
||||
children: [
|
||||
{
|
||||
children: [],
|
||||
direction: null,
|
||||
format: '',
|
||||
indent: 0,
|
||||
type: 'paragraph',
|
||||
version: 1,
|
||||
},
|
||||
{
|
||||
format: '',
|
||||
type: 'upload',
|
||||
version: 1,
|
||||
fields: null,
|
||||
relationTo: 'media',
|
||||
value: {
|
||||
id: '{{MEDIA_ID}}',
|
||||
},
|
||||
},
|
||||
],
|
||||
direction: null,
|
||||
},
|
||||
},
|
||||
relationshipMonoHasMany: ['{{POST_1_ID}}'],
|
||||
relationshipMonoHasOne: '{{POST_1_ID}}',
|
||||
relationshipPolyHasMany: [{ relationTo: 'posts', value: '{{POST_1_ID}}' }],
|
||||
|
||||
Reference in New Issue
Block a user