diff --git a/packages/netlify-cms-core/src/reducers/__tests__/collections.spec.js b/packages/netlify-cms-core/src/reducers/__tests__/collections.spec.js index 2b861649..0d47d66a 100644 --- a/packages/netlify-cms-core/src/reducers/__tests__/collections.spec.js +++ b/packages/netlify-cms-core/src/reducers/__tests__/collections.spec.js @@ -120,6 +120,15 @@ describe('collections', () => { }, ], }, + { + name: 'list_3', + types: [ + { + name: 'list_3_type', + media_folder: 'list_3_type_media_folder', + }, + ], + }, ], }), ), @@ -134,6 +143,10 @@ describe('collections', () => { name: 'list_2_item', media_folder: 'list_2_item_media_folder', }), + fromJS({ + name: 'list_3_type', + media_folder: 'list_3_type_media_folder', + }), ]); }); @@ -182,6 +195,15 @@ describe('collections', () => { name: 'list_2_item', media_folder: 'list_2_item_media_folder', }, + { + name: 'list_3', + types: [ + { + name: 'list_3_type', + media_folder: 'list_3_type_media_folder', + }, + ], + }, ], }, ], @@ -195,6 +217,10 @@ describe('collections', () => { name: 'list_2_item', media_folder: 'list_2_item_media_folder', }), + fromJS({ + name: 'list_3_type', + media_folder: 'list_3_type_media_folder', + }), ]); }); }); @@ -219,11 +245,19 @@ describe('collections', () => { name: 'image', media_folder: '{{media_folder}}/customers/', }, + { + name: 'list', + types: [{ name: 'widget', media_folder: '{{media_folder}}/widgets' }], + }, ], }), fromJS({ slug: 'name', path: 'src/post/post1.md', data: {} }), ), - ).toEqual(['static/img/general', 'static/img/general/customers']); + ).toEqual([ + 'static/img/general', + 'static/img/general/customers', + 'static/img/general/widgets', + ]); }); it('should return fields, file and collection folders', () => { @@ -242,6 +276,10 @@ describe('collections', () => { name: 'image', media_folder: '{{media_folder}}/logos/', }, + { + name: 'list', + types: [{ name: 'widget', media_folder: '{{media_folder}}/widgets' }], + }, ], }, ], @@ -252,6 +290,7 @@ describe('collections', () => { 'static/img/general', 'static/img/general/customers', 'static/img/general/customers/logos', + 'static/img/general/customers/widgets', ]); }); }); @@ -270,18 +309,25 @@ describe('collections', () => { { name: 'en', fields: [{ name: 'title' }, { name: 'body' }] }, { name: 'es', fields: [{ name: 'title' }, { name: 'body' }] }, { name: 'it', field: { name: 'title', fields: [{ name: 'subTitle' }] } }, + { + name: 'fr', + fields: [{ name: 'title', widget: 'list', types: [{ name: 'variableType' }] }], + }, ], }); expect(getFieldsNames(collection.get('fields').toArray())).toEqual([ 'en', 'es', 'it', + 'fr', 'en.title', 'en.body', 'es.title', 'es.body', 'it.title', 'it.title.subTitle', + 'fr.title', + 'fr.title.variableType', ]); }); }); @@ -300,6 +346,10 @@ describe('collections', () => { { name: 'en', fields: [{ name: 'title' }, { name: 'body' }] }, { name: 'es', fields: [{ name: 'title' }, { name: 'body' }] }, { name: 'it', field: { name: 'title', fields: [{ name: 'subTitle' }] } }, + { + name: 'fr', + fields: [{ name: 'title', widget: 'list', types: [{ name: 'variableType' }] }], + }, ], }); @@ -319,6 +369,16 @@ describe('collections', () => { .get('fields') .get(0), ); + + expect(selectField(collection, 'fr.title.variableType')).toBe( + collection + .get('fields') + .get(3) + .get('fields') + .get(0) + .get('types') + .get(0), + ); }); }); }); diff --git a/packages/netlify-cms-core/src/reducers/__tests__/entries.spec.js b/packages/netlify-cms-core/src/reducers/__tests__/entries.spec.js index bb02dca6..9c82269a 100644 --- a/packages/netlify-cms-core/src/reducers/__tests__/entries.spec.js +++ b/packages/netlify-cms-core/src/reducers/__tests__/entries.spec.js @@ -289,7 +289,12 @@ describe('entries', () => { it('should cascade media_folders', () => { const mainImageField = fromJS({ name: 'main_image' }); const logoField = fromJS({ name: 'logo', media_folder: '{{media_folder}}/logos/' }); - const nestedField2 = fromJS({ name: 'nested', media_folder: '{{media_folder}}/nested2/' }); + const nestedField3 = fromJS({ name: 'nested', media_folder: '{{media_folder}}/nested3/' }); + const nestedField2 = fromJS({ + name: 'nested', + media_folder: '{{media_folder}}/nested2/', + types: [nestedField3], + }); const nestedField1 = fromJS({ name: 'nested', media_folder: '{{media_folder}}/nested1/', @@ -324,6 +329,9 @@ describe('entries', () => { expect(selectMediaFolder(...args, nestedField2)).toBe( 'static/img/general/customers/nested/nested1/nested2', ); + expect(selectMediaFolder(...args, nestedField3)).toBe( + 'static/img/general/customers/nested/nested1/nested2/nested3', + ); }); }); diff --git a/packages/netlify-cms-core/src/reducers/collections.ts b/packages/netlify-cms-core/src/reducers/collections.ts index ea8e0220..0ac43f32 100644 --- a/packages/netlify-cms-core/src/reducers/collections.ts +++ b/packages/netlify-cms-core/src/reducers/collections.ts @@ -124,10 +124,12 @@ const getFieldsWithMediaFolders = (fields: EntryField[]) => { if (f.has('fields')) { const fields = f.get('fields')?.toArray() as EntryField[]; acc = [...acc, ...getFieldsWithMediaFolders(fields)]; - } - if (f.has('field')) { + } else if (f.has('field')) { const field = f.get('field') as EntryField; acc = [...acc, ...getFieldsWithMediaFolders([field])]; + } else if (f.has('types')) { + const types = f.get('types')?.toArray() as EntryField[]; + acc = [...acc, ...getFieldsWithMediaFolders(types)]; } return acc; @@ -200,10 +202,12 @@ export const getFieldsNames = (fields: EntryField[], prefix = '') => { if (f.has('fields')) { const fields = f.get('fields')?.toArray() as EntryField[]; names = [...names, ...getFieldsNames(fields, `${names[index]}.`)]; - } - if (f.has('field')) { + } else if (f.has('field')) { const field = f.get('field') as EntryField; names = [...names, ...getFieldsNames([field], `${names[index]}.`)]; + } else if (f.has('types')) { + const types = f.get('types')?.toArray() as EntryField[]; + names = [...names, ...getFieldsNames(types, `${names[index]}.`)]; } }); @@ -219,9 +223,10 @@ export const selectField = (collection: Collection, key: string) => { field = fields.find(f => f.get('name') === name); if (field?.has('fields')) { fields = field?.get('fields')?.toArray() as EntryField[]; - } - if (field?.has('field')) { + } else if (field?.has('field')) { fields = [field?.get('field') as EntryField]; + } else if (field?.has('types')) { + fields = field?.get('types')?.toArray() as EntryField[]; } } diff --git a/packages/netlify-cms-core/src/reducers/entries.ts b/packages/netlify-cms-core/src/reducers/entries.ts index 2d415e75..d5b90ece 100644 --- a/packages/netlify-cms-core/src/reducers/entries.ts +++ b/packages/netlify-cms-core/src/reducers/entries.ts @@ -208,8 +208,9 @@ const traverseFields = ( folderKey, config.get('slug'), ); + let fieldFolder = null; if (f.has('fields')) { - return traverseFields( + fieldFolder = traverseFields( folderKey, config, collection, @@ -219,7 +220,7 @@ const traverseFields = ( folder, ); } else if (f.has('field')) { - return traverseFields( + fieldFolder = traverseFields( folderKey, config, collection, @@ -228,6 +229,19 @@ const traverseFields = ( [f.get('field')!], folder, ); + } else if (f.has('types')) { + fieldFolder = traverseFields( + folderKey, + config, + collection, + entryMap, + field, + f.get('types')!.toArray(), + folder, + ); + } + if (fieldFolder != null) { + return fieldFolder; } } diff --git a/packages/netlify-cms-core/src/types/redux.ts b/packages/netlify-cms-core/src/types/redux.ts index 10b60599..7bbf6da1 100644 --- a/packages/netlify-cms-core/src/types/redux.ts +++ b/packages/netlify-cms-core/src/types/redux.ts @@ -92,6 +92,7 @@ export type EntryDraft = StaticallyTypedRecord<{ export type EntryField = StaticallyTypedRecord<{ field?: EntryField; fields?: List; + types?: List; widget: string; name: string; default: string | null;