Skip to content

Commit

Permalink
feat(*): support blocks field type (#47)
Browse files Browse the repository at this point in the history
* feat(*): support blocks field type

* docs(reamdme): update
  • Loading branch information
thompsonsj authored Jul 2, 2023
1 parent b681bef commit 6a8d4a9
Show file tree
Hide file tree
Showing 7 changed files with 415 additions and 178 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# WIP 8 June 2023
# WIP 2 July 2023

This plugin is currently being refactored. Although the logic works, it was extracted from a Payload CMS install and needs work to decouple logic from that installation.

Expand All @@ -11,15 +11,15 @@ Features:
- [x] `group`
- [x] `array`
- [x] `collapsible`
- [ ] `blocks`
- [x] `blocks`
- [x] Improve testing so that it does not require a local server. See https://github.com/thompsonsj/payload-crowdin-sync/issues/40.
- [ ] Support **updating localized fields from CrowdIn** for the following Payload fields. Note that this will require effective required field detection to avoid update errors. See `getLocalizedRequiredFields`.
- [ ] `group`
- [ ] `array`
- [ ] `collapsible`
- [ ] `blocks`
- [ ] Add UI for syncing translations (currently done with URLs added to `server.ts` on the Payload installation).
- [ ] Add option to make localized fields read-only in other locales (CrowdIn mangaes these fields).
- [ ] Add option to make localized fields read-only in other locales (CrowdIn manages these fields).

# Payload CrowdIn Sync Plugin

Expand Down
2 changes: 1 addition & 1 deletion dev/src/collections/NestedFieldCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Block, CollectionConfig } from 'payload/types';
import { basicLocalizedFields } from './fields/basicLocalizedFields';

const LocalizedBlock: Block = {
slug: 'Block', // required
slug: 'basicBlock', // required
fields: basicLocalizedFields,
};

Expand Down
43 changes: 43 additions & 0 deletions dev/src/tests/files.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,47 @@ describe(`Collection: ${collections.nestedFields}`, () => {
expect(crowdInFiles.find((file) => file.name === 'arrayField[1].content.html')).toBeDefined()
expect(crowdInFiles.find((file) => file.name === 'fields.json')).toBeDefined()
})

it('creates files containing `blocks` fieldType content', async () => {
const article = await payload.create({
collection: collections.nestedFields,
data: {
layout: [
{
title: 'Test title 1',
content: [
{
children: [
{
text: "Test content 1"
}
]
}
],
metaDescription: 'Test meta description 1',
blockType: 'basicBlock'
},
{
title: 'Test title 2',
content: [
{
children: [
{
text: "Test content 2"
}
]
}
],
metaDescription: 'Test meta description 2',
blockType: 'basicBlock'
},
],
},
});
const crowdInFiles = await getFilesByDocumentID(article.id, payload)
expect(crowdInFiles.length).toEqual(3)
expect(crowdInFiles.find((file) => file.name === 'layout[0].content.html')).toBeDefined()
expect(crowdInFiles.find((file) => file.name === 'layout[1].content.html')).toBeDefined()
expect(crowdInFiles.find((file) => file.name === 'fields.json')).toBeDefined()
})
});
57 changes: 43 additions & 14 deletions src/utilities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const localizedFieldTypes = [
const nestedFieldTypes = [
'array',
'group',
// 'blocks',
'blocks',
]

export const containsNestedFields = (field: Field) => nestedFieldTypes.includes(field.type)
Expand Down Expand Up @@ -58,12 +58,16 @@ export const getLocalizedFieldsRecursive = ({
}
return type ? fieldCrowdinFileType(field as FieldWithName) === type : true
})
// exclude group and array fields with no localized fields
// exclude group, array and block fields with no localized fields
// TODO: find a better way to do this - block, array and group logic is duplicated, and this filter needs to be compatible with field extraction logic later in this function
.filter(field => {
const localizedParent = hasLocalizedProp(field)
if (field.type === 'group' || field.type === 'array') {
const localizedParent = hasLocalizedProp(field)
return containsLocalizedFields({ fields: field.fields, type, localizedParent })
}
if (field.type === 'blocks') {
return field.blocks.find(block => containsLocalizedFields({ fields: block.fields, type, localizedParent }))
}
return true
})
// recursion for group, array and blocks field
Expand All @@ -80,18 +84,21 @@ export const getLocalizedFieldsRecursive = ({
}
}
if (field.type === 'blocks') {
const blocks = field.blocks.map((block: Block) => {
if (containsLocalizedFields({ fields: block.fields, type, localizedParent })) {
return {
slug: block.slug,
fields: getLocalizedFields({
fields: block.fields,
type,
localizedParent,
})
}
}
}).filter(block => block)
return {
...field,
blocks:
field.blocks.map((block: Block) => {
return {
fields: getLocalizedFields({
fields: block.fields,
type,
localizedParent,
})
}
})
blocks,
}
}
return field
Expand Down Expand Up @@ -147,7 +154,9 @@ export const containsLocalizedFields = ({
fields: Field[],
type?: 'json' | 'html',
localizedParent?: boolean,
}): boolean => !isEmpty(getLocalizedFields({ fields, type, localizedParent }))
}): boolean => {
return !isEmpty(getLocalizedFields({ fields, type, localizedParent }))
}

