From e8f05165eb3a28c00deb11931db01ad1f8c75c74 Mon Sep 17 00:00:00 2001 From: Dan Ribbens Date: Sat, 5 Aug 2023 13:48:56 -0400 Subject: [PATCH] feat: default tab labels from name (#3129) --- docs/fields/tabs.mdx | 14 +++++++------- src/fields/config/sanitize.ts | 7 +++++-- src/fields/config/schema.ts | 5 +++-- test/fields/collections/Tabs/index.ts | 2 -- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/fields/tabs.mdx b/docs/fields/tabs.mdx index cfa766d91..33fc66f45 100644 --- a/docs/fields/tabs.mdx +++ b/docs/fields/tabs.mdx @@ -25,14 +25,14 @@ _Tabs field type used to separate Hero fields from Page Layout_ #### Tab-specific Config -Each tab has its own required `label` and `fields` array. You can also optionally pass a `description` to render within each individual tab. +Each tab must have either a `name` or `label` and the required `fields` array. You can also optionally pass a `description` to render within each individual tab. -| Option | Description | -| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| **`name`** | An optional property name to be used when stored and retrieved from the database. [More](/docs/fields/overview#field-names) | -| **`label`** \* | The label to render on the tab itself. | -| **`fields`** \* | The fields to render within this tab. | -| **`description`** | Optionally render a description within this tab to describe the contents of the tab itself. | +| Option | Description | +| ---------------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **`name`** | Groups field data into an object when stored and retrieved from the database. [More](/docs/fields/overview#field-names) | +| **`label`** | The label to render on the tab itself. Required when name is undefined, defaults to name converted to words. | +| **`fields`** \* | The fields to render within this tab. | +| **`description`** | Optionally render a description within this tab to describe the contents of the tab itself. | | **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). (`name` must be present) | _\* An asterisk denotes that a property is required._ diff --git a/src/fields/config/sanitize.ts b/src/fields/config/sanitize.ts index 56a23de91..01021a0f1 100644 --- a/src/fields/config/sanitize.ts +++ b/src/fields/config/sanitize.ts @@ -1,9 +1,9 @@ import { formatLabels, toWords } from '../../utilities/formatLabels'; -import { MissingFieldType, InvalidFieldRelationship, InvalidFieldName } from '../../errors'; +import { InvalidFieldName, InvalidFieldRelationship, MissingFieldType } from '../../errors'; import { baseBlockFields } from '../baseFields/baseBlockFields'; import validations from '../validations'; import { baseIDField } from '../baseFields/baseIDField'; -import { Field, fieldAffectsData } from './types'; +import { Field, fieldAffectsData, tabHasName } from './types'; import withCondition from '../../admin/components/forms/withCondition'; const sanitizeFields = (fields: Field[], validRelationships: string[]): Field[] => { @@ -87,6 +87,9 @@ const sanitizeFields = (fields: Field[], validRelationships: string[]): Field[] if (field.type === 'tabs') { field.tabs = field.tabs.map((tab) => { const unsanitizedTab = { ...tab }; + if (tabHasName(tab) && typeof tab.label === 'undefined') { + unsanitizedTab.label = toWords(tab.name); + } unsanitizedTab.fields = sanitizeFields(tab.fields, validRelationships); return unsanitizedTab; }); diff --git a/src/fields/config/schema.ts b/src/fields/config/schema.ts index c1125aa6f..a0135aff3 100644 --- a/src/fields/config/schema.ts +++ b/src/fields/config/schema.ts @@ -224,13 +224,14 @@ export const collapsible = baseField.keys({ }); const tab = baseField.keys({ - name: joi.string().when('localized', { is: joi.exist(), then: joi.required() }), + name: joi.string() + .when('localized', { is: joi.exist(), then: joi.required() }), localized: joi.boolean(), interfaceName: joi.string().when('name', { not: joi.exist(), then: joi.forbidden() }), label: joi.alternatives().try( joi.string(), joi.object().pattern(joi.string(), [joi.string()]), - ).required(), + ).when('name', { is: joi.not(), then: joi.required() }), fields: joi.array().items(joi.link('#field')).required(), description: joi.alternatives().try( joi.string(), diff --git a/test/fields/collections/Tabs/index.ts b/test/fields/collections/Tabs/index.ts index 9b8a48be6..c1321b88c 100644 --- a/test/fields/collections/Tabs/index.ts +++ b/test/fields/collections/Tabs/index.ts @@ -135,7 +135,6 @@ const TabsFields: CollectionConfig = { }, { name: 'namedTabWithDefaultValue', - label: 'Tab with Default Value', description: 'This tab has a name, which should namespace the contained fields.', fields: [ { @@ -159,7 +158,6 @@ const TabsFields: CollectionConfig = { }, { name: 'accessControlTab', - label: 'Access Control Tab', access: { read: () => false, },