Skip to content

Commit 87137fe

Browse files
fix: corrects outgoing localized data from afterRead (#14603)
Additional incorrect data was being appended when querying with `locale: "all"` for localized groups and tabs. #### Before (incorrect) ```ts g1: { en: { g2: { g2a1: [ { text: 'EN Deep 1' }, ] } }, g2: { // <-- bad g2a1: [] } } ``` #### After (correct) ```ts g1: { en: { g2: { g2a1: [ { text: 'EN Deep 1' }, ] } }, } ```
1 parent 6e364fa commit 87137fe

File tree

4 files changed

+232
-82
lines changed

4 files changed

+232
-82
lines changed

packages/payload/src/fields/hooks/afterRead/promise.ts

Lines changed: 174 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,17 @@ export const promise = async ({
140140
}
141141
}
142142

143+
const shouldLocalizeField = fieldShouldBeLocalized({
144+
field,
145+
parentIsLocalized: parentIsLocalized!,
146+
})
147+
143148
const shouldHoistLocalizedValue: boolean = Boolean(
144149
flattenLocales &&
145150
fieldAffectsDataResult &&
146151
typeof siblingDoc[field.name!] === 'object' &&
147152
siblingDoc[field.name!] !== null &&
148-
fieldShouldBeLocalized({ field, parentIsLocalized: parentIsLocalized! }) &&
153+
shouldLocalizeField &&
149154
locale !== 'all' &&
150155
req.payload.config.localization,
151156
)
@@ -258,7 +263,7 @@ export const promise = async ({
258263
// => Object.entries(siblingDoc[field.name]) will be the value of a single locale, not all locales
259264
// => do not run the hook for each locale
260265
!shouldHoistLocalizedValue &&
261-
fieldShouldBeLocalized({ field, parentIsLocalized: parentIsLocalized! }) &&
266+
shouldLocalizeField &&
262267
typeof siblingDoc[field.name] === 'object'
263268

264269
if (fieldAffectsDataResult) {
@@ -644,44 +649,76 @@ export const promise = async ({
644649

645650
case 'group': {
646651
if (fieldAffectsDataResult) {
647-
let groupDoc = siblingDoc[field.name] as JsonObject
652+
const groupSelect =
653+
typeof select?.[field.name] === 'object'
654+
? (select?.[field.name] as SelectType)
655+
: undefined
648656

649-
if (typeof siblingDoc[field.name] !== 'object') {
650-
groupDoc = {}
657+
if (shouldLocalizeField && !shouldHoistLocalizedValue) {
658+
Object.values(siblingDoc[field.name] || {}).forEach((localizedData) => {
659+
traverseFields({
660+
blockData,
661+
collection,
662+
context,
663+
currentDepth,
664+
depth,
665+
doc,
666+
draft,
667+
fallbackLocale,
668+
fieldPromises,
669+
fields: field.fields,
670+
findMany,
671+
flattenLocales,
672+
global,
673+
locale,
674+
overrideAccess,
675+
parentIndexPath: '',
676+
parentIsLocalized: parentIsLocalized || field.localized,
677+
parentPath: path,
678+
parentSchemaPath: schemaPath,
679+
populate,
680+
populationPromises,
681+
req,
682+
select: groupSelect,
683+
selectMode,
684+
showHiddenFields,
685+
siblingDoc: localizedData || {},
686+
triggerAccessControl,
687+
triggerHooks,
688+
})
689+
})
690+
} else {
691+
traverseFields({
692+
blockData,
693+
collection,
694+
context,
695+
currentDepth,
696+
depth,
697+
doc,
698+
draft,
699+
fallbackLocale,
700+
fieldPromises,
701+
fields: field.fields,
702+
findMany,
703+
flattenLocales,
704+
global,
705+
locale,
706+
overrideAccess,
707+
parentIndexPath: '',
708+
parentIsLocalized: parentIsLocalized || field.localized,
709+
parentPath: path,
710+
parentSchemaPath: schemaPath,
711+
populate,
712+
populationPromises,
713+
req,
714+
select: groupSelect,
715+
selectMode,
716+
showHiddenFields,
717+
siblingDoc: typeof siblingDoc[field.name] !== 'object' ? {} : siblingDoc[field.name],
718+
triggerAccessControl,
719+
triggerHooks,
720+
})
651721
}
652-
653-
const groupSelect = select?.[field.name]
654-
655-
traverseFields({
656-
blockData,
657-
collection,
658-
context,
659-
currentDepth,
660-
depth,
661-
doc,
662-
draft,
663-
fallbackLocale,
664-
fieldPromises,
665-
fields: field.fields,
666-
findMany,
667-
flattenLocales,
668-
global,
669-
locale,
670-
overrideAccess,
671-
parentIndexPath: '',
672-
parentIsLocalized: parentIsLocalized || field.localized,
673-
parentPath: path,
674-
parentSchemaPath: schemaPath,
675-
populate,
676-
populationPromises,
677-
req,
678-
select: typeof groupSelect === 'object' ? groupSelect : undefined,
679-
selectMode,
680-
showHiddenFields,
681-
siblingDoc: groupDoc,
682-
triggerAccessControl,
683-
triggerHooks,
684-
})
685722
} else {
686723
traverseFields({
687724
blockData,
@@ -814,56 +851,113 @@ export const promise = async ({
814851
}
815852

816853
case 'tab': {
817-
let tabDoc = siblingDoc
818-
let tabSelect: SelectType | undefined
854+
const tabDoc = siblingDoc
819855

820856
const isNamedTab = tabHasName(field)
821857

822858
if (isNamedTab) {
823-
tabDoc = siblingDoc[field.name] as JsonObject
824-
825-
if (typeof siblingDoc[field.name] !== 'object') {
826-
tabDoc = {}
827-
}
828-
829-
if (typeof select?.[field.name] === 'object') {
830-
tabSelect = select?.[field.name] as SelectType
859+
const tabSelect: SelectType | undefined =
860+
typeof select?.[field.name] === 'object'
861+
? (select?.[field.name] as SelectType)
862+
: undefined
863+
if (shouldLocalizeField && !shouldHoistLocalizedValue) {
864+
Object.values(siblingDoc[field.name] || {}).forEach((localizedData) => {
865+
traverseFields({
866+
blockData,
867+
collection,
868+
context,
869+
currentDepth,
870+
depth,
871+
doc,
872+
draft,
873+
fallbackLocale,
874+
fieldPromises,
875+
fields: field.fields,
876+
findMany,
877+
flattenLocales,
878+
global,
879+
locale,
880+
overrideAccess,
881+
parentIndexPath: '',
882+
parentIsLocalized: parentIsLocalized || field.localized,
883+
parentPath: path,
884+
parentSchemaPath: schemaPath,
885+
populate,
886+
populationPromises,
887+
req,
888+
select: tabSelect,
889+
selectMode,
890+
showHiddenFields,
891+
siblingDoc: localizedData || {},
892+
triggerAccessControl,
893+
triggerHooks,
894+
})
895+
})
896+
} else {
897+
traverseFields({
898+
blockData,
899+
collection,
900+
context,
901+
currentDepth,
902+
depth,
903+
doc,
904+
draft,
905+
fallbackLocale,
906+
fieldPromises,
907+
fields: field.fields,
908+
findMany,
909+
flattenLocales,
910+
global,
911+
locale,
912+
overrideAccess,
913+
parentIndexPath: '',
914+
parentIsLocalized: parentIsLocalized || field.localized,
915+
parentPath: path,
916+
parentSchemaPath: schemaPath,
917+
populate,
918+
populationPromises,
919+
req,
920+
select: tabSelect,
921+
selectMode,
922+
showHiddenFields,
923+
siblingDoc: typeof siblingDoc[field.name] !== 'object' ? {} : siblingDoc[field.name],
924+
triggerAccessControl,
925+
triggerHooks,
926+
})
831927
}
832928
} else {
833-
tabSelect = select
929+
traverseFields({
930+
blockData,
931+
collection,
932+
context,
933+
currentDepth,
934+
depth,
935+
doc,
936+
draft,
937+
fallbackLocale,
938+
fieldPromises,
939+
fields: field.fields,
940+
findMany,
941+
flattenLocales,
942+
global,
943+
locale,
944+
overrideAccess,
945+
parentIndexPath: isNamedTab ? '' : indexPath,
946+
parentIsLocalized: parentIsLocalized || field.localized,
947+
parentPath: isNamedTab ? path : parentPath,
948+
parentSchemaPath: schemaPath,
949+
populate,
950+
populationPromises,
951+
req,
952+
select,
953+
selectMode,
954+
showHiddenFields,
955+
siblingDoc: tabDoc,
956+
triggerAccessControl,
957+
triggerHooks,
958+
})
834959
}
835960

836-
traverseFields({
837-
blockData,
838-
collection,
839-
context,
840-
currentDepth,
841-
depth,
842-
doc,
843-
draft,
844-
fallbackLocale,
845-
fieldPromises,
846-
fields: field.fields,
847-
findMany,
848-
flattenLocales,
849-
global,
850-
locale,
851-
overrideAccess,
852-
parentIndexPath: isNamedTab ? '' : indexPath,
853-
parentIsLocalized: parentIsLocalized || field.localized,
854-
parentPath: isNamedTab ? path : parentPath,
855-
parentSchemaPath: schemaPath,
856-
populate,
857-
populationPromises,
858-
req,
859-
select: tabSelect,
860-
selectMode,
861-
showHiddenFields,
862-
siblingDoc: tabDoc,
863-
triggerAccessControl,
864-
triggerHooks,
865-
})
866-
867961
break
868962
}
869963

test/localization/collections/AllFields/index.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,36 @@ export const AllFieldsLocalized: CollectionConfig = {
207207
],
208208
},
209209

210+
// Deeply nested: localized tab
211+
{
212+
type: 'tabs',
213+
tabs: [
214+
{
215+
name: 't1',
216+
label: 'Deeply Nested Tab',
217+
localized: true,
218+
fields: [
219+
{
220+
type: 'tabs',
221+
tabs: [
222+
{
223+
name: 't2',
224+
label: 'Nested Tab Level 2',
225+
fields: [
226+
{
227+
name: 'text',
228+
type: 'text',
229+
localized: true,
230+
},
231+
],
232+
},
233+
],
234+
},
235+
],
236+
},
237+
],
238+
},
239+
210240
// Deeply nested: localized group > non-localized group > localized array
211241
{
212242
name: 'g1',

test/localization/int.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3496,6 +3496,11 @@ describe('Localization', () => {
34963496
const doc = await payload.create({
34973497
collection: allFieldsLocalizedSlug,
34983498
data: {
3499+
t1: {
3500+
t2: {
3501+
text: 'EN Deep Text',
3502+
},
3503+
},
34993504
g1: {
35003505
g2: {
35013506
g2a1: [{ text: 'EN Deep 1' }, { text: 'EN Deep 2' }],
@@ -3573,9 +3578,16 @@ describe('Localization', () => {
35733578

35743579
// Verify deeply nested localization has locale keys only at topmost localized field
35753580
expect((allLocalesDoc.g1 as any).en).toBeDefined()
3581+
expect((allLocalesDoc.g1 as any).g2).toBeUndefined()
35763582
expect((allLocalesDoc.g1 as any).en.g2.g2a1).toHaveLength(2)
35773583
expect((allLocalesDoc.g1 as any).en.g2.g2a1[0].text).toBe('EN Deep 1')
35783584
expect((allLocalesDoc.g1 as any).es).toBeUndefined()
3585+
3586+
// Verify deeply nested localization in tab has locale keys only at topmost localized field
3587+
expect((allLocalesDoc.t1 as any).en).toBeDefined()
3588+
expect((allLocalesDoc.t1 as any).t2).toBeUndefined()
3589+
expect((allLocalesDoc.t1 as any).en.t2.text).toBe('EN Deep Text')
3590+
expect((allLocalesDoc.t1 as any).es).toBeUndefined()
35793591
})
35803592
})
35813593
})

0 commit comments

Comments
 (0)