Skip to content

Commit e3866c4

Browse files
authored
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
1 parent f338c5c commit e3866c4

File tree

6 files changed

+131
-13
lines changed

6 files changed

+131
-13
lines changed

packages/ui/src/forms/Form/fieldReducer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ export function fieldReducer(state: FormState, action: FieldAction): FormState {
198198
...flattenRows(path, rows),
199199
[path]: {
200200
...state[path],
201+
requiresRender: true,
201202
rows: rowStateCopy,
202203
},
203204
}

test/fields/collections/Lexical/e2e/main/e2e.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,30 @@ describe('lexicalMain', () => {
10441044
await expect(htmlOutput).toBeVisible()
10451045
})
10461046

1047+
test('ensure lexical fields in blocks have correct value when moving blocks', async () => {
1048+
// 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
1049+
// re-rendering of server components
1050+
await page.goto('http://localhost:3000/admin/collections/LexicalInBlock?limit=10')
1051+
await page.locator('.cell-id a').first().click()
1052+
await page.waitForURL(`**/collections/LexicalInBlock/**`)
1053+
1054+
await expect(page.locator('#blocks-row-0 .LexicalEditorTheme__paragraph')).toContainText('1')
1055+
await expect(page.locator('#blocks-row-0 .section-title__input')).toHaveValue('1') // block name
1056+
await expect(page.locator('#blocks-row-1 .LexicalEditorTheme__paragraph')).toContainText('2')
1057+
await expect(page.locator('#blocks-row-1 .section-title__input')).toHaveValue('2') // block name
1058+
1059+
// Move block 1 to the end
1060+
await page.locator('#blocks-row-0 .array-actions__button').click()
1061+
await expect(page.locator('#blocks-row-0 .popup__content')).toBeVisible()
1062+
1063+
await page.locator('#blocks-row-0 .popup__content').getByText('Move Down').click()
1064+
1065+
await expect(page.locator('#blocks-row-0 .LexicalEditorTheme__paragraph')).toContainText('2')
1066+
await expect(page.locator('#blocks-row-0 .section-title__input')).toHaveValue('2') // block name
1067+
await expect(page.locator('#blocks-row-1 .LexicalEditorTheme__paragraph')).toContainText('1')
1068+
await expect(page.locator('#blocks-row-1 .section-title__input')).toHaveValue('1') // block name
1069+
})
1070+
10471071
describe('localization', () => {
10481072
test.skip('ensure simple localized lexical field works', async () => {
10491073
await navigateToLexicalFields(true, true)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { CollectionConfig } from 'payload'
2+
3+
export const LexicalInBlock: CollectionConfig = {
4+
slug: 'LexicalInBlock',
5+
fields: [
6+
{
7+
name: 'blocks',
8+
type: 'blocks',
9+
blocks: [
10+
{
11+
slug: 'lexicalInBlock2',
12+
fields: [
13+
{
14+
name: 'lexical',
15+
type: 'richText',
16+
},
17+
],
18+
},
19+
],
20+
},
21+
],
22+
}

test/fields/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import GroupFields from './collections/Group/index.js'
1919
import IndexedFields from './collections/Indexed/index.js'
2020
import JSONFields from './collections/JSON/index.js'
2121
import { LexicalFields } from './collections/Lexical/index.js'
22+
import { LexicalInBlock } from './collections/LexicalInBlock/index.js'
2223
import { LexicalLocalizedFields } from './collections/LexicalLocalized/index.js'
2324
import { LexicalMigrateFields } from './collections/LexicalMigrate/index.js'
2425
import { LexicalObjectReferenceBugCollection } from './collections/LexicalObjectReferenceBug/index.js'
@@ -63,6 +64,8 @@ export const collectionSlugs: CollectionConfig[] = [
6364
},
6465
],
6566
},
67+
LexicalInBlock,
68+
6669
ArrayFields,
6770
BlockFields,
6871
CheckboxFields,

test/fields/payload-types.ts

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export interface Config {
3333
'lexical-localized-fields': LexicalLocalizedField;
3434
lexicalObjectReferenceBug: LexicalObjectReferenceBug;
3535
users: User;
36+
LexicalInBlock: LexicalInBlock;
3637
'array-fields': ArrayField;
3738
'block-fields': BlockField;
3839
'checkbox-fields': CheckboxField;
@@ -75,6 +76,7 @@ export interface Config {
7576
'lexical-localized-fields': LexicalLocalizedFieldsSelect<false> | LexicalLocalizedFieldsSelect<true>;
7677
lexicalObjectReferenceBug: LexicalObjectReferenceBugSelect<false> | LexicalObjectReferenceBugSelect<true>;
7778
users: UsersSelect<false> | UsersSelect<true>;
79+
LexicalInBlock: LexicalInBlockSelect<false> | LexicalInBlockSelect<true>;
7880
'array-fields': ArrayFieldsSelect<false> | ArrayFieldsSelect<true>;
7981
'block-fields': BlockFieldsSelect<false> | BlockFieldsSelect<true>;
8082
'checkbox-fields': CheckboxFieldsSelect<false> | CheckboxFieldsSelect<true>;
@@ -123,9 +125,9 @@ export interface Config {
123125
user: User & {
124126
collection: 'users';
125127
};
126-
jobs?: {
128+
jobs: {
127129
tasks: unknown;
128-
workflows?: unknown;
130+
workflows: unknown;
129131
};
130132
}
131133
export interface UserAuthOperations {
@@ -394,6 +396,37 @@ export interface User {
394396
lockUntil?: string | null;
395397
password?: string | null;
396398
}
399+
/**
400+
* This interface was referenced by `Config`'s JSON-Schema
401+
* via the `definition` "LexicalInBlock".
402+
*/
403+
export interface LexicalInBlock {
404+
id: string;
405+
blocks?:
406+
| {
407+
lexical?: {
408+
root: {
409+
type: string;
410+
children: {
411+
type: string;
412+
version: number;
413+
[k: string]: unknown;
414+
}[];
415+
direction: ('ltr' | 'rtl') | null;
416+
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
417+
indent: number;
418+
version: number;
419+
};
420+
[k: string]: unknown;
421+
} | null;
422+
id?: string | null;
423+
blockName?: string | null;
424+
blockType: 'lexicalInBlock2';
425+
}[]
426+
| null;
427+
updatedAt: string;
428+
createdAt: string;
429+
}
397430
/**
398431
* This interface was referenced by `Config`'s JSON-Schema
399432
* via the `definition` "array-fields".
@@ -1783,6 +1816,10 @@ export interface PayloadLockedDocument {
17831816
relationTo: 'users';
17841817
value: string | User;
17851818
} | null)
1819+
| ({
1820+
relationTo: 'LexicalInBlock';
1821+
value: string | LexicalInBlock;
1822+
} | null)
17861823
| ({
17871824
relationTo: 'array-fields';
17881825
value: string | ArrayField;
@@ -2025,6 +2062,25 @@ export interface UsersSelect<T extends boolean = true> {
20252062
loginAttempts?: T;
20262063
lockUntil?: T;
20272064
}
2065+
/**
2066+
* This interface was referenced by `Config`'s JSON-Schema
2067+
* via the `definition` "LexicalInBlock_select".
2068+
*/
2069+
export interface LexicalInBlockSelect<T extends boolean = true> {
2070+
blocks?:
2071+
| T
2072+
| {
2073+
lexicalInBlock2?:
2074+
| T
2075+
| {
2076+
lexical?: T;
2077+
id?: T;
2078+
blockName?: T;
2079+
};
2080+
};
2081+
updatedAt?: T;
2082+
createdAt?: T;
2083+
}
20282084
/**
20292085
* This interface was referenced by `Config`'s JSON-Schema
20302086
* via the `definition` "array-fields_select".

test/fields/seed.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ export const seed = async (_payload: Payload) => {
367367
collection: lexicalLocalizedFieldsSlug,
368368
data: {
369369
title: 'Localized Lexical en',
370-
lexicalBlocksLocalized: textToLexicalJSON({ text: 'English text' }) as any,
370+
lexicalBlocksLocalized: textToLexicalJSON({ text: 'English text' }),
371371
lexicalBlocksSubLocalized: generateLexicalLocalizedRichText(
372372
'Shared text',
373373
'English text in block',
@@ -381,7 +381,7 @@ export const seed = async (_payload: Payload) => {
381381
await _payload.create({
382382
collection: lexicalRelationshipFieldsSlug,
383383
data: {
384-
richText: textToLexicalJSON({ text: 'English text' }) as any,
384+
richText: textToLexicalJSON({ text: 'English text' }),
385385
},
386386
depth: 0,
387387
overrideAccess: true,
@@ -392,7 +392,7 @@ export const seed = async (_payload: Payload) => {
392392
id: lexicalLocalizedDoc1.id,
393393
data: {
394394
title: 'Localized Lexical es',
395-
lexicalBlocksLocalized: textToLexicalJSON({ text: 'Spanish text' }) as any,
395+
lexicalBlocksLocalized: textToLexicalJSON({ text: 'Spanish text' }),
396396
lexicalBlocksSubLocalized: generateLexicalLocalizedRichText(
397397
'Shared text',
398398
'Spanish text in block',
@@ -408,10 +408,7 @@ export const seed = async (_payload: Payload) => {
408408
collection: lexicalLocalizedFieldsSlug,
409409
data: {
410410
title: 'Localized Lexical en 2',
411-
lexicalSimple: textToLexicalJSON({
412-
text: 'English text 2',
413-
lexicalLocalizedRelID: lexicalLocalizedDoc1.id,
414-
}),
411+
415412
lexicalBlocksLocalized: textToLexicalJSON({
416413
text: 'English text 2',
417414
lexicalLocalizedRelID: lexicalLocalizedDoc1.id,
@@ -431,10 +428,7 @@ export const seed = async (_payload: Payload) => {
431428
id: lexicalLocalizedDoc2.id,
432429
data: {
433430
title: 'Localized Lexical es 2',
434-
lexicalSimple: textToLexicalJSON({
435-
text: 'Spanish text 2',
436-
lexicalLocalizedRelID: lexicalLocalizedDoc1.id,
437-
}),
431+
438432
lexicalBlocksLocalized: textToLexicalJSON({
439433
text: 'Spanish text 2',
440434
lexicalLocalizedRelID: lexicalLocalizedDoc1.id,
@@ -479,6 +473,24 @@ export const seed = async (_payload: Payload) => {
479473
text: 'text',
480474
},
481475
})
476+
477+
await _payload.create({
478+
collection: 'LexicalInBlock',
479+
data: {
480+
blocks: [
481+
{
482+
blockType: 'lexicalInBlock2',
483+
blockName: '1',
484+
lexical: textToLexicalJSON({ text: '1' }),
485+
},
486+
{
487+
blockType: 'lexicalInBlock2',
488+
blockName: '2',
489+
lexical: textToLexicalJSON({ text: '2' }),
490+
},
491+
],
492+
},
493+
})
482494
}
483495

484496
export async function clearAndSeedEverything(_payload: Payload) {

0 commit comments

Comments
 (0)