diff --git a/packages/payload/src/fields/hooks/beforeChange/cloneDataFromOriginalDoc.ts b/packages/payload/src/fields/hooks/beforeChange/cloneDataFromOriginalDoc.ts index 0aadae2dc34..1db456767c4 100644 --- a/packages/payload/src/fields/hooks/beforeChange/cloneDataFromOriginalDoc.ts +++ b/packages/payload/src/fields/hooks/beforeChange/cloneDataFromOriginalDoc.ts @@ -5,6 +5,12 @@ export const cloneDataFromOriginalDoc = ( ): JsonArray | JsonObject => { if (Array.isArray(originalDocData)) { return originalDocData.map((row) => { + // Recurse for nested arrays so they stay arrays instead of being + // spread into index-keyed objects (`{...[1, 2]}` -> `{ 0: 1, 1: 2 }`). + if (Array.isArray(row)) { + return cloneDataFromOriginalDoc(row) + } + if (typeof row === 'object' && row != null) { return { ...row, diff --git a/test/fields/int.spec.ts b/test/fields/int.spec.ts index 1c1a720fa4a..c97672f3a07 100644 --- a/test/fields/int.spec.ts +++ b/test/fields/int.spec.ts @@ -4318,6 +4318,36 @@ describe('Fields', () => { expect(updatedJsonFieldsDoc.json.state).toEqual({}) }) + it('should preserve nested arrays in a json field omitted from a partial update', async () => { + const arrayOfArrays = [ + [1, 2], + [3, 4], + ] + + const jsonFieldsDoc = await payload.create({ + collection: 'json-fields', + data: { + customJSON: arrayOfArrays, + }, + }) + + expect(jsonFieldsDoc.customJSON).toStrictEqual(arrayOfArrays) + + // Partial update that intentionally omits `customJSON` so that its + // existing value is cloned from the original doc via + // `cloneDataFromOriginalDoc`. Nested arrays must survive that clone + // instead of being turned into index-keyed objects. + const updatedJsonFieldsDoc = await payload.update({ + id: jsonFieldsDoc.id, + collection: 'json-fields', + data: { + json: { foo: 'bar' }, + }, + }) + + expect(updatedJsonFieldsDoc.customJSON).toStrictEqual(arrayOfArrays) + }) + describe('querying', () => { let fooBar let bazBar