Skip to content

Commit 067e14f

Browse files
jakobpevecpevecj
andauthored
fix: localized array,group,blocks fields duplicate with empty values (#15849)
### What? Localized blocks, array, and group fields are empty when duplicating a selected locale. ### Why? filterDataToSelectedLocales assumed field types (blocks, array, group) always stored data directly as arrays. When these fields are localized, data is wrapped in a locale map ({ en: [...], de: [...] }), which was not being unwrapped before processing. ### How? - Added locale-aware branching for blocks, array, and group field types — when the field is localized, the locale map is filtered first, then each locale's value is recursed into - Extracted a filterLocaleMap helper to consistently filter and transform locale maps across all field types - Added unit tests Fixes #15847 --------- Co-authored-by: pevecj <jakob.pevec@renderspace.si>
1 parent af1a932 commit 067e14f

2 files changed

Lines changed: 348 additions & 35 deletions

File tree

packages/payload/src/utilities/filterDataToSelectedLocales.spec.ts

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ describe('filterDataToSelectedLocales', () => {
111111
configBlockReferences: [
112112
{
113113
slug: 'content',
114+
flattenedFields: [],
114115
fields: [
115116
{
116117
name: 'body',
@@ -140,6 +141,245 @@ describe('filterDataToSelectedLocales', () => {
140141
})
141142
})
142143

144+
describe('localized arrays', () => {
145+
it('should filter localized array to selected locales and recurse into rows', () => {
146+
const fields: Field[] = [
147+
{
148+
name: 'items',
149+
type: 'array',
150+
localized: true,
151+
fields: [
152+
{
153+
name: 'title',
154+
type: 'text',
155+
},
156+
],
157+
},
158+
]
159+
160+
const docWithLocales = {
161+
items: {
162+
en: [{ title: 'English Item' }],
163+
es: [{ title: 'Spanish Item' }],
164+
de: [{ title: 'German Item' }],
165+
},
166+
}
167+
168+
const result = filterDataToSelectedLocales({
169+
configBlockReferences: [],
170+
docWithLocales,
171+
fields,
172+
selectedLocales,
173+
})
174+
175+
expect(result.items).toEqual({
176+
en: [{ title: 'English Item' }],
177+
})
178+
})
179+
180+
it('should handle non-localized array with localized children', () => {
181+
const fields: Field[] = [
182+
{
183+
name: 'items',
184+
type: 'array',
185+
fields: [
186+
{
187+
name: 'label',
188+
type: 'text',
189+
localized: true,
190+
},
191+
{
192+
name: 'value',
193+
type: 'text',
194+
},
195+
],
196+
},
197+
]
198+
199+
const docWithLocales = {
200+
items: [
201+
{ label: { en: 'English', es: 'Spanish' }, value: 'one' },
202+
{ label: { en: 'English 2', es: 'Spanish 2' }, value: 'two' },
203+
],
204+
}
205+
206+
const result = filterDataToSelectedLocales({
207+
configBlockReferences: [],
208+
docWithLocales,
209+
fields,
210+
selectedLocales,
211+
})
212+
213+
expect(result.items).toEqual([
214+
{ label: { en: 'English' }, value: 'one' },
215+
{ label: { en: 'English 2' }, value: 'two' },
216+
])
217+
})
218+
})
219+
220+
describe('localized blocks', () => {
221+
it('should filter localized blocks field to selected locales', () => {
222+
const fields: Field[] = [
223+
{
224+
name: 'layout',
225+
type: 'blocks',
226+
localized: true,
227+
blocks: [
228+
{
229+
slug: 'hero',
230+
fields: [
231+
{
232+
name: 'heading',
233+
type: 'text',
234+
},
235+
],
236+
},
237+
],
238+
},
239+
]
240+
241+
const docWithLocales = {
242+
layout: {
243+
en: [{ blockType: 'hero', id: '1', blockName: 'Hero EN', heading: 'Hello' }],
244+
es: [{ blockType: 'hero', id: '2', blockName: 'Hero ES', heading: 'Hola' }],
245+
de: [{ blockType: 'hero', id: '3', blockName: 'Hero DE', heading: 'Hallo' }],
246+
},
247+
}
248+
249+
const result = filterDataToSelectedLocales({
250+
configBlockReferences: [],
251+
docWithLocales,
252+
fields,
253+
selectedLocales,
254+
})
255+
256+
expect(result.layout).toEqual({
257+
en: [{ blockType: 'hero', id: '1', blockName: 'Hero EN', heading: 'Hello' }],
258+
})
259+
})
260+
261+
it('should filter localized blocks with multiple selected locales', () => {
262+
const fields: Field[] = [
263+
{
264+
name: 'layout',
265+
type: 'blocks',
266+
localized: true,
267+
blocks: [
268+
{
269+
slug: 'text',
270+
fields: [
271+
{
272+
name: 'body',
273+
type: 'text',
274+
},
275+
],
276+
},
277+
],
278+
},
279+
]
280+
281+
const docWithLocales = {
282+
layout: {
283+
en: [{ blockType: 'text', id: '1', blockName: 'Text EN', body: 'English' }],
284+
es: [{ blockType: 'text', id: '2', blockName: 'Text ES', body: 'Spanish' }],
285+
de: [{ blockType: 'text', id: '3', blockName: 'Text DE', body: 'German' }],
286+
},
287+
}
288+
289+
const result = filterDataToSelectedLocales({
290+
configBlockReferences: [],
291+
docWithLocales,
292+
fields,
293+
selectedLocales: ['en', 'es'],
294+
})
295+
296+
expect(result.layout).toEqual({
297+
en: [{ blockType: 'text', id: '1', blockName: 'Text EN', body: 'English' }],
298+
es: [{ blockType: 'text', id: '2', blockName: 'Text ES', body: 'Spanish' }],
299+
})
300+
})
301+
})
302+
303+
describe('localized groups', () => {
304+
it('should filter localized group to selected locales and recurse into children', () => {
305+
const fields: Field[] = [
306+
{
307+
name: 'meta',
308+
type: 'group',
309+
localized: true,
310+
fields: [
311+
{
312+
name: 'title',
313+
type: 'text',
314+
},
315+
{
316+
name: 'description',
317+
type: 'text',
318+
},
319+
],
320+
},
321+
]
322+
323+
const docWithLocales = {
324+
meta: {
325+
en: { title: 'English Title', description: 'English Desc' },
326+
es: { title: 'Spanish Title', description: 'Spanish Desc' },
327+
de: { title: 'German Title', description: 'German Desc' },
328+
},
329+
}
330+
331+
const result = filterDataToSelectedLocales({
332+
configBlockReferences: [],
333+
docWithLocales,
334+
fields,
335+
selectedLocales,
336+
})
337+
338+
expect(result.meta).toEqual({
339+
en: { title: 'English Title', description: 'English Desc' },
340+
})
341+
})
342+
343+
it('should handle non-localized group with localized children', () => {
344+
const fields: Field[] = [
345+
{
346+
name: 'seo',
347+
type: 'group',
348+
fields: [
349+
{
350+
name: 'title',
351+
type: 'text',
352+
localized: true,
353+
},
354+
{
355+
name: 'slug',
356+
type: 'text',
357+
},
358+
],
359+
},
360+
]
361+
362+
const docWithLocales = {
363+
seo: {
364+
title: { en: 'English SEO', es: 'Spanish SEO' },
365+
slug: 'my-page',
366+
},
367+
}
368+
369+
const result = filterDataToSelectedLocales({
370+
configBlockReferences: [],
371+
docWithLocales,
372+
fields,
373+
selectedLocales,
374+
})
375+
376+
expect(result.seo).toEqual({
377+
title: { en: 'English SEO' },
378+
slug: 'my-page',
379+
})
380+
})
381+
})
382+
143383
describe('simple fields', () => {
144384
it('should filter localized field values to selected locales', () => {
145385
const fields: Field[] = [

0 commit comments

Comments
 (0)