diff --git a/demo/collections/AutoLabel.ts b/demo/collections/AutoLabel.ts index 23ad49fae5..cac61262f3 100644 --- a/demo/collections/AutoLabel.ts +++ b/demo/collections/AutoLabel.ts @@ -1,5 +1,4 @@ import { PayloadCollectionConfig } from '../../src/collections/config/types'; -import { relationship } from '../../src/fields/config/schema'; const AutoLabel: PayloadCollectionConfig = { slug: 'auto-label', @@ -54,6 +53,38 @@ const AutoLabel: PayloadCollectionConfig = { }, ], }, + { + name: 'noLabelBlock', + type: 'blocks', + label: false, + minRows: 1, + maxRows: 20, + blocks: [ + { + slug: 'number', + // labels: { + // singular: 'Number', + // plural: 'Numbers', + // }, + fields: [ + { + name: 'testNumber', + type: 'number', + }, + ], + }, + ], + }, + { + name: 'items', + type: 'array', + fields: [ + { + name: 'itemName', + type: 'text', + }, + ], + }, { name: 'noLabelArray', type: 'array', diff --git a/src/admin/components/forms/field-types/Blocks/Blocks.tsx b/src/admin/components/forms/field-types/Blocks/Blocks.tsx index 7d8ce05128..9c5db11711 100644 --- a/src/admin/components/forms/field-types/Blocks/Blocks.tsx +++ b/src/admin/components/forms/field-types/Blocks/Blocks.tsx @@ -241,9 +241,7 @@ const RenderBlocks = React.memo((props: RenderBlockProps) => { This field requires at least {' '} - {minRows - ? `${minRows} ${labels.plural}` - : `1 ${labels.singular}`} + {`${minRows} ${minRows === 1 ? labels.singular : labels.plural}`} )} {(rows.length === 0 && readOnly) && ( diff --git a/src/fields/config/sanitize.spec.ts b/src/fields/config/sanitize.spec.ts index 8208be2066..1249717baa 100644 --- a/src/fields/config/sanitize.spec.ts +++ b/src/fields/config/sanitize.spec.ts @@ -12,39 +12,124 @@ describe('sanitizeFields', () => { sanitizeFields(fields, []); }).toThrow(MissingFieldType); }); - it('should populate label if missing', () => { - const fields = [{ - name: 'someCollection', - type: 'text', - }]; - const sanitizedField = sanitizeFields(fields, [])[0]; - expect(sanitizedField.name).toStrictEqual('someCollection'); - expect(sanitizedField.label).toStrictEqual('Some Collection'); - expect(sanitizedField.type).toStrictEqual('text'); - }); - it('should allow auto-label override', () => { - const fields = [{ - name: 'someCollection', - type: 'text', - label: 'Do not label', - }]; - const sanitizedField = sanitizeFields(fields, [])[0]; - expect(sanitizedField.name).toStrictEqual('someCollection'); - expect(sanitizedField.label).toStrictEqual('Do not label'); - expect(sanitizedField.type).toStrictEqual('text'); - }); - it('should allow label opt-out', () => { - const fields = [{ - name: 'someCollection', - type: 'text', - label: false, - }]; - const sanitizedField = sanitizeFields(fields, [])[0]; - expect(sanitizedField.name).toStrictEqual('someCollection'); - expect(sanitizedField.label).toStrictEqual(false); - expect(sanitizedField.type).toStrictEqual('text'); - }); + describe('auto-labeling', () => { + it('should populate label if missing', () => { + const fields = [{ + name: 'someField', + type: 'text', + }]; + const sanitizedField = sanitizeFields(fields, [])[0]; + expect(sanitizedField.name).toStrictEqual('someField'); + expect(sanitizedField.label).toStrictEqual('Some Field'); + expect(sanitizedField.type).toStrictEqual('text'); + }); + it('should allow auto-label override', () => { + const fields = [{ + name: 'someField', + type: 'text', + label: 'Do not label', + }]; + const sanitizedField = sanitizeFields(fields, [])[0]; + expect(sanitizedField.name).toStrictEqual('someField'); + expect(sanitizedField.label).toStrictEqual('Do not label'); + expect(sanitizedField.type).toStrictEqual('text'); + }); + + describe('opt-out', () => { + it('should allow label opt-out', () => { + const fields = [{ + name: 'someField', + type: 'text', + label: false, + }]; + const sanitizedField = sanitizeFields(fields, [])[0]; + expect(sanitizedField.name).toStrictEqual('someField'); + expect(sanitizedField.label).toStrictEqual(false); + expect(sanitizedField.type).toStrictEqual('text'); + }); + + it('should allow label opt-out for arrays', () => { + const fields = [{ + name: 'items', + type: 'array', + label: false, + fields: [ + { + name: 'itemName', + type: 'text', + }, + ], + }]; + const sanitizedField = sanitizeFields(fields, [])[0]; + expect(sanitizedField.name).toStrictEqual('items'); + expect(sanitizedField.label).toStrictEqual(false); + expect(sanitizedField.type).toStrictEqual('array'); + expect(sanitizedField.labels).toBeUndefined(); + }); + it('should allow label opt-out for blocks', () => { + const fields = [{ + name: 'noLabelBlock', + type: 'blocks', + label: false, + blocks: [ + { + slug: 'number', + fields: [ + { + name: 'testNumber', + type: 'number', + }, + ], + }, + ], + }]; + const sanitizedField = sanitizeFields(fields, [])[0]; + expect(sanitizedField.name).toStrictEqual('noLabelBlock'); + expect(sanitizedField.label).toStrictEqual(false); + expect(sanitizedField.type).toStrictEqual('blocks'); + expect(sanitizedField.labels).toBeUndefined(); + }); + }); + + + it('should label arrays with plural and singular', () => { + const fields = [{ + name: 'items', + type: 'array', + fields: [ + { + name: 'itemName', + type: 'text', + }, + ], + }]; + const sanitizedField = sanitizeFields(fields, [])[0]; + expect(sanitizedField.name).toStrictEqual('items'); + expect(sanitizedField.label).toStrictEqual('Items'); + expect(sanitizedField.type).toStrictEqual('array'); + expect(sanitizedField.labels).toMatchObject({ singular: 'Item', plural: 'Items' }); + }); + + it('should label blocks with plural and singular', () => { + const fields = [{ + name: 'specialBlock', + type: 'blocks', + blocks: [ + { + slug: 'number', + fields: [{ name: 'testNumber', type: 'number' }], + }, + ], + }]; + const sanitizedField = sanitizeFields(fields, [])[0]; + expect(sanitizedField.name).toStrictEqual('specialBlock'); + expect(sanitizedField.label).toStrictEqual('Special Block'); + expect(sanitizedField.type).toStrictEqual('blocks'); + expect(sanitizedField.labels).toMatchObject({ singular: 'Special Block', plural: 'Special Blocks' }); + expect(sanitizedField.blocks[0].fields[0].label).toStrictEqual('Test Number'); + }); + }); describe('relationships', () => { it('should not throw on valid relationship', () => { diff --git a/src/fields/config/sanitize.ts b/src/fields/config/sanitize.ts index 73ac21e9ef..94e63550dd 100644 --- a/src/fields/config/sanitize.ts +++ b/src/fields/config/sanitize.ts @@ -24,7 +24,7 @@ const sanitizeFields = (fields, validRelationships: string[]) => { }); } - if (field.type === 'blocks') { + if ((field.type === 'blocks' || field.type === 'array') && field.label !== false) { field.labels = field.labels || formatLabels(field.name); } diff --git a/src/fields/config/schema.ts b/src/fields/config/schema.ts index f3c41e815f..3b225926e4 100644 --- a/src/fields/config/schema.ts +++ b/src/fields/config/schema.ts @@ -53,6 +53,10 @@ export const text = baseField.keys({ defaultValue: joi.string(), minLength: joi.number(), maxLength: joi.number(), + admin: baseAdminFields.keys({ + placeholder: joi.string(), + autoComplete: joi.string(), + }), }); export const number = baseField.keys({ @@ -62,6 +66,8 @@ export const number = baseField.keys({ min: joi.number(), max: joi.number(), admin: baseAdminFields.keys({ + placeholder: joi.string(), + autoComplete: joi.string(), step: joi.number(), }), }); @@ -72,6 +78,10 @@ export const textarea = baseField.keys({ defaultValue: joi.string(), minLength: joi.number(), maxLength: joi.number(), + admin: baseAdminFields.keys({ + placeholder: joi.string(), + rows: joi.number(), + }), }); export const email = baseField.keys({ @@ -80,6 +90,10 @@ export const email = baseField.keys({ defaultValue: joi.string(), minLength: joi.number(), maxLength: joi.number(), + admin: baseAdminFields.keys({ + placeholder: joi.string(), + autoComplete: joi.string(), + }), }); export const code = baseField.keys({ @@ -181,7 +195,6 @@ export const blocks = baseField.keys({ singular: joi.string(), plural: joi.string(), }), - label: joi.string(), blocks: joi.array().items( joi.object({ slug: joi.string().required(), @@ -202,6 +215,7 @@ export const richText = baseField.keys({ name: joi.string().required(), defaultValue: joi.array().items(joi.object()), admin: baseAdminFields.keys({ + placeholder: joi.string(), elements: joi.array().items( joi.alternatives().try( joi.string(), @@ -232,6 +246,7 @@ export const date = baseField.keys({ name: joi.string().required(), defaultValue: joi.string(), admin: baseAdminFields.keys({ + placeholder: joi.string(), date: joi.object({ displayFormat: joi.string(), pickerAppearance: joi.string(),