fix(ui): stale server components when rows are moved (#9410)
We need to trigger server component re-rendering for moving rows, just like we do for adding or deleting rows. Video of the issue: https://github.com/user-attachments/assets/32fb31c5-f304-4082-8028-59a6ad723fbe
This commit is contained in:
@@ -198,6 +198,7 @@ export function fieldReducer(state: FormState, action: FieldAction): FormState {
|
|||||||
...flattenRows(path, rows),
|
...flattenRows(path, rows),
|
||||||
[path]: {
|
[path]: {
|
||||||
...state[path],
|
...state[path],
|
||||||
|
requiresRender: true,
|
||||||
rows: rowStateCopy,
|
rows: rowStateCopy,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1044,6 +1044,30 @@ describe('lexicalMain', () => {
|
|||||||
await expect(htmlOutput).toBeVisible()
|
await expect(htmlOutput).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('ensure lexical fields in blocks have correct value when moving blocks', async () => {
|
||||||
|
// Previously, we had the issue that the lexical field values did not update when moving blocks, as the MOVE_ROW form action did not request
|
||||||
|
// re-rendering of server components
|
||||||
|
await page.goto('http://localhost:3000/admin/collections/LexicalInBlock?limit=10')
|
||||||
|
await page.locator('.cell-id a').first().click()
|
||||||
|
await page.waitForURL(`**/collections/LexicalInBlock/**`)
|
||||||
|
|
||||||
|
await expect(page.locator('#blocks-row-0 .LexicalEditorTheme__paragraph')).toContainText('1')
|
||||||
|
await expect(page.locator('#blocks-row-0 .section-title__input')).toHaveValue('1') // block name
|
||||||
|
await expect(page.locator('#blocks-row-1 .LexicalEditorTheme__paragraph')).toContainText('2')
|
||||||
|
await expect(page.locator('#blocks-row-1 .section-title__input')).toHaveValue('2') // block name
|
||||||
|
|
||||||
|
// Move block 1 to the end
|
||||||
|
await page.locator('#blocks-row-0 .array-actions__button').click()
|
||||||
|
await expect(page.locator('#blocks-row-0 .popup__content')).toBeVisible()
|
||||||
|
|
||||||
|
await page.locator('#blocks-row-0 .popup__content').getByText('Move Down').click()
|
||||||
|
|
||||||
|
await expect(page.locator('#blocks-row-0 .LexicalEditorTheme__paragraph')).toContainText('2')
|
||||||
|
await expect(page.locator('#blocks-row-0 .section-title__input')).toHaveValue('2') // block name
|
||||||
|
await expect(page.locator('#blocks-row-1 .LexicalEditorTheme__paragraph')).toContainText('1')
|
||||||
|
await expect(page.locator('#blocks-row-1 .section-title__input')).toHaveValue('1') // block name
|
||||||
|
})
|
||||||
|
|
||||||
describe('localization', () => {
|
describe('localization', () => {
|
||||||
test.skip('ensure simple localized lexical field works', async () => {
|
test.skip('ensure simple localized lexical field works', async () => {
|
||||||
await navigateToLexicalFields(true, true)
|
await navigateToLexicalFields(true, true)
|
||||||
|
|||||||
22
test/fields/collections/LexicalInBlock/index.ts
Normal file
22
test/fields/collections/LexicalInBlock/index.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import type { CollectionConfig } from 'payload'
|
||||||
|
|
||||||
|
export const LexicalInBlock: CollectionConfig = {
|
||||||
|
slug: 'LexicalInBlock',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'blocks',
|
||||||
|
type: 'blocks',
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
slug: 'lexicalInBlock2',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'lexical',
|
||||||
|
type: 'richText',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import GroupFields from './collections/Group/index.js'
|
|||||||
import IndexedFields from './collections/Indexed/index.js'
|
import IndexedFields from './collections/Indexed/index.js'
|
||||||
import JSONFields from './collections/JSON/index.js'
|
import JSONFields from './collections/JSON/index.js'
|
||||||
import { LexicalFields } from './collections/Lexical/index.js'
|
import { LexicalFields } from './collections/Lexical/index.js'
|
||||||
|
import { LexicalInBlock } from './collections/LexicalInBlock/index.js'
|
||||||
import { LexicalLocalizedFields } from './collections/LexicalLocalized/index.js'
|
import { LexicalLocalizedFields } from './collections/LexicalLocalized/index.js'
|
||||||
import { LexicalMigrateFields } from './collections/LexicalMigrate/index.js'
|
import { LexicalMigrateFields } from './collections/LexicalMigrate/index.js'
|
||||||
import { LexicalObjectReferenceBugCollection } from './collections/LexicalObjectReferenceBug/index.js'
|
import { LexicalObjectReferenceBugCollection } from './collections/LexicalObjectReferenceBug/index.js'
|
||||||
@@ -63,6 +64,8 @@ export const collectionSlugs: CollectionConfig[] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
LexicalInBlock,
|
||||||
|
|
||||||
ArrayFields,
|
ArrayFields,
|
||||||
BlockFields,
|
BlockFields,
|
||||||
CheckboxFields,
|
CheckboxFields,
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export interface Config {
|
|||||||
'lexical-localized-fields': LexicalLocalizedField;
|
'lexical-localized-fields': LexicalLocalizedField;
|
||||||
lexicalObjectReferenceBug: LexicalObjectReferenceBug;
|
lexicalObjectReferenceBug: LexicalObjectReferenceBug;
|
||||||
users: User;
|
users: User;
|
||||||
|
LexicalInBlock: LexicalInBlock;
|
||||||
'array-fields': ArrayField;
|
'array-fields': ArrayField;
|
||||||
'block-fields': BlockField;
|
'block-fields': BlockField;
|
||||||
'checkbox-fields': CheckboxField;
|
'checkbox-fields': CheckboxField;
|
||||||
@@ -75,6 +76,7 @@ export interface Config {
|
|||||||
'lexical-localized-fields': LexicalLocalizedFieldsSelect<false> | LexicalLocalizedFieldsSelect<true>;
|
'lexical-localized-fields': LexicalLocalizedFieldsSelect<false> | LexicalLocalizedFieldsSelect<true>;
|
||||||
lexicalObjectReferenceBug: LexicalObjectReferenceBugSelect<false> | LexicalObjectReferenceBugSelect<true>;
|
lexicalObjectReferenceBug: LexicalObjectReferenceBugSelect<false> | LexicalObjectReferenceBugSelect<true>;
|
||||||
users: UsersSelect<false> | UsersSelect<true>;
|
users: UsersSelect<false> | UsersSelect<true>;
|
||||||
|
LexicalInBlock: LexicalInBlockSelect<false> | LexicalInBlockSelect<true>;
|
||||||
'array-fields': ArrayFieldsSelect<false> | ArrayFieldsSelect<true>;
|
'array-fields': ArrayFieldsSelect<false> | ArrayFieldsSelect<true>;
|
||||||
'block-fields': BlockFieldsSelect<false> | BlockFieldsSelect<true>;
|
'block-fields': BlockFieldsSelect<false> | BlockFieldsSelect<true>;
|
||||||
'checkbox-fields': CheckboxFieldsSelect<false> | CheckboxFieldsSelect<true>;
|
'checkbox-fields': CheckboxFieldsSelect<false> | CheckboxFieldsSelect<true>;
|
||||||
@@ -123,9 +125,9 @@ export interface Config {
|
|||||||
user: User & {
|
user: User & {
|
||||||
collection: 'users';
|
collection: 'users';
|
||||||
};
|
};
|
||||||
jobs?: {
|
jobs: {
|
||||||
tasks: unknown;
|
tasks: unknown;
|
||||||
workflows?: unknown;
|
workflows: unknown;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export interface UserAuthOperations {
|
export interface UserAuthOperations {
|
||||||
@@ -394,6 +396,37 @@ export interface User {
|
|||||||
lockUntil?: string | null;
|
lockUntil?: string | null;
|
||||||
password?: string | null;
|
password?: string | null;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "LexicalInBlock".
|
||||||
|
*/
|
||||||
|
export interface LexicalInBlock {
|
||||||
|
id: string;
|
||||||
|
blocks?:
|
||||||
|
| {
|
||||||
|
lexical?: {
|
||||||
|
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;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'lexicalInBlock2';
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "array-fields".
|
* via the `definition` "array-fields".
|
||||||
@@ -1783,6 +1816,10 @@ export interface PayloadLockedDocument {
|
|||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: string | User;
|
||||||
} | null)
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'LexicalInBlock';
|
||||||
|
value: string | LexicalInBlock;
|
||||||
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'array-fields';
|
relationTo: 'array-fields';
|
||||||
value: string | ArrayField;
|
value: string | ArrayField;
|
||||||
@@ -2025,6 +2062,25 @@ export interface UsersSelect<T extends boolean = true> {
|
|||||||
loginAttempts?: T;
|
loginAttempts?: T;
|
||||||
lockUntil?: T;
|
lockUntil?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "LexicalInBlock_select".
|
||||||
|
*/
|
||||||
|
export interface LexicalInBlockSelect<T extends boolean = true> {
|
||||||
|
blocks?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
lexicalInBlock2?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
lexical?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "array-fields_select".
|
* via the `definition` "array-fields_select".
|
||||||
|
|||||||
@@ -367,7 +367,7 @@ export const seed = async (_payload: Payload) => {
|
|||||||
collection: lexicalLocalizedFieldsSlug,
|
collection: lexicalLocalizedFieldsSlug,
|
||||||
data: {
|
data: {
|
||||||
title: 'Localized Lexical en',
|
title: 'Localized Lexical en',
|
||||||
lexicalBlocksLocalized: textToLexicalJSON({ text: 'English text' }) as any,
|
lexicalBlocksLocalized: textToLexicalJSON({ text: 'English text' }),
|
||||||
lexicalBlocksSubLocalized: generateLexicalLocalizedRichText(
|
lexicalBlocksSubLocalized: generateLexicalLocalizedRichText(
|
||||||
'Shared text',
|
'Shared text',
|
||||||
'English text in block',
|
'English text in block',
|
||||||
@@ -381,7 +381,7 @@ export const seed = async (_payload: Payload) => {
|
|||||||
await _payload.create({
|
await _payload.create({
|
||||||
collection: lexicalRelationshipFieldsSlug,
|
collection: lexicalRelationshipFieldsSlug,
|
||||||
data: {
|
data: {
|
||||||
richText: textToLexicalJSON({ text: 'English text' }) as any,
|
richText: textToLexicalJSON({ text: 'English text' }),
|
||||||
},
|
},
|
||||||
depth: 0,
|
depth: 0,
|
||||||
overrideAccess: true,
|
overrideAccess: true,
|
||||||
@@ -392,7 +392,7 @@ export const seed = async (_payload: Payload) => {
|
|||||||
id: lexicalLocalizedDoc1.id,
|
id: lexicalLocalizedDoc1.id,
|
||||||
data: {
|
data: {
|
||||||
title: 'Localized Lexical es',
|
title: 'Localized Lexical es',
|
||||||
lexicalBlocksLocalized: textToLexicalJSON({ text: 'Spanish text' }) as any,
|
lexicalBlocksLocalized: textToLexicalJSON({ text: 'Spanish text' }),
|
||||||
lexicalBlocksSubLocalized: generateLexicalLocalizedRichText(
|
lexicalBlocksSubLocalized: generateLexicalLocalizedRichText(
|
||||||
'Shared text',
|
'Shared text',
|
||||||
'Spanish text in block',
|
'Spanish text in block',
|
||||||
@@ -408,10 +408,7 @@ export const seed = async (_payload: Payload) => {
|
|||||||
collection: lexicalLocalizedFieldsSlug,
|
collection: lexicalLocalizedFieldsSlug,
|
||||||
data: {
|
data: {
|
||||||
title: 'Localized Lexical en 2',
|
title: 'Localized Lexical en 2',
|
||||||
lexicalSimple: textToLexicalJSON({
|
|
||||||
text: 'English text 2',
|
|
||||||
lexicalLocalizedRelID: lexicalLocalizedDoc1.id,
|
|
||||||
}),
|
|
||||||
lexicalBlocksLocalized: textToLexicalJSON({
|
lexicalBlocksLocalized: textToLexicalJSON({
|
||||||
text: 'English text 2',
|
text: 'English text 2',
|
||||||
lexicalLocalizedRelID: lexicalLocalizedDoc1.id,
|
lexicalLocalizedRelID: lexicalLocalizedDoc1.id,
|
||||||
@@ -431,10 +428,7 @@ export const seed = async (_payload: Payload) => {
|
|||||||
id: lexicalLocalizedDoc2.id,
|
id: lexicalLocalizedDoc2.id,
|
||||||
data: {
|
data: {
|
||||||
title: 'Localized Lexical es 2',
|
title: 'Localized Lexical es 2',
|
||||||
lexicalSimple: textToLexicalJSON({
|
|
||||||
text: 'Spanish text 2',
|
|
||||||
lexicalLocalizedRelID: lexicalLocalizedDoc1.id,
|
|
||||||
}),
|
|
||||||
lexicalBlocksLocalized: textToLexicalJSON({
|
lexicalBlocksLocalized: textToLexicalJSON({
|
||||||
text: 'Spanish text 2',
|
text: 'Spanish text 2',
|
||||||
lexicalLocalizedRelID: lexicalLocalizedDoc1.id,
|
lexicalLocalizedRelID: lexicalLocalizedDoc1.id,
|
||||||
@@ -479,6 +473,24 @@ export const seed = async (_payload: Payload) => {
|
|||||||
text: 'text',
|
text: 'text',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await _payload.create({
|
||||||
|
collection: 'LexicalInBlock',
|
||||||
|
data: {
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
blockType: 'lexicalInBlock2',
|
||||||
|
blockName: '1',
|
||||||
|
lexical: textToLexicalJSON({ text: '1' }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockType: 'lexicalInBlock2',
|
||||||
|
blockName: '2',
|
||||||
|
lexical: textToLexicalJSON({ text: '2' }),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function clearAndSeedEverything(_payload: Payload) {
|
export async function clearAndSeedEverything(_payload: Payload) {
|
||||||
|
|||||||
Reference in New Issue
Block a user