export const fieldChanged = (previousValue: string | object | undefined, value: string | object | undefined, type: string) => {
if (type === 'richText') {
Expand Down Expand Up @@ -190,6 +199,12 @@ export const buildCrowdinJsonObject = ({
fields: field.fields,
topLevel: false,
}))
} else if (field.type === 'blocks') {
response[field.name] = doc[field.name].map((item: any) => buildCrowdinJsonObject({
doc: item,
fields: field.blocks.find((block: Block) => block.slug === item.blockType)?.fields || [],
topLevel: false,
})).filter((item: any) => !isEmpty(item))
} else {
/**
* Kept the following comments from when the plugin
Expand Down Expand Up @@ -270,6 +285,20 @@ export const buildCrowdinHtmlObject = ({
...response,
...merge({}, ...arrayValues)
}
} else if (field.type === 'blocks') {
const arrayValues = doc[field.name].map((item: any, index: number) => {
const subPrefix = `${[prefix, `${field.name}[${index}]`].filter(string => string).join('.')}`
return buildCrowdinHtmlObject({
doc: item,
fields: field.blocks.find((block: Block) => block.slug === item.blockType)?.fields || [],
prefix: subPrefix,
topLevel: false,
})
})
response = {
...response,
...merge({}, ...arrayValues)
}
} else {
if (doc[field.name]?.en) {
response[name] = doc[field.name].en
Expand Down
165 changes: 165 additions & 0 deletions src/utilities/tests/buildJsonCrowdinObject/array-field-type.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { CollectionConfig, Field } from "payload/types"
import { buildCrowdinJsonObject, getLocalizedFields } from "../.."
import { FieldWithName } from "../../../types"

describe("fn: buildCrowdinJsonObject: array field type", () => {
it ("includes localized fields nested in an array", () => {
const doc = {
id: '638641358b1a140462752076',
title: 'Test Policy created with title',
arrayField: [
{
title: "Array field title content one",
text: "Array field text content one",
select: "two",
id: "64735620230d57bce946d370"
},
{
title: "Array field title content two",
text: "Array field text content two",
select: "two",
id: "64735621230d57bce946d371"
}
],
status: 'draft',
createdAt: '2022-11-29T17:28:21.644Z',
updatedAt: '2022-11-29T17:28:21.644Z'
}
const fields: FieldWithName[] = [
{
name: 'title',
type: 'text',
localized: true,
},
// select not supported yet
{
name: 'select',
type: 'select',
localized: true,
options: [
'one',
'two'
]
},
{
name: 'arrayField',
type: 'array',
fields: [
{
name: 'title',
type: 'text',
localized: true,
},
{
name: 'text',
type: 'text',
localized: true,
},
{
name: 'select',
type: 'select',
localized: true,
options: [
'one',
'two'
]
},
]
},
]
const localizedFields = getLocalizedFields({ fields })
const expected = {
title: 'Test Policy created with title',
arrayField: [
{
title: "Array field title content one",
text: "Array field text content one",
},
{
title: "Array field title content two",
text: "Array field text content two",
}
],
}
expect(buildCrowdinJsonObject({doc, fields: localizedFields})).toEqual(expected)
})

it ("includes localized fields nested in an array with a localization setting on the array field", () => {
const doc = {
id: '638641358b1a140462752076',
title: 'Test Policy created with title',
arrayField: [
{
title: "Array field title content one",
text: "Array field text content one",
select: "two",
id: "64735620230d57bce946d370"
},
{
title: "Array field title content two",
text: "Array field text content two",
select: "two",
id: "64735621230d57bce946d371"
}
],
status: 'draft',
createdAt: '2022-11-29T17:28:21.644Z',
updatedAt: '2022-11-29T17:28:21.644Z'
}
const fields: FieldWithName[] = [
{
name: 'title',
type: 'text',
localized: true,
},
// select not supported yet
{
name: 'select',
type: 'select',
localized: true,
options: [
'one',
'two'
]
},
{
name: 'arrayField',
type: 'array',
localized: true,
fields: [
{
name: 'title',
type: 'text',
},
{
name: 'text',
type: 'text',
},
{
name: 'select',
type: 'select',
options: [
'one',
'two'
]
},
]
},
]
const localizedFields = getLocalizedFields({ fields })
const expected = {
title: 'Test Policy created with title',
arrayField: [
{
title: "Array field title content one",
text: "Array field text content one",
},
{
title: "Array field title content two",
text: "Array field text content two",
}
],
}
expect(buildCrowdinJsonObject({doc, fields: localizedFields})).toEqual(expected)
})
})
Loading

0 comments on commit 6a8d4a9

Please sign in to comment.