From 97837f0708fc1c7570c6947c4c0a51dcf1f1a7d3 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Fri, 26 Jul 2024 14:03:25 -0400 Subject: [PATCH] feat(ui)!: passes field props to custom components (#7360) ## Description Currently, there is no way to read field props from within a custom field component, i.e. `admin.components.Description`. For example, if you set `maxLength: 100` on your field, your custom description component cannot read it from `props.maxLength` or any other methods. Because these components are rendered on the server, there is also no way of using `admin.component.Field` to inject custom props yourself, either. To support this, we can simply pass the base component props into these components on the server, as expected. This has also led to custom field component props becoming more strictly typed within the config. This change is considered breaking only because the types have changed. This only affects you if you were previously importing the following types into your own custom components. To migrate, simply change the import paths for that type. Old: ```ts import type { ArrayFieldProps, ReducedBlock, BlocksFieldProps, CheckboxFieldProps, CodeFieldProps, CollapsibleFieldProps, DateFieldProps, EmailFieldProps, GroupFieldProps, HiddenFieldProps, JSONFieldProps, NumberFieldProps, PointFieldProps, RadioFieldProps, RelationshipFieldProps, RichTextComponentProps, RowFieldProps, SelectFieldProps, TabsFieldProps, TextFieldProps, TextareaFieldProps, UploadFieldProps, ErrorProps, FormFieldBase, FieldComponentProps, FieldMap, MappedField, MappedTab, ReducedBlock, } from '@payloadcms/ui' ``` New: ```ts import type { FormFieldBase, // etc. } from 'payload' ``` Custom field components are now much more strongly typed. To make this happen, an explicit type for every custom component has been generated for every field type. The convention is to append `DescriptionComponent`, `LabelComponent`, and `ErrorComponent` onto the end of the field name, i.e. `TextFieldDescriptionComponent`. Here's an example: ```ts import type { TextFieldDescriptionComponent } from 'payload' import React from 'react' export const CustomDescription: TextFieldDescriptionComponent = (props) => { return (
{`The max length of this field is: ${props?.maxLength}`}
) } ``` Here's the full list of all new types: Label Components: ```ts import type { ArrayFieldLabelComponent, BlocksFieldLabelComponent, CheckboxFieldLabelComponent, CodeFieldLabelComponent, CollapsibleFieldLabelComponent, DateFieldLabelComponent, EmailFieldLabelComponent, GroupFieldLabelComponent, HiddenFieldLabelComponent, JSONFieldLabelComponent, NumberFieldLabelComponent, PointFieldLabelComponent, RadioFieldLabelComponent, RelationshipFieldLabelComponent, RichTextFieldLabelComponent, RowFieldLabelComponent, SelectFieldLabelComponent, TabsFieldLabelComponent, TextFieldLabelComponent, TextareaFieldLabelComponent, UploadFieldLabelComponent } from 'payload' ``` Error Components: ```tsx import type { ArrayFieldErrorComponent, BlocksFieldErrorComponent, CheckboxFieldErrorComponent, CodeFieldErrorComponent, CollapsibleFieldErrorComponent, DateFieldErrorComponent, EmailFieldErrorComponent, GroupFieldErrorComponent, HiddenFieldErrorComponent, JSONFieldErrorComponent, NumberFieldErrorComponent, PointFieldErrorComponent, RadioFieldErrorComponent, RelationshipFieldErrorComponent, RichTextFieldErrorComponent, RowFieldErrorComponent, SelectFieldErrorComponent, TabsFieldErrorComponent, TextFieldErrorComponent, TextareaFieldErrorComponent, UploadFieldErrorComponent } from 'payload' ``` Description Components: ```tsx import type { ArrayFieldDescriptionComponent, BlocksFieldDescriptionComponent, CheckboxFieldDescriptionComponent, CodeFieldDescriptionComponent, CollapsibleFieldDescriptionComponent, DateFieldDescriptionComponent, EmailFieldDescriptionComponent, GroupFieldDescriptionComponent, HiddenFieldDescriptionComponent, JSONFieldDescriptionComponent, NumberFieldDescriptionComponent, PointFieldDescriptionComponent, RadioFieldDescriptionComponent, RelationshipFieldDescriptionComponent, RichTextFieldDescriptionComponent, RowFieldDescriptionComponent, SelectFieldDescriptionComponent, TabsFieldDescriptionComponent, TextFieldDescriptionComponent, TextareaFieldDescriptionComponent, UploadFieldDescriptionComponent } from 'payload' ``` This PR also: - Standardizes the `FieldBase['label']` type with a new `LabelStatic` type. This makes type usage much more consistent across components. - Simplifies some of the typings in the field component map, removes unneeded ``, etc. - Fixes misc. linting issues around voiding promises - [x] I have read and understand the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository. ## Type of change - [x] New feature (non-breaking change which adds functionality) ## Checklist: - [x] I have added tests that prove my fix is effective or that my feature works - [x] Existing test suite passes locally with my changes - [x] I have made corresponding changes to the documentation --- docs/admin/fields.mdx | 98 ++++- docs/migration-guide/overview.mdx | 1 - .../src/lib/wrap-next-config.ts | 2 +- .../src/transform/read/traverseFields.ts | 3 +- .../src/views/LivePreview/index.client.tsx | 2 +- .../src/views/Version/Default/SetStepNav.tsx | 3 +- .../fields/Iterable/index.tsx | 2 +- .../fields/Relationship/index.tsx | 4 +- .../fields/Select/index.tsx | 4 +- .../RenderFieldsToDiff/fields/Tabs/index.tsx | 2 +- .../RenderFieldsToDiff/fields/types.ts | 3 +- .../views/Version/RenderFieldsToDiff/types.ts | 3 +- packages/payload/src/admin/elements/Cell.ts | 3 +- packages/payload/src/admin/fields/Array.ts | 22 + packages/payload/src/admin/fields/Blocks.ts | 32 ++ packages/payload/src/admin/fields/Checkbox.ts | 19 + packages/payload/src/admin/fields/Code.ts | 17 + .../payload/src/admin/fields/Collapsible.ts | 15 + packages/payload/src/admin/fields/Date.ts | 17 + packages/payload/src/admin/fields/Email.ts | 17 + packages/payload/src/admin/fields/Group.ts | 17 + packages/payload/src/admin/fields/Hidden.ts | 16 + packages/payload/src/admin/fields/JSON.ts | 17 + packages/payload/src/admin/fields/Number.ts | 22 + packages/payload/src/admin/fields/Point.ts | 16 + packages/payload/src/admin/fields/Radio.ts | 21 + .../payload/src/admin/fields/Relationship.ts | 19 + packages/payload/src/admin/fields/RichText.ts | 15 + packages/payload/src/admin/fields/Row.ts | 18 + packages/payload/src/admin/fields/Select.ts | 21 + packages/payload/src/admin/fields/Tabs.ts | 24 ++ packages/payload/src/admin/fields/Text.ts | 23 ++ packages/payload/src/admin/fields/Textarea.ts | 19 + packages/payload/src/admin/fields/Upload.ts | 17 + packages/payload/src/admin/fields/index.ts | 86 ++++ packages/payload/src/admin/forms/Error.ts | 14 +- packages/payload/src/admin/forms/Field.ts | 32 ++ .../src/admin/forms/FieldDescription.ts | 18 +- packages/payload/src/admin/forms/FieldMap.ts | 24 ++ packages/payload/src/admin/forms/Label.ts | 25 +- packages/payload/src/admin/types.ts | 176 +++++++- .../payload/src/collections/config/types.ts | 5 +- packages/payload/src/config/types.ts | 2 + packages/payload/src/fields/config/types.ts | 66 +-- .../Forms/DynamicFieldSelector.tsx | 4 +- .../Forms/DynamicPriceSelector.tsx | 3 +- .../src/collections/Forms/index.ts | 2 +- .../MetaDescriptionComponent.tsx | 7 +- .../fields/MetaImage/MetaImageComponent.tsx | 4 +- .../fields/MetaTitle/MetaTitleComponent.tsx | 7 +- .../blocks/component/BlockContent.tsx | 4 +- .../src/features/blocks/feature.client.tsx | 3 +- packages/richtext-lexical/src/field/Field.tsx | 2 +- packages/richtext-lexical/src/field/index.tsx | 2 +- .../src/lexical/LexicalProvider.tsx | 2 +- .../config/client/EditorConfigProvider.tsx | 2 +- .../richtext-slate/src/field/RichText.tsx | 3 +- .../field/elements/link/LinkDrawer/types.ts | 3 +- .../elements/relationship/Element/index.tsx | 2 +- .../upload/Element/UploadDrawer/index.tsx | 4 +- .../field/elements/upload/Element/index.tsx | 2 +- packages/richtext-slate/src/field/index.tsx | 2 +- .../field/providers/ElementButtonProvider.tsx | 2 +- .../src/field/providers/ElementProvider.tsx | 2 +- .../field/providers/LeafButtonProvider.tsx | 2 +- .../src/field/providers/LeafProvider.tsx | 2 +- .../ui/src/elements/ArrayAction/index.tsx | 4 +- .../ui/src/elements/Collapsible/index.tsx | 4 +- .../ui/src/elements/DocumentFields/index.tsx | 4 +- packages/ui/src/elements/EditMany/index.tsx | 4 +- .../ui/src/elements/FieldSelect/index.tsx | 4 +- .../ListControls/getTextFieldsToBeSearched.ts | 2 +- .../ui/src/elements/ListControls/index.tsx | 4 +- packages/ui/src/elements/SortColumn/index.tsx | 16 +- packages/ui/src/elements/StepNav/types.ts | 4 +- packages/ui/src/elements/Table/index.tsx | 4 +- .../TableColumns/buildColumnState.tsx | 4 +- .../elements/TableColumns/filterFields.tsx | 2 +- .../TableColumns/getInitialColumns.ts | 3 +- .../elements/WhereBuilder/reduceFieldMap.tsx | 12 +- .../ui/src/elements/WhereBuilder/types.ts | 4 +- packages/ui/src/exports/client/index.ts | 14 +- packages/ui/src/fields/Array/ArrayRow.tsx | 5 +- packages/ui/src/fields/Array/index.tsx | 24 +- packages/ui/src/fields/Blocks/BlockRow.tsx | 5 +- .../src/fields/Blocks/BlocksDrawer/index.tsx | 8 +- packages/ui/src/fields/Blocks/RowActions.tsx | 11 +- packages/ui/src/fields/Blocks/index.tsx | 21 +- packages/ui/src/fields/Checkbox/Input.tsx | 2 +- packages/ui/src/fields/Checkbox/index.tsx | 3 +- packages/ui/src/fields/Checkbox/types.ts | 12 - packages/ui/src/fields/Code/index.tsx | 12 +- packages/ui/src/fields/Collapsible/index.tsx | 14 +- packages/ui/src/fields/DateTime/index.tsx | 12 +- packages/ui/src/fields/Email/index.tsx | 12 +- .../ui/src/fields/FieldDescription/index.tsx | 6 +- packages/ui/src/fields/FieldError/index.tsx | 6 +- packages/ui/src/fields/FieldLabel/index.tsx | 6 +- packages/ui/src/fields/Group/index.tsx | 15 +- packages/ui/src/fields/Hidden/index.tsx | 15 +- packages/ui/src/fields/JSON/index.tsx | 12 +- packages/ui/src/fields/Number/index.tsx | 16 +- packages/ui/src/fields/Password/index.tsx | 4 +- packages/ui/src/fields/Point/index.tsx | 14 +- .../ui/src/fields/RadioGroup/Radio/index.tsx | 6 +- packages/ui/src/fields/RadioGroup/index.tsx | 16 +- packages/ui/src/fields/Relationship/index.tsx | 6 +- packages/ui/src/fields/Relationship/types.ts | 14 +- packages/ui/src/fields/RichText/index.tsx | 12 +- packages/ui/src/fields/Row/index.tsx | 4 +- packages/ui/src/fields/Row/types.ts | 13 - packages/ui/src/fields/Select/Input.tsx | 3 +- packages/ui/src/fields/Select/index.tsx | 15 +- packages/ui/src/fields/Tabs/Tab/index.tsx | 4 +- packages/ui/src/fields/Tabs/index.tsx | 19 +- packages/ui/src/fields/Text/index.tsx | 6 +- packages/ui/src/fields/Text/types.ts | 17 +- packages/ui/src/fields/Textarea/index.tsx | 6 +- packages/ui/src/fields/Textarea/types.ts | 14 +- packages/ui/src/fields/Upload/Input.tsx | 8 +- packages/ui/src/fields/Upload/index.tsx | 3 +- packages/ui/src/fields/Upload/types.ts | 11 - packages/ui/src/fields/shared/index.tsx | 37 +- .../src/forms/Form/createNestedFieldPath.ts | 4 +- .../ui/src/forms/RenderFields/RenderField.tsx | 11 +- packages/ui/src/forms/RenderFields/types.ts | 4 +- .../WatchChildErrors/buildPathSegments.ts | 2 +- .../ui/src/forms/WatchChildErrors/index.tsx | 3 +- packages/ui/src/hooks/useUseAsTitle.ts | 4 +- .../ComponentMap/buildComponentMap/fields.tsx | 390 ++++++++++-------- .../ComponentMap/buildComponentMap/types.ts | 91 +--- .../ui/src/providers/ComponentMap/index.tsx | 4 +- packages/ui/src/utilities/flattenFieldMap.ts | 2 +- .../collections/CustomFields}/AfterInput.tsx | 0 .../collections/CustomFields}/BeforeInput.tsx | 0 .../collections/CustomFields}/CustomError.tsx | 4 +- .../CustomFields}/FieldDescription/index.tsx | 2 +- .../Select/index.tsx} | 5 +- .../CustomFields/fields/Text/Description.tsx | 9 + .../CustomFields/fields/Text/Label.tsx | 7 + test/admin/collections/CustomFields/index.ts | 46 ++- test/admin/collections/Posts.ts | 24 -- test/admin/e2e/1/e2e.spec.ts | 81 ++++ test/admin/e2e/2/e2e.spec.ts | 23 -- test/admin/payload-types.ts | 7 +- test/fields/collections/Number/e2e.spec.ts | 25 +- test/fields/collections/Text/CustomLabel.tsx | 18 - test/fields/collections/Text/e2e.spec.ts | 34 -- test/fields/collections/Text/index.ts | 33 -- test/fields/payload-types.ts | 104 +++-- test/live-preview/payload-types.ts | 8 +- test/plugin-form-builder/payload-types.ts | 8 +- test/plugin-seo/payload-types.ts | 8 +- test/versions/payload-types.ts | 8 +- 154 files changed, 1525 insertions(+), 1030 deletions(-) create mode 100644 packages/payload/src/admin/fields/Array.ts create mode 100644 packages/payload/src/admin/fields/Blocks.ts create mode 100644 packages/payload/src/admin/fields/Checkbox.ts create mode 100644 packages/payload/src/admin/fields/Code.ts create mode 100644 packages/payload/src/admin/fields/Collapsible.ts create mode 100644 packages/payload/src/admin/fields/Date.ts create mode 100644 packages/payload/src/admin/fields/Email.ts create mode 100644 packages/payload/src/admin/fields/Group.ts create mode 100644 packages/payload/src/admin/fields/Hidden.ts create mode 100644 packages/payload/src/admin/fields/JSON.ts create mode 100644 packages/payload/src/admin/fields/Number.ts create mode 100644 packages/payload/src/admin/fields/Point.ts create mode 100644 packages/payload/src/admin/fields/Radio.ts create mode 100644 packages/payload/src/admin/fields/Relationship.ts create mode 100644 packages/payload/src/admin/fields/RichText.ts create mode 100644 packages/payload/src/admin/fields/Row.ts create mode 100644 packages/payload/src/admin/fields/Select.ts create mode 100644 packages/payload/src/admin/fields/Tabs.ts create mode 100644 packages/payload/src/admin/fields/Text.ts create mode 100644 packages/payload/src/admin/fields/Textarea.ts create mode 100644 packages/payload/src/admin/fields/Upload.ts create mode 100644 packages/payload/src/admin/fields/index.ts create mode 100644 packages/payload/src/admin/forms/Field.ts create mode 100644 packages/payload/src/admin/forms/FieldMap.ts delete mode 100644 packages/ui/src/fields/Checkbox/types.ts delete mode 100644 packages/ui/src/fields/Row/types.ts delete mode 100644 packages/ui/src/fields/Upload/types.ts rename test/{fields/collections/Text => admin/collections/CustomFields}/AfterInput.tsx (100%) rename test/{fields/collections/Text => admin/collections/CustomFields}/BeforeInput.tsx (100%) rename test/{fields/collections/Text => admin/collections/CustomFields}/CustomError.tsx (86%) rename test/admin/{components => collections/CustomFields}/FieldDescription/index.tsx (84%) rename test/admin/collections/CustomFields/{components/CustomSelect.tsx => fields/Select/index.tsx} (87%) create mode 100644 test/admin/collections/CustomFields/fields/Text/Description.tsx create mode 100644 test/admin/collections/CustomFields/fields/Text/Label.tsx delete mode 100644 test/fields/collections/Text/CustomLabel.tsx diff --git a/docs/admin/fields.mdx b/docs/admin/fields.mdx index 8b3dad20b..21ae64100 100644 --- a/docs/admin/fields.mdx +++ b/docs/admin/fields.mdx @@ -266,12 +266,10 @@ export const myField: Field = { _For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._ -All Label Components receive the following props: +Custom Label Components receive all [Field Component](#the-field-component) props, plus the following props: | Property | Description | | -------------- | ---------------------------------------------------------------- | -| **`label`** | Label value provided in field, it can be used with i18n. | -| **`required`** | The `admin.required` property defined in the [Field Config](../fields/overview). | | **`schemaPath`** | The path to the field in the schema. Similar to `path`, but without dynamic indices. | @@ -279,6 +277,36 @@ All Label Components receive the following props: All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details. +#### TypeScript + +When building Custom Error Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Error Component, one for every [Field Type](../fields/overview). The convention is to append `ErrorComponent` to the type of field, i.e. `TextFieldErrorComponent`. + +```tsx +import type { + ArrayFieldLabelComponent, + BlocksFieldLabelComponent, + CheckboxFieldLabelComponent, + CodeFieldLabelComponent, + CollapsibleFieldLabelComponent, + DateFieldLabelComponent, + EmailFieldLabelComponent, + GroupFieldLabelComponent, + HiddenFieldLabelComponent, + JSONFieldLabelComponent, + NumberFieldLabelComponent, + PointFieldLabelComponent, + RadioFieldLabelComponent, + RelationshipFieldLabelComponent, + RichTextFieldLabelComponent, + RowFieldLabelComponent, + SelectFieldLabelComponent, + TabsFieldLabelComponent, + TextFieldLabelComponent, + TextareaFieldLabelComponent, + UploadFieldLabelComponent +} from 'payload' +``` + ### The Error Component The Error Component is rendered when a field fails validation. It is typically displayed beneath the field input in a visually-compelling style. @@ -301,7 +329,7 @@ export const myField: Field = { _For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._ -All Error Components receive the following props: +Custom Error Components receive all [Field Component](#the-field-component) props, plus the following props: | Property | Description | | --------------- | ------------------------------------------------------------- | @@ -312,6 +340,36 @@ All Error Components receive the following props: All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details. +#### TypeScript + +When building Custom Error Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Error Component, one for every [Field Type](../fields/overview). The convention is to append `ErrorComponent` to the type of field, i.e. `TextFieldErrorComponent`. + +```tsx +import type { + ArrayFieldErrorComponent, + BlocksFieldErrorComponent, + CheckboxFieldErrorComponent, + CodeFieldErrorComponent, + CollapsibleFieldErrorComponent, + DateFieldErrorComponent, + EmailFieldErrorComponent, + GroupFieldErrorComponent, + HiddenFieldErrorComponent, + JSONFieldErrorComponent, + NumberFieldErrorComponent, + PointFieldErrorComponent, + RadioFieldErrorComponent, + RelationshipFieldErrorComponent, + RichTextFieldErrorComponent, + RowFieldErrorComponent, + SelectFieldErrorComponent, + TabsFieldErrorComponent, + TextFieldErrorComponent, + TextareaFieldErrorComponent, + UploadFieldErrorComponent +} from 'payload' +``` + ### The Description Property Field Descriptions are used to provide additional information to the editor about a field, such as special instructions. Their placement varies from field to field, but typically are displayed with subtle style differences beneath the field inputs. @@ -406,7 +464,7 @@ export const MyCollectionConfig: SanitizedCollectionConfig = { _For details on how to build a Custom Description, see [Building Custom Components](./components#building-custom-components)._ -All Description Components receive the following props: +Custom Description Components receive all [Field Component](#the-field-component) props, plus the following props: | Property | Description | | -------------- | ---------------------------------------------------------------- | @@ -417,6 +475,36 @@ All Description Components receive the following props: All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details. +#### TypeScript + +When building Custom Description Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Description Component, one for every [Field Type](../fields/overview). The convention is to append `DescriptionComponent` to the type of field, i.e. `TextFieldDescriptionComponent`. + +```tsx +import type { + ArrayFieldDescriptionComponent, + BlocksFieldDescriptionComponent, + CheckboxFieldDescriptionComponent, + CodeFieldDescriptionComponent, + CollapsibleFieldDescriptionComponent, + DateFieldDescriptionComponent, + EmailFieldDescriptionComponent, + GroupFieldDescriptionComponent, + HiddenFieldDescriptionComponent, + JSONFieldDescriptionComponent, + NumberFieldDescriptionComponent, + PointFieldDescriptionComponent, + RadioFieldDescriptionComponent, + RelationshipFieldDescriptionComponent, + RichTextFieldDescriptionComponent, + RowFieldDescriptionComponent, + SelectFieldDescriptionComponent, + TabsFieldDescriptionComponent, + TextFieldDescriptionComponent, + TextareaFieldDescriptionComponent, + UploadFieldDescriptionComponent +} from 'payload' +``` + ### afterInput and beforeInput With these properties you can add multiple components _before_ and _after_ the input element, as their name suggests. This is useful when you need to render additional elements alongside the field without replacing the entire field component. diff --git a/docs/migration-guide/overview.mdx b/docs/migration-guide/overview.mdx index ff1ee1e2b..00bd1348a 100644 --- a/docs/migration-guide/overview.mdx +++ b/docs/migration-guide/overview.mdx @@ -335,7 +335,6 @@ import { FieldMap, File, Form, - FormFieldBase, FormLoadingOverlayToggle, FormSubmit, GenerateConfirmation, diff --git a/packages/create-payload-app/src/lib/wrap-next-config.ts b/packages/create-payload-app/src/lib/wrap-next-config.ts index b2e1e208a..cf9626f4e 100644 --- a/packages/create-payload-app/src/lib/wrap-next-config.ts +++ b/packages/create-payload-app/src/lib/wrap-next-config.ts @@ -138,7 +138,7 @@ export async function parseAndModifyConfigContent( (m) => m.type === 'ExportDefaultExpression' && (m.expression.type === 'Identifier' || m.expression.type === 'CallExpression'), - ) as ExportDefaultExpression | undefined + ) if (exportDefaultDeclaration) { if (!('span' in exportDefaultDeclaration.expression)) { diff --git a/packages/drizzle/src/transform/read/traverseFields.ts b/packages/drizzle/src/transform/read/traverseFields.ts index ef28c8cc3..a899079f9 100644 --- a/packages/drizzle/src/transform/read/traverseFields.ts +++ b/packages/drizzle/src/transform/read/traverseFields.ts @@ -1,5 +1,4 @@ - -import type { Field, SanitizedConfig , TabAsField } from 'payload' +import type { Field, SanitizedConfig, TabAsField } from 'payload' import { fieldAffectsData } from 'payload/shared' diff --git a/packages/next/src/views/LivePreview/index.client.tsx b/packages/next/src/views/LivePreview/index.client.tsx index 688de86fc..d696c6e5e 100644 --- a/packages/next/src/views/LivePreview/index.client.tsx +++ b/packages/next/src/views/LivePreview/index.client.tsx @@ -1,11 +1,11 @@ 'use client' import type { FormProps } from '@payloadcms/ui' -import type { FieldMap } from '@payloadcms/ui/utilities/buildComponentMap' import type { ClientCollectionConfig, ClientConfig, ClientGlobalConfig, Data, + FieldMap, LivePreviewConfig, } from 'payload' diff --git a/packages/next/src/views/Version/Default/SetStepNav.tsx b/packages/next/src/views/Version/Default/SetStepNav.tsx index 3efb70ff6..4c2664346 100644 --- a/packages/next/src/views/Version/Default/SetStepNav.tsx +++ b/packages/next/src/views/Version/Default/SetStepNav.tsx @@ -1,6 +1,5 @@ import type { StepNavItem } from '@payloadcms/ui' -import type { FieldMap } from '@payloadcms/ui/utilities/buildComponentMap' -import type { ClientCollectionConfig, ClientGlobalConfig } from 'payload' +import type { ClientCollectionConfig, ClientGlobalConfig, FieldMap } from 'payload' import type React from 'react' import { getTranslation } from '@payloadcms/translations' diff --git a/packages/next/src/views/Version/RenderFieldsToDiff/fields/Iterable/index.tsx b/packages/next/src/views/Version/RenderFieldsToDiff/fields/Iterable/index.tsx index 29e674a21..c8788515a 100644 --- a/packages/next/src/views/Version/RenderFieldsToDiff/fields/Iterable/index.tsx +++ b/packages/next/src/views/Version/RenderFieldsToDiff/fields/Iterable/index.tsx @@ -1,4 +1,4 @@ -import type { MappedField } from '@payloadcms/ui/utilities/buildComponentMap' +import type { MappedField } from 'payload' import { getTranslation } from '@payloadcms/translations' import { getUniqueListBy } from 'payload/shared' diff --git a/packages/next/src/views/Version/RenderFieldsToDiff/fields/Relationship/index.tsx b/packages/next/src/views/Version/RenderFieldsToDiff/fields/Relationship/index.tsx index 9c5ae384e..4dbc06da6 100644 --- a/packages/next/src/views/Version/RenderFieldsToDiff/fields/Relationship/index.tsx +++ b/packages/next/src/views/Version/RenderFieldsToDiff/fields/Relationship/index.tsx @@ -1,8 +1,8 @@ 'use client' -import type { ClientCollectionConfig } from 'payload' +import type { ClientCollectionConfig, MappedField } from 'payload' import { getTranslation } from '@payloadcms/translations' -import { type MappedField, useConfig } from '@payloadcms/ui' +import { useConfig } from '@payloadcms/ui' import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/shared' import React from 'react' import ReactDiffViewerImport from 'react-diff-viewer-continued' diff --git a/packages/next/src/views/Version/RenderFieldsToDiff/fields/Select/index.tsx b/packages/next/src/views/Version/RenderFieldsToDiff/fields/Select/index.tsx index 85b294cdf..54d692fc7 100644 --- a/packages/next/src/views/Version/RenderFieldsToDiff/fields/Select/index.tsx +++ b/packages/next/src/views/Version/RenderFieldsToDiff/fields/Select/index.tsx @@ -1,7 +1,5 @@ import type { I18nClient } from '@payloadcms/translations' -import type { SelectFieldProps } from '@payloadcms/ui' -import type { MappedField } from '@payloadcms/ui/utilities/buildComponentMap' -import type { OptionObject, SelectField } from 'payload' +import type { MappedField, OptionObject, SelectField, SelectFieldProps } from 'payload' import { getTranslation } from '@payloadcms/translations' import React from 'react' diff --git a/packages/next/src/views/Version/RenderFieldsToDiff/fields/Tabs/index.tsx b/packages/next/src/views/Version/RenderFieldsToDiff/fields/Tabs/index.tsx index adc5a689a..b984f4096 100644 --- a/packages/next/src/views/Version/RenderFieldsToDiff/fields/Tabs/index.tsx +++ b/packages/next/src/views/Version/RenderFieldsToDiff/fields/Tabs/index.tsx @@ -1,4 +1,4 @@ -import type { MappedField, TabsFieldProps } from '@payloadcms/ui' +import type { MappedField, TabsFieldProps } from 'payload' import React from 'react' diff --git a/packages/next/src/views/Version/RenderFieldsToDiff/fields/types.ts b/packages/next/src/views/Version/RenderFieldsToDiff/fields/types.ts index 74488f4e5..c032cb046 100644 --- a/packages/next/src/views/Version/RenderFieldsToDiff/fields/types.ts +++ b/packages/next/src/views/Version/RenderFieldsToDiff/fields/types.ts @@ -1,6 +1,5 @@ import type { I18nClient } from '@payloadcms/translations' -import type { FieldMap, MappedField } from '@payloadcms/ui/utilities/buildComponentMap' -import type { FieldPermissions } from 'payload' +import type { FieldMap, FieldPermissions, MappedField } from 'payload' import type React from 'react' import type { DiffMethod } from 'react-diff-viewer-continued' diff --git a/packages/next/src/views/Version/RenderFieldsToDiff/types.ts b/packages/next/src/views/Version/RenderFieldsToDiff/types.ts index a9205769e..265d9e9b5 100644 --- a/packages/next/src/views/Version/RenderFieldsToDiff/types.ts +++ b/packages/next/src/views/Version/RenderFieldsToDiff/types.ts @@ -1,6 +1,5 @@ import type { I18nClient } from '@payloadcms/translations' -import type { FieldMap, MappedField } from '@payloadcms/ui/utilities/buildComponentMap' -import type { FieldPermissions } from 'payload' +import type { FieldMap, FieldPermissions, MappedField } from 'payload' import type { DiffMethod } from 'react-diff-viewer-continued' import type { DiffComponents } from './fields/types.js' diff --git a/packages/payload/src/admin/elements/Cell.ts b/packages/payload/src/admin/elements/Cell.ts index 97fc349f2..4f07ec6e5 100644 --- a/packages/payload/src/admin/elements/Cell.ts +++ b/packages/payload/src/admin/elements/Cell.ts @@ -8,6 +8,7 @@ import type { RelationshipField, SelectField, } from '../../fields/config/types.js' +import type { FormFieldBase } from '../types.js' export type RowData = Record @@ -20,7 +21,7 @@ export type CellComponentProps = { dateDisplayFormat?: DateField['admin']['date']['displayFormat'] fieldType?: Field['type'] isFieldAffectingData?: boolean - label?: Record | string + label?: FormFieldBase['label'] labels?: Labels link?: boolean name: FieldBase['name'] diff --git a/packages/payload/src/admin/fields/Array.ts b/packages/payload/src/admin/fields/Array.ts new file mode 100644 index 000000000..0721189a8 --- /dev/null +++ b/packages/payload/src/admin/fields/Array.ts @@ -0,0 +1,22 @@ +import type { ArrayField } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { FieldMap } from '../forms/FieldMap.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type ArrayFieldProps = { + CustomRowLabel?: React.ReactNode + fieldMap: FieldMap + forceRender?: boolean + isSortable?: boolean + labels?: ArrayField['labels'] + maxRows?: ArrayField['maxRows'] + minRows?: ArrayField['minRows'] + name?: string + width?: string +} & FormFieldBase + +export type ArrayFieldLabelComponent = LabelComponent<'array'> + +export type ArrayFieldDescriptionComponent = DescriptionComponent<'array'> + +export type ArrayFieldErrorComponent = ErrorComponent<'array'> diff --git a/packages/payload/src/admin/fields/Blocks.ts b/packages/payload/src/admin/fields/Blocks.ts new file mode 100644 index 000000000..494702e1a --- /dev/null +++ b/packages/payload/src/admin/fields/Blocks.ts @@ -0,0 +1,32 @@ +import type { Block, BlockField } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { FieldMap } from '../forms/FieldMap.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type BlocksFieldProps = { + blocks?: ReducedBlock[] + forceRender?: boolean + isSortable?: boolean + labels?: BlockField['labels'] + maxRows?: number + minRows?: number + name?: string + slug?: string + width?: string +} & FormFieldBase + +export type ReducedBlock = { + LabelComponent: Block['admin']['components']['Label'] + custom?: Record + fieldMap: FieldMap + imageAltText?: string + imageURL?: string + labels: BlockField['labels'] + slug: string +} + +export type BlocksFieldLabelComponent = LabelComponent<'blocks'> + +export type BlocksFieldDescriptionComponent = DescriptionComponent<'blocks'> + +export type BlocksFieldErrorComponent = ErrorComponent<'blocks'> diff --git a/packages/payload/src/admin/fields/Checkbox.ts b/packages/payload/src/admin/fields/Checkbox.ts new file mode 100644 index 000000000..5fa9c2250 --- /dev/null +++ b/packages/payload/src/admin/fields/Checkbox.ts @@ -0,0 +1,19 @@ +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type CheckboxFieldProps = { + checked?: boolean + disableFormData?: boolean + id?: string + name?: string + onChange?: (val: boolean) => void + partialChecked?: boolean + path?: string + width?: string +} & FormFieldBase + +export type CheckboxFieldLabelComponent = LabelComponent<'checkbox'> + +export type CheckboxFieldDescriptionComponent = DescriptionComponent<'checkbox'> + +export type CheckboxFieldErrorComponent = ErrorComponent<'checkbox'> diff --git a/packages/payload/src/admin/fields/Code.ts b/packages/payload/src/admin/fields/Code.ts new file mode 100644 index 000000000..dd9158369 --- /dev/null +++ b/packages/payload/src/admin/fields/Code.ts @@ -0,0 +1,17 @@ +import type { CodeField } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type CodeFieldProps = { + editorOptions?: CodeField['admin']['editorOptions'] + language?: CodeField['admin']['language'] + name?: string + path?: string + width: string +} & FormFieldBase + +export type CodeFieldLabelComponent = LabelComponent<'code'> + +export type CodeFieldDescriptionComponent = DescriptionComponent<'code'> + +export type CodeFieldErrorComponent = ErrorComponent<'code'> diff --git a/packages/payload/src/admin/fields/Collapsible.ts b/packages/payload/src/admin/fields/Collapsible.ts new file mode 100644 index 000000000..602c728be --- /dev/null +++ b/packages/payload/src/admin/fields/Collapsible.ts @@ -0,0 +1,15 @@ +import type { ErrorComponent } from '../forms/Error.js' +import type { FieldMap } from '../forms/FieldMap.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type CollapsibleFieldProps = { + fieldMap: FieldMap + initCollapsed?: boolean + width?: string +} & FormFieldBase + +export type CollapsibleFieldLabelComponent = LabelComponent<'collapsible'> + +export type CollapsibleFieldDescriptionComponent = DescriptionComponent<'collapsible'> + +export type CollapsibleFieldErrorComponent = ErrorComponent<'collapsible'> diff --git a/packages/payload/src/admin/fields/Date.ts b/packages/payload/src/admin/fields/Date.ts new file mode 100644 index 000000000..9c40c6ee2 --- /dev/null +++ b/packages/payload/src/admin/fields/Date.ts @@ -0,0 +1,17 @@ +import type { DateField } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type DateFieldProps = { + date?: DateField['admin']['date'] + name?: string + path?: string + placeholder?: DateField['admin']['placeholder'] | string + width?: string +} & FormFieldBase + +export type DateFieldLabelComponent = LabelComponent<'date'> + +export type DateFieldDescriptionComponent = DescriptionComponent<'date'> + +export type DateFieldErrorComponent = ErrorComponent<'date'> diff --git a/packages/payload/src/admin/fields/Email.ts b/packages/payload/src/admin/fields/Email.ts new file mode 100644 index 000000000..3b6aef47d --- /dev/null +++ b/packages/payload/src/admin/fields/Email.ts @@ -0,0 +1,17 @@ +import type { EmailField } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type EmailFieldProps = { + autoComplete?: string + name?: string + path?: string + placeholder?: EmailField['admin']['placeholder'] + width?: string +} & FormFieldBase + +export type EmailFieldLabelComponent = LabelComponent<'email'> + +export type EmailFieldDescriptionComponent = DescriptionComponent<'email'> + +export type EmailFieldErrorComponent = ErrorComponent<'email'> diff --git a/packages/payload/src/admin/fields/Group.ts b/packages/payload/src/admin/fields/Group.ts new file mode 100644 index 000000000..d8d183c0e --- /dev/null +++ b/packages/payload/src/admin/fields/Group.ts @@ -0,0 +1,17 @@ +import type { ErrorComponent } from '../forms/Error.js' +import type { FieldMap } from '../forms/FieldMap.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type GroupFieldProps = { + fieldMap: FieldMap + forceRender?: boolean + hideGutter?: boolean + name?: string + width?: string +} & FormFieldBase + +export type GroupFieldLabelComponent = LabelComponent<'group'> + +export type GroupFieldDescriptionComponent = DescriptionComponent<'group'> + +export type GroupFieldErrorComponent = ErrorComponent<'group'> diff --git a/packages/payload/src/admin/fields/Hidden.ts b/packages/payload/src/admin/fields/Hidden.ts new file mode 100644 index 000000000..5cfeeda1d --- /dev/null +++ b/packages/payload/src/admin/fields/Hidden.ts @@ -0,0 +1,16 @@ +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type HiddenFieldProps = { + disableModifyingForm?: false + forceUsePathFromProps?: boolean + name?: string + path?: string + value?: unknown +} & FormFieldBase + +export type HiddenFieldLabelComponent = LabelComponent<'hidden'> + +export type HiddenFieldDescriptionComponent = DescriptionComponent<'hidden'> + +export type HiddenFieldErrorComponent = ErrorComponent<'hidden'> diff --git a/packages/payload/src/admin/fields/JSON.ts b/packages/payload/src/admin/fields/JSON.ts new file mode 100644 index 000000000..3d1d9bf57 --- /dev/null +++ b/packages/payload/src/admin/fields/JSON.ts @@ -0,0 +1,17 @@ +import type { JSONField } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type JSONFieldProps = { + editorOptions?: JSONField['admin']['editorOptions'] + jsonSchema?: Record + name?: string + path?: string + width?: string +} & FormFieldBase + +export type JSONFieldLabelComponent = LabelComponent<'json'> + +export type JSONFieldDescriptionComponent = DescriptionComponent<'json'> + +export type JSONFieldErrorComponent = ErrorComponent<'json'> diff --git a/packages/payload/src/admin/fields/Number.ts b/packages/payload/src/admin/fields/Number.ts new file mode 100644 index 000000000..94a18bd8c --- /dev/null +++ b/packages/payload/src/admin/fields/Number.ts @@ -0,0 +1,22 @@ +import type { NumberField } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type NumberFieldProps = { + hasMany?: boolean + max?: number + maxRows?: number + min?: number + name?: string + onChange?: (e: number) => void + path?: string + placeholder?: NumberField['admin']['placeholder'] + step?: number + width?: string +} & FormFieldBase + +export type NumberFieldLabelComponent = LabelComponent<'number'> + +export type NumberFieldDescriptionComponent = DescriptionComponent<'number'> + +export type NumberFieldErrorComponent = ErrorComponent<'number'> diff --git a/packages/payload/src/admin/fields/Point.ts b/packages/payload/src/admin/fields/Point.ts new file mode 100644 index 000000000..0a60c9d06 --- /dev/null +++ b/packages/payload/src/admin/fields/Point.ts @@ -0,0 +1,16 @@ +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type PointFieldProps = { + name?: string + path?: string + placeholder?: string + step?: number + width?: string +} & FormFieldBase + +export type PointFieldLabelComponent = LabelComponent<'point'> + +export type PointFieldDescriptionComponent = DescriptionComponent<'point'> + +export type PointFieldErrorComponent = ErrorComponent<'point'> diff --git a/packages/payload/src/admin/fields/Radio.ts b/packages/payload/src/admin/fields/Radio.ts new file mode 100644 index 000000000..19a4c1827 --- /dev/null +++ b/packages/payload/src/admin/fields/Radio.ts @@ -0,0 +1,21 @@ +import type { Option } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type RadioFieldProps = { + layout?: 'horizontal' | 'vertical' + name?: string + onChange?: OnChange + options?: Option[] + path?: string + value?: string + width?: string +} & FormFieldBase + +export type OnChange = (value: T) => void + +export type RadioFieldLabelComponent = LabelComponent<'radio'> + +export type RadioFieldDescriptionComponent = DescriptionComponent<'radio'> + +export type RadioFieldErrorComponent = ErrorComponent<'radio'> diff --git a/packages/payload/src/admin/fields/Relationship.ts b/packages/payload/src/admin/fields/Relationship.ts new file mode 100644 index 000000000..525cec696 --- /dev/null +++ b/packages/payload/src/admin/fields/Relationship.ts @@ -0,0 +1,19 @@ +import type { RelationshipField } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type RelationshipFieldProps = { + allowCreate?: RelationshipField['admin']['allowCreate'] + hasMany?: boolean + isSortable?: boolean + name: string + relationTo?: RelationshipField['relationTo'] + sortOptions?: RelationshipField['admin']['sortOptions'] + width?: string +} & FormFieldBase + +export type RelationshipFieldLabelComponent = LabelComponent<'relationship'> + +export type RelationshipFieldDescriptionComponent = DescriptionComponent<'relationship'> + +export type RelationshipFieldErrorComponent = ErrorComponent<'relationship'> diff --git a/packages/payload/src/admin/fields/RichText.ts b/packages/payload/src/admin/fields/RichText.ts new file mode 100644 index 000000000..ce4707ece --- /dev/null +++ b/packages/payload/src/admin/fields/RichText.ts @@ -0,0 +1,15 @@ +import type { ErrorComponent } from '../forms/Error.js' +import type { MappedField } from '../forms/FieldMap.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type RichTextComponentProps = { + name: string + richTextComponentMap?: Map + width?: string +} & FormFieldBase + +export type RichTextFieldLabelComponent = LabelComponent<'richText'> + +export type RichTextFieldDescriptionComponent = DescriptionComponent<'richText'> + +export type RichTextFieldErrorComponent = ErrorComponent<'richText'> diff --git a/packages/payload/src/admin/fields/Row.ts b/packages/payload/src/admin/fields/Row.ts new file mode 100644 index 000000000..e3734e8af --- /dev/null +++ b/packages/payload/src/admin/fields/Row.ts @@ -0,0 +1,18 @@ +import type { DescriptionComponent, FormFieldBase, LabelComponent } from 'payload' + +import type { ErrorComponent } from '../forms/Error.js' +import type { FieldMap } from '../forms/FieldMap.js' + +export type RowFieldProps = { + fieldMap: FieldMap + forceRender?: boolean + indexPath: string + path?: string + width?: string +} & FormFieldBase + +export type RowFieldLabelComponent = LabelComponent<'row'> + +export type RowFieldDescriptionComponent = DescriptionComponent<'row'> + +export type RowFieldErrorComponent = ErrorComponent<'row'> diff --git a/packages/payload/src/admin/fields/Select.ts b/packages/payload/src/admin/fields/Select.ts new file mode 100644 index 000000000..4c830f0b6 --- /dev/null +++ b/packages/payload/src/admin/fields/Select.ts @@ -0,0 +1,21 @@ +import type { Option } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type SelectFieldProps = { + hasMany?: boolean + isClearable?: boolean + isSortable?: boolean + name?: string + onChange?: (e: string | string[]) => void + options?: Option[] + path?: string + value?: string + width?: string +} & FormFieldBase + +export type SelectFieldLabelComponent = LabelComponent<'select'> + +export type SelectFieldDescriptionComponent = DescriptionComponent<'select'> + +export type SelectFieldErrorComponent = ErrorComponent<'select'> diff --git a/packages/payload/src/admin/fields/Tabs.ts b/packages/payload/src/admin/fields/Tabs.ts new file mode 100644 index 000000000..f11613fb1 --- /dev/null +++ b/packages/payload/src/admin/fields/Tabs.ts @@ -0,0 +1,24 @@ +import type { TabsField } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { FieldMap } from '../forms/FieldMap.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type TabsFieldProps = { + forceRender?: boolean + name?: string + path?: string + tabs?: MappedTab[] + width?: string +} & FormFieldBase + +export type MappedTab = { + fieldMap?: FieldMap + label: TabsField['tabs'][0]['label'] + name?: string +} + +export type TabsFieldLabelComponent = LabelComponent<'tabs'> + +export type TabsFieldDescriptionComponent = DescriptionComponent<'tabs'> + +export type TabsFieldErrorComponent = ErrorComponent<'tabs'> diff --git a/packages/payload/src/admin/fields/Text.ts b/packages/payload/src/admin/fields/Text.ts new file mode 100644 index 000000000..9f01b7de7 --- /dev/null +++ b/packages/payload/src/admin/fields/Text.ts @@ -0,0 +1,23 @@ +import type { TextField } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type TextFieldProps = { + hasMany?: boolean + inputRef?: React.MutableRefObject + maxLength?: number + maxRows?: number + minLength?: number + minRows?: number + name?: string + onKeyDown?: React.KeyboardEventHandler + path?: string + placeholder?: TextField['admin']['placeholder'] + width?: string +} & FormFieldBase + +export type TextFieldLabelComponent = LabelComponent<'text'> + +export type TextFieldDescriptionComponent = DescriptionComponent<'text'> + +export type TextFieldErrorComponent = ErrorComponent<'text'> diff --git a/packages/payload/src/admin/fields/Textarea.ts b/packages/payload/src/admin/fields/Textarea.ts new file mode 100644 index 000000000..fd7c6977c --- /dev/null +++ b/packages/payload/src/admin/fields/Textarea.ts @@ -0,0 +1,19 @@ +import type { TextareaField } from '../../fields/config/types.js' +import type { ErrorComponent } from '../forms/Error.js' +import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js' + +export type TextareaFieldProps = { + maxLength?: number + minLength?: number + name?: string + path?: string + placeholder?: TextareaField['admin']['placeholder'] + rows?: number + width?: string +} & FormFieldBase + +export type TextareaFieldLabelComponent = LabelComponent<'textarea'> + +export type TextareaFieldDescriptionComponent = DescriptionComponent<'textarea'> + +export type TextareaFieldErrorComponent = ErrorComponent<'textarea'> diff --git a/packages/payload/src/admin/fields/Upload.ts b/packages/payload/src/admin/fields/Upload.ts new file mode 100644 index 000000000..27496fcc2 --- /dev/null +++ b/packages/payload/src/admin/fields/Upload.ts @@ -0,0 +1,17 @@ +import type { DescriptionComponent, FormFieldBase, LabelComponent, UploadField } from 'payload' + +import type { ErrorComponent } from '../forms/Error.js' + +export type UploadFieldProps = { + filterOptions?: UploadField['filterOptions'] + name?: string + path?: string + relationTo?: UploadField['relationTo'] + width?: string +} & FormFieldBase + +export type UploadFieldLabelComponent = LabelComponent<'upload'> + +export type UploadFieldDescriptionComponent = DescriptionComponent<'upload'> + +export type UploadFieldErrorComponent = ErrorComponent<'upload'> diff --git a/packages/payload/src/admin/fields/index.ts b/packages/payload/src/admin/fields/index.ts new file mode 100644 index 000000000..835ccf073 --- /dev/null +++ b/packages/payload/src/admin/fields/index.ts @@ -0,0 +1,86 @@ +import type { ArrayFieldProps } from './Array.js' +import type { BlocksFieldProps } from './Blocks.js' +import type { CheckboxFieldProps } from './Checkbox.js' +import type { CodeFieldProps } from './Code.js' +import type { CollapsibleFieldProps } from './Collapsible.js' +import type { DateFieldProps } from './Date.js' +import type { EmailFieldProps } from './Email.js' +import type { GroupFieldProps } from './Group.js' +import type { HiddenFieldProps } from './Hidden.js' +import type { JSONFieldProps } from './JSON.js' +import type { NumberFieldProps } from './Number.js' +import type { PointFieldProps } from './Point.js' +import type { RadioFieldProps } from './Radio.js' +import type { RelationshipFieldProps } from './Relationship.js' +import type { RichTextComponentProps } from './RichText.js' +import type { RowFieldProps } from './Row.js' +import type { SelectFieldProps } from './Select.js' +import type { TabsFieldProps } from './Tabs.js' +import type { TextFieldProps } from './Text.js' +import type { TextareaFieldProps } from './Textarea.js' +import type { UploadFieldProps } from './Upload.js' + +export type FieldComponentProps = + | ({ + type: 'array' + } & ArrayFieldProps) + | ({ + type: 'blocks' + } & BlocksFieldProps) + | ({ + type: 'checkbox' + } & CheckboxFieldProps) + | ({ + type: 'code' + } & CodeFieldProps) + | ({ + type: 'collapsible' + } & CollapsibleFieldProps) + | ({ + type: 'date' + } & DateFieldProps) + | ({ + type: 'email' + } & EmailFieldProps) + | ({ + type: 'group' + } & GroupFieldProps) + | ({ + type: 'hidden' + } & HiddenFieldProps) + | ({ + type: 'json' + } & JSONFieldProps) + | ({ + type: 'number' + } & NumberFieldProps) + | ({ + type: 'point' + } & PointFieldProps) + | ({ + type: 'radio' + } & RadioFieldProps) + | ({ + type: 'relationship' + } & RelationshipFieldProps) + | ({ + type: 'richText' + } & RichTextComponentProps) + | ({ + type: 'row' + } & RowFieldProps) + | ({ + type: 'select' + } & SelectFieldProps) + | ({ + type: 'tabs' + } & TabsFieldProps) + | ({ + type: 'text' + } & TextFieldProps) + | ({ + type: 'textarea' + } & TextareaFieldProps) + | ({ + type: 'upload' + } & UploadFieldProps) diff --git a/packages/payload/src/admin/forms/Error.ts b/packages/payload/src/admin/forms/Error.ts index 8d3472e05..cbff6e443 100644 --- a/packages/payload/src/admin/forms/Error.ts +++ b/packages/payload/src/admin/forms/Error.ts @@ -1,7 +1,19 @@ -export type ErrorProps = { +import type { CustomComponent, ServerProps } from '../../config/types.js' +import type { FieldComponentProps } from '../types.js' +import type { FieldTypes } from './FieldTypes.js' + +export type GenericErrorProps = { CustomError?: React.ReactNode alignCaret?: 'center' | 'left' | 'right' message?: string path?: string showError?: boolean } + +export type ErrorProps = { + type: T +} & FieldComponentProps & + GenericErrorProps & + Partial + +export type ErrorComponent = CustomComponent> diff --git a/packages/payload/src/admin/forms/Field.ts b/packages/payload/src/admin/forms/Field.ts new file mode 100644 index 000000000..2ce02a893 --- /dev/null +++ b/packages/payload/src/admin/forms/Field.ts @@ -0,0 +1,32 @@ +import type { User } from '../../auth/types.js' +import type { LabelStatic, Locale } from '../../config/types.js' +import type { Validate } from '../../fields/config/types.js' +import type { DocumentPreferences } from '../../preferences/types.js' +import type { ErrorProps } from './Error.js' +import type { FieldDescriptionProps } from './FieldDescription.js' +import type { SanitizedLabelProps } from './Label.js' + +export type FormFieldBase = { + AfterInput?: React.ReactNode + BeforeInput?: React.ReactNode + CustomDescription?: React.ReactNode + CustomError?: React.ReactNode + CustomLabel?: React.ReactNode + className?: string + custom?: Record + descriptionProps?: Omit + disabled?: boolean + docPreferences?: DocumentPreferences + errorProps?: Omit + label?: LabelStatic | false + labelProps?: SanitizedLabelProps + locale?: Locale + localized?: boolean + path?: string + readOnly?: boolean + required?: boolean + rtl?: boolean + style?: React.CSSProperties + user?: User + validate?: Validate +} diff --git a/packages/payload/src/admin/forms/FieldDescription.ts b/packages/payload/src/admin/forms/FieldDescription.ts index 23883aa19..f2e8e5726 100644 --- a/packages/payload/src/admin/forms/FieldDescription.ts +++ b/packages/payload/src/admin/forms/FieldDescription.ts @@ -1,18 +1,24 @@ import type React from 'react' -import type { CustomComponent, LabelFunction } from '../../config/types.js' -import type { Payload } from '../../index.js' +import type { CustomComponent, LabelFunction, ServerProps } from '../../config/types.js' +import type { FieldComponentProps } from '../types.js' +import type { FieldTypes } from './FieldTypes.js' export type DescriptionFunction = LabelFunction -export type DescriptionComponent = CustomComponent +export type DescriptionComponent = CustomComponent< + FieldDescriptionProps +> export type Description = DescriptionFunction | Record | string - -export type FieldDescriptionProps = { +export type GenericDescriptionProps = { CustomDescription?: React.ReactNode className?: string description?: Record | string marginPlacement?: 'bottom' | 'top' - payload?: Payload } +export type FieldDescriptionProps = { + type: T +} & FieldComponentProps & + GenericDescriptionProps & + Partial diff --git a/packages/payload/src/admin/forms/FieldMap.ts b/packages/payload/src/admin/forms/FieldMap.ts new file mode 100644 index 000000000..dfceaa6aa --- /dev/null +++ b/packages/payload/src/admin/forms/FieldMap.ts @@ -0,0 +1,24 @@ +import type { CellComponentProps, FieldComponentProps } from '../types.js' +import type { FieldTypes } from './FieldTypes.js' + +export type MappedField = { + CustomCell?: React.ReactNode + CustomField?: React.ReactNode + cellComponentProps: CellComponentProps + custom?: Record + disableBulkEdit?: boolean + disableListColumn?: boolean + disableListFilter?: boolean + disabled?: boolean + fieldComponentProps: FieldComponentProps + fieldIsPresentational: boolean + isFieldAffectingData: boolean + isHidden?: boolean + isSidebar?: boolean + localized: boolean + name?: string + type: keyof FieldTypes + unique?: boolean +} + +export type FieldMap = MappedField[] diff --git a/packages/payload/src/admin/forms/Label.ts b/packages/payload/src/admin/forms/Label.ts index 37ac85cbe..43497c20c 100644 --- a/packages/payload/src/admin/forms/Label.ts +++ b/packages/payload/src/admin/forms/Label.ts @@ -1,11 +1,24 @@ -export type LabelProps = { - CustomLabel?: React.ReactNode +import type { CustomComponent, ServerProps } from '../../config/types.js' +import type { FieldComponentProps } from '../fields/index.js' +import type { FormFieldBase } from './Field.js' +import type { FieldTypes } from './FieldTypes.js' + +export type GenericLabelProps = { as?: 'label' | 'span' htmlFor?: string - label?: Record | string - required?: boolean schemaPath?: string unstyled?: boolean -} +} & FormFieldBase -export type SanitizedLabelProps = Omit +export type LabelProps = { + type: T +} & FieldComponentProps & + GenericLabelProps & + Partial + +export type SanitizedLabelProps = Omit< + LabelProps, + 'label' | 'required' +> + +export type LabelComponent = CustomComponent> diff --git a/packages/payload/src/admin/types.ts b/packages/payload/src/admin/types.ts index ad37c5148..6a8919e54 100644 --- a/packages/payload/src/admin/types.ts +++ b/packages/payload/src/admin/types.ts @@ -7,6 +7,7 @@ export type { CustomPreviewButton } from './elements/PreviewButton.js' export type { CustomPublishButton } from './elements/PublishButton.js' export type { CustomSaveButton } from './elements/SaveButton.js' export type { CustomSaveDraftButton } from './elements/SaveDraftButton.js' + export type { DocumentTab, DocumentTabComponent, @@ -14,20 +15,191 @@ export type { DocumentTabConfig, DocumentTabProps, } from './elements/Tab.js' + export type { CustomUpload } from './elements/Upload.js' + export type { WithServerSidePropsComponent, WithServerSidePropsComponentProps, } from './elements/WithServerSideProps.js' -export type { ErrorProps } from './forms/Error.js' + +export type { + ArrayFieldDescriptionComponent, + ArrayFieldErrorComponent, + ArrayFieldLabelComponent, + ArrayFieldProps, +} from './fields/Array.js' + +export type { ReducedBlock } from './fields/Blocks.js' + +export type { + BlocksFieldDescriptionComponent, + BlocksFieldErrorComponent, + BlocksFieldLabelComponent, + BlocksFieldProps, +} from './fields/Blocks.js' + +export type { + CheckboxFieldDescriptionComponent, + CheckboxFieldErrorComponent, + CheckboxFieldLabelComponent, + CheckboxFieldProps, +} from './fields/Checkbox.js' + +export type { + CodeFieldDescriptionComponent, + CodeFieldErrorComponent, + CodeFieldLabelComponent, + CodeFieldProps, +} from './fields/Code.js' + +export type { + CollapsibleFieldDescriptionComponent, + CollapsibleFieldErrorComponent, + CollapsibleFieldLabelComponent, + CollapsibleFieldProps, +} from './fields/Collapsible.js' + +export type { + DateFieldDescriptionComponent, + DateFieldErrorComponent, + DateFieldLabelComponent, + DateFieldProps, +} from './fields/Date.js' + +export type { + EmailFieldDescriptionComponent, + EmailFieldErrorComponent, + EmailFieldLabelComponent, + EmailFieldProps, +} from './fields/Email.js' + +export type { + GroupFieldDescriptionComponent, + GroupFieldErrorComponent, + GroupFieldLabelComponent, + GroupFieldProps, +} from './fields/Group.js' + +export type { + HiddenFieldDescriptionComponent, + HiddenFieldErrorComponent, + HiddenFieldLabelComponent, + HiddenFieldProps, +} from './fields/Hidden.js' + +export type { + JSONFieldDescriptionComponent, + JSONFieldErrorComponent, + JSONFieldLabelComponent, + JSONFieldProps, +} from './fields/JSON.js' + +export type { + NumberFieldDescriptionComponent, + NumberFieldErrorComponent, + NumberFieldLabelComponent, + NumberFieldProps, +} from './fields/Number.js' + +export type { + PointFieldDescriptionComponent, + PointFieldErrorComponent, + PointFieldLabelComponent, + PointFieldProps, +} from './fields/Point.js' + +export type { + RadioFieldDescriptionComponent, + RadioFieldErrorComponent, + RadioFieldLabelComponent, + RadioFieldProps, +} from './fields/Radio.js' + +export type { + RelationshipFieldDescriptionComponent, + RelationshipFieldErrorComponent, + RelationshipFieldLabelComponent, + RelationshipFieldProps, +} from './fields/Relationship.js' + +export type { + RichTextComponentProps, + RichTextFieldDescriptionComponent, + RichTextFieldErrorComponent, + RichTextFieldLabelComponent, +} from './fields/RichText.js' + +export type { + RowFieldDescriptionComponent, + RowFieldErrorComponent, + RowFieldLabelComponent, + RowFieldProps, +} from './fields/Row.js' + +export type { + SelectFieldDescriptionComponent, + SelectFieldErrorComponent, + SelectFieldLabelComponent, + SelectFieldProps, +} from './fields/Select.js' + +export type { MappedTab } from './fields/Tabs.js' + +export type { + TabsFieldDescriptionComponent, + TabsFieldErrorComponent, + TabsFieldLabelComponent, + TabsFieldProps, +} from './fields/Tabs.js' + +export type { + TextFieldDescriptionComponent, + TextFieldErrorComponent, + TextFieldLabelComponent, + TextFieldProps, +} from './fields/Text.js' + +export type { + TextareaFieldDescriptionComponent, + TextareaFieldErrorComponent, + TextareaFieldLabelComponent, + TextareaFieldProps, +} from './fields/Textarea.js' + +export type { + UploadFieldDescriptionComponent, + UploadFieldErrorComponent, + UploadFieldLabelComponent, + UploadFieldProps, +} from './fields/Upload.js' + +export type { FieldComponentProps } from './fields/index.js' + +export type { ErrorComponent, ErrorProps, GenericErrorProps } from './forms/Error.js' + +export type { FormFieldBase } from './forms/Field.js' + export type { Description, DescriptionComponent, DescriptionFunction, FieldDescriptionProps, + GenericDescriptionProps, } from './forms/FieldDescription.js' + +export type { MappedField } from './forms/FieldMap.js' + +export type { FieldMap } from './forms/FieldMap.js' + export type { Data, FilterOptionsResult, FormField, FormState, Row } from './forms/Form.js' -export type { LabelProps, SanitizedLabelProps } from './forms/Label.js' + +export type { + GenericLabelProps, + LabelComponent, + LabelProps, + SanitizedLabelProps, +} from './forms/Label.js' export type { RowLabel, RowLabelComponent } from './forms/RowLabel.js' diff --git a/packages/payload/src/collections/config/types.ts b/packages/payload/src/collections/config/types.ts index adf299c88..6239b6ec3 100644 --- a/packages/payload/src/collections/config/types.ts +++ b/packages/payload/src/collections/config/types.ts @@ -23,6 +23,7 @@ import type { EntityDescriptionComponent, GeneratePreviewURL, LabelFunction, + LabelStatic, LivePreviewConfig, OpenGraphConfig, } from '../../config/types.js' @@ -438,8 +439,8 @@ export type CollectionConfig = { * Label configuration */ labels?: { - plural?: LabelFunction | Record | string - singular?: LabelFunction | Record | string + plural?: LabelFunction | LabelStatic + singular?: LabelFunction | LabelStatic } slug: string /** diff --git a/packages/payload/src/config/types.ts b/packages/payload/src/config/types.ts index b4a430651..d0eb0a9cc 100644 --- a/packages/payload/src/config/types.ts +++ b/packages/payload/src/config/types.ts @@ -423,6 +423,8 @@ export type LocalizationConfig = Prettify< export type LabelFunction = ({ t }: { t: TFunction }) => string +export type LabelStatic = Record | string + export type SharpDependency = ( input?: | ArrayBuffer diff --git a/packages/payload/src/fields/config/types.ts b/packages/payload/src/fields/config/types.ts index 5986440b4..611a1e588 100644 --- a/packages/payload/src/fields/config/types.ts +++ b/packages/payload/src/fields/config/types.ts @@ -9,16 +9,16 @@ import type { JSONSchema4 } from 'json-schema' import type React from 'react' import type { RichTextAdapter, RichTextAdapterProvider } from '../../admin/RichText.js' +import type { ErrorComponent } from '../../admin/forms/Error.js' import type { ConditionalDateProps, Description, DescriptionComponent, - ErrorProps, - LabelProps, + LabelComponent, RowLabelComponent, } from '../../admin/types.js' import type { SanitizedCollectionConfig, TypeWithID } from '../../collections/config/types.js' -import type { CustomComponent, LabelFunction } from '../../config/types.js' +import type { CustomComponent, LabelFunction, LabelStatic } from '../../config/types.js' import type { DBIdentifierName } from '../../database/types.js' import type { SanitizedGlobalConfig } from '../../globals/config/types.js' import type { CollectionSlug } from '../../index.js' @@ -171,8 +171,8 @@ type Admin = { } export type Labels = { - plural: LabelFunction | Record | string - singular: LabelFunction | Record | string + plural: LabelFunction | LabelStatic + singular: LabelFunction | LabelStatic } export type BaseValidateOptions = { @@ -203,7 +203,7 @@ export type Validate< export type ClientValidate = Omit export type OptionObject = { - label: LabelFunction | Record | string + label: LabelFunction | LabelStatic value: string } @@ -231,7 +231,7 @@ export interface FieldBase { beforeValidate?: FieldHook[] } index?: boolean - label?: LabelFunction | Record | false | string + label?: LabelFunction | LabelStatic | false localized?: boolean /** * The name of the field. Must be alphanumeric and cannot contain ' . ' @@ -256,8 +256,8 @@ export type NumberField = { /** Set this property to a string that will be used for browser autocomplete. */ autoComplete?: string components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent afterInput?: CustomComponent[] beforeInput?: CustomComponent[] } @@ -295,8 +295,8 @@ export type TextField = { admin?: { autoComplete?: string components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent afterInput?: CustomComponent[] beforeInput?: CustomComponent[] } @@ -330,8 +330,8 @@ export type EmailField = { admin?: { autoComplete?: string components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent afterInput?: CustomComponent[] beforeInput?: CustomComponent[] } @@ -343,8 +343,8 @@ export type EmailField = { export type TextareaField = { admin?: { components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent afterInput?: CustomComponent[] beforeInput?: CustomComponent[] } @@ -360,8 +360,8 @@ export type TextareaField = { export type CheckboxField = { admin?: { components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent afterInput?: CustomComponent[] beforeInput?: CustomComponent[] } @@ -372,8 +372,8 @@ export type CheckboxField = { export type DateField = { admin?: { components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent afterInput?: CustomComponent[] beforeInput?: CustomComponent[] } @@ -508,8 +508,8 @@ export type UIField = { export type UploadField = { admin?: { components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent } } filterOptions?: FilterOptions @@ -525,8 +525,8 @@ export type UploadField = { type CodeAdmin = { components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent } editorOptions?: EditorProps['options'] language?: string @@ -541,8 +541,8 @@ export type CodeField = { type JSONAdmin = { components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent } editorOptions?: EditorProps['options'] } & Admin @@ -560,8 +560,8 @@ export type JSONField = { export type SelectField = { admin?: { components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent } isClearable?: boolean isSortable?: boolean @@ -622,8 +622,8 @@ type SharedRelationshipProperties = { type RelationshipAdmin = { allowCreate?: boolean components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent } isSortable?: boolean } & Admin @@ -663,8 +663,8 @@ export type RichTextField< > = { admin?: { components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent } } & Admin editor?: @@ -712,8 +712,8 @@ export type ArrayField = { export type RadioField = { admin?: { components?: { - Error?: CustomComponent - Label?: CustomComponent + Error?: ErrorComponent + Label?: LabelComponent } layout?: 'horizontal' | 'vertical' } & Admin diff --git a/packages/plugin-form-builder/src/collections/Forms/DynamicFieldSelector.tsx b/packages/plugin-form-builder/src/collections/Forms/DynamicFieldSelector.tsx index 90dfdab22..351369bc4 100644 --- a/packages/plugin-form-builder/src/collections/Forms/DynamicFieldSelector.tsx +++ b/packages/plugin-form-builder/src/collections/Forms/DynamicFieldSelector.tsx @@ -1,6 +1,6 @@ 'use client' -import type { TextFieldProps } from '@payloadcms/ui' +import type { TextFieldProps } from 'payload' import { SelectField, useForm } from '@payloadcms/ui' import React, { useEffect, useState } from 'react' @@ -34,7 +34,5 @@ export const DynamicFieldSelector: React.FC = (props) => { } }, [fields, getDataByPath]) - // TODO: label from config is Record | false | string - // but the FormFieldBase type has only label?: string, changing FormFieldBase breaks other ui components return } diff --git a/packages/plugin-form-builder/src/collections/Forms/DynamicPriceSelector.tsx b/packages/plugin-form-builder/src/collections/Forms/DynamicPriceSelector.tsx index ea8bee6e5..a0d858203 100644 --- a/packages/plugin-form-builder/src/collections/Forms/DynamicPriceSelector.tsx +++ b/packages/plugin-form-builder/src/collections/Forms/DynamicPriceSelector.tsx @@ -1,7 +1,6 @@ 'use client' -import type { TextFieldProps } from '@payloadcms/ui' -import type { Data } from 'payload' +import type { Data, TextFieldProps } from 'payload' import { TextField, useLocale, useWatchForm } from '@payloadcms/ui' import React, { useEffect, useState } from 'react' diff --git a/packages/plugin-form-builder/src/collections/Forms/index.ts b/packages/plugin-form-builder/src/collections/Forms/index.ts index 404d4e19b..02362e9ec 100644 --- a/packages/plugin-form-builder/src/collections/Forms/index.ts +++ b/packages/plugin-form-builder/src/collections/Forms/index.ts @@ -2,7 +2,7 @@ import type { Block, CollectionConfig, Field } from 'payload' import { deepMergeWithSourceArrays } from 'payload' -import type { FieldConfig, FormBuilderPluginConfig } from '../../types.js' +import type { FormBuilderPluginConfig } from '../../types.js' import { fields } from './fields.js' diff --git a/packages/plugin-seo/src/fields/MetaDescription/MetaDescriptionComponent.tsx b/packages/plugin-seo/src/fields/MetaDescription/MetaDescriptionComponent.tsx index 826e7c0ab..a3c2d6782 100644 --- a/packages/plugin-seo/src/fields/MetaDescription/MetaDescriptionComponent.tsx +++ b/packages/plugin-seo/src/fields/MetaDescription/MetaDescriptionComponent.tsx @@ -1,6 +1,7 @@ 'use client' -import type { FieldType, FormFieldBase, Options } from '@payloadcms/ui' +import type { FieldType, Options } from '@payloadcms/ui' +import type { FormFieldBase } from 'payload' import { FieldLabel, @@ -82,7 +83,9 @@ export const MetaDescriptionComponent: React.FC = (props)   —   diff --git a/packages/ui/src/fields/Blocks/BlockRow.tsx b/packages/ui/src/fields/Blocks/BlockRow.tsx index 278fbe59a..e32282615 100644 --- a/packages/ui/src/fields/Blocks/BlockRow.tsx +++ b/packages/ui/src/fields/Blocks/BlockRow.tsx @@ -1,11 +1,10 @@ 'use client' -import type { FieldPermissions, Labels, Row } from 'payload' +import type { FieldPermissions, Labels, ReducedBlock, Row } from 'payload' import { getTranslation } from '@payloadcms/translations' import React from 'react' import type { UseDraggableSortableReturn } from '../../elements/DraggableSortable/useDraggableSortable/types.js' -import type { ReducedBlock } from '../../providers/ComponentMap/buildComponentMap/types.js' import { Collapsible } from '../../elements/Collapsible/index.js' import { ErrorPill } from '../../elements/ErrorPill/index.js' @@ -19,7 +18,7 @@ import { SectionTitle } from './SectionTitle/index.js' const baseClass = 'blocks-field' type BlockFieldProps = { - addRow: (rowIndex: number, blockType: string) => void + addRow: (rowIndex: number, blockType: string) => Promise | void block: ReducedBlock blocks: ReducedBlock[] duplicateRow: (rowIndex: number) => void diff --git a/packages/ui/src/fields/Blocks/BlocksDrawer/index.tsx b/packages/ui/src/fields/Blocks/BlocksDrawer/index.tsx index ad2b41df1..938742a0f 100644 --- a/packages/ui/src/fields/Blocks/BlocksDrawer/index.tsx +++ b/packages/ui/src/fields/Blocks/BlocksDrawer/index.tsx @@ -1,13 +1,11 @@ 'use client' import type { I18nClient } from '@payloadcms/translations' -import type { Labels } from 'payload' +import type { Labels, ReducedBlock } from 'payload' import { useModal } from '@faceless-ui/modal' import { getTranslation } from '@payloadcms/translations' import React, { useEffect, useState } from 'react' -import type { ReducedBlock } from '../../../providers/ComponentMap/buildComponentMap/types.js' - import { Drawer } from '../../../elements/Drawer/index.js' import { ThumbnailCard } from '../../../elements/ThumbnailCard/index.js' import { DefaultBlockImage } from '../../../graphics/DefaultBlockImage/index.js' @@ -16,7 +14,7 @@ import { BlockSearch } from './BlockSearch/index.js' import './index.scss' export type Props = { - addRow: (index: number, blockType?: string) => void + addRow: (index: number, blockType?: string) => Promise | void addRowIndex: number blocks: ReducedBlock[] drawerSlug: string @@ -76,7 +74,7 @@ export const BlocksDrawer: React.FC = (props) => { alignLabel="center" label={getTranslation(blockLabels?.singular, i18n)} onClick={() => { - addRow(addRowIndex, slug) + void addRow(addRowIndex, slug) closeModal(drawerSlug) }} thumbnail={ diff --git a/packages/ui/src/fields/Blocks/RowActions.tsx b/packages/ui/src/fields/Blocks/RowActions.tsx index bd9996983..a3ef63160 100644 --- a/packages/ui/src/fields/Blocks/RowActions.tsx +++ b/packages/ui/src/fields/Blocks/RowActions.tsx @@ -1,20 +1,15 @@ 'use client' -import type { Labels } from 'payload' +import type { FieldMap, Labels, ReducedBlock } from 'payload' import { useModal } from '@faceless-ui/modal' import React from 'react' -import type { - FieldMap, - ReducedBlock, -} from '../../providers/ComponentMap/buildComponentMap/types.js' - import { ArrayAction } from '../../elements/ArrayAction/index.js' import { useDrawerSlug } from '../../elements/Drawer/useDrawerSlug.js' import { BlocksDrawer } from './BlocksDrawer/index.js' export const RowActions: React.FC<{ - addRow: (rowIndex: number, blockType: string) => void + addRow: (rowIndex: number, blockType: string) => Promise | void blockType: string blocks: ReducedBlock[] duplicateRow: (rowIndex: number, blockType: string) => void @@ -51,7 +46,7 @@ export const RowActions: React.FC<{ { if (typeof addRow === 'function') { - addRow(indexToAdd, rowBlockType) + void addRow(indexToAdd, rowBlockType) } closeModal(drawerSlug) }} diff --git a/packages/ui/src/fields/Blocks/index.tsx b/packages/ui/src/fields/Blocks/index.tsx index a422afcfd..716db879f 100644 --- a/packages/ui/src/fields/Blocks/index.tsx +++ b/packages/ui/src/fields/Blocks/index.tsx @@ -1,12 +1,9 @@ 'use client' -import type { BlockField } from 'payload' +import type { BlocksFieldProps } from 'payload' import { getTranslation } from '@payloadcms/translations' import React, { Fragment, useCallback } from 'react' -import type { ReducedBlock } from '../../providers/ComponentMap/buildComponentMap/types.js' -import type { FormFieldBase } from '../shared/index.js' - import { Banner } from '../../elements/Banner/index.js' import { Button } from '../../elements/Button/index.js' import { DraggableSortableItem } from '../../elements/DraggableSortable/DraggableSortableItem/index.js' @@ -34,18 +31,6 @@ import './index.scss' const baseClass = 'blocks-field' -export type BlocksFieldProps = { - blocks?: ReducedBlock[] - forceRender?: boolean - isSortable?: boolean - labels?: BlockField['labels'] - maxRows?: number - minRows?: number - name?: string - slug?: string - width?: string -} & FormFieldBase - const BlocksFieldComponent: React.FC = (props) => { const { i18n, t } = useTranslation() @@ -132,7 +117,7 @@ const BlocksFieldComponent: React.FC = (props) => { const disabled = readOnlyFromProps || readOnlyFromContext || formProcessing || formInitializing const addRow = useCallback( - async (rowIndex: number, blockType: string) => { + async (rowIndex: number, blockType: string): Promise => { await addFieldRow({ data: { blockType }, path, @@ -278,7 +263,6 @@ const BlocksFieldComponent: React.FC = (props) => { {(draggableSortableItemProps) => ( = (props) => { - label?: LabelProps['label'] + label?: LabelProps<'checkbox'>['label'] labelProps?: SanitizedLabelProps name?: string onToggle: (event: React.ChangeEvent) => void diff --git a/packages/ui/src/fields/Checkbox/index.tsx b/packages/ui/src/fields/Checkbox/index.tsx index 5fdb7f713..6687a9939 100644 --- a/packages/ui/src/fields/Checkbox/index.tsx +++ b/packages/ui/src/fields/Checkbox/index.tsx @@ -1,10 +1,9 @@ 'use client' -import type { ClientValidate } from 'payload' +import type { CheckboxFieldProps, ClientValidate } from 'payload' import React, { useCallback } from 'react' import type { CheckboxInputProps } from './Input.js' -import type { CheckboxFieldProps } from './types.js' import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' import { useForm } from '../../forms/Form/context.js' diff --git a/packages/ui/src/fields/Checkbox/types.ts b/packages/ui/src/fields/Checkbox/types.ts deleted file mode 100644 index 5b26ebbde..000000000 --- a/packages/ui/src/fields/Checkbox/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { FormFieldBase } from '../shared/index.js' - -export type CheckboxFieldProps = { - checked?: boolean - disableFormData?: boolean - id?: string - name?: string - onChange?: (val: boolean) => void - partialChecked?: boolean - path?: string - width?: string -} & FormFieldBase diff --git a/packages/ui/src/fields/Code/index.tsx b/packages/ui/src/fields/Code/index.tsx index c97168ffa..5ae3fd58a 100644 --- a/packages/ui/src/fields/Code/index.tsx +++ b/packages/ui/src/fields/Code/index.tsx @@ -1,10 +1,8 @@ 'use client' -import type { CodeField as CodeFieldType } from 'payload' +import type { CodeFieldProps } from 'payload' import React, { useCallback } from 'react' -import type { FormFieldBase } from '../shared/index.js' - import { CodeEditor } from '../../elements/CodeEditor/index.js' import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' import { useField } from '../../forms/useField/index.js' @@ -15,14 +13,6 @@ import { FieldLabel } from '../FieldLabel/index.js' import { fieldBaseClass } from '../shared/index.js' import './index.scss' -export type CodeFieldProps = { - editorOptions?: CodeFieldType['admin']['editorOptions'] - language?: CodeFieldType['admin']['language'] - name?: string - path?: string - width: string -} & FormFieldBase - const prismToMonacoLanguageMap = { js: 'javascript', ts: 'typescript', diff --git a/packages/ui/src/fields/Collapsible/index.tsx b/packages/ui/src/fields/Collapsible/index.tsx index 072ac604e..8e5a2242d 100644 --- a/packages/ui/src/fields/Collapsible/index.tsx +++ b/packages/ui/src/fields/Collapsible/index.tsx @@ -1,5 +1,5 @@ 'use client' -import type { DocumentPreferences, FieldPermissions } from 'payload' +import type { CollapsibleFieldProps, DocumentPreferences } from 'payload' import React, { Fragment, useCallback, useEffect, useState } from 'react' @@ -18,18 +18,9 @@ import './index.scss' const baseClass = 'collapsible-field' -import type { FieldMap } from '../../providers/ComponentMap/buildComponentMap/types.js' -import type { FormFieldBase } from '../shared/index.js' - import { useFormInitializing, useFormProcessing } from '../../forms/Form/context.js' import { FieldDescription } from '../FieldDescription/index.js' -export type CollapsibleFieldProps = { - fieldMap: FieldMap - initCollapsed?: boolean - width?: string -} & FormFieldBase - const CollapsibleFieldComponent: React.FC = (props) => { const { CustomDescription, @@ -65,7 +56,7 @@ const CollapsibleFieldComponent: React.FC = (props) => { const fieldHasErrors = errorCount > 0 const onToggle = useCallback( - async (newCollapsedState: boolean) => { + async (newCollapsedState: boolean): Promise => { const existingPreferences: DocumentPreferences = await getPreference(preferencesKey) if (preferencesKey) { @@ -145,7 +136,6 @@ const CollapsibleFieldComponent: React.FC = (props) => { } initCollapsed={collapsedOnMount} - // eslint-disable-next-line @typescript-eslint/no-misused-promises onToggle={onToggle} > = (props) => { const { name, diff --git a/packages/ui/src/fields/Email/index.tsx b/packages/ui/src/fields/Email/index.tsx index 330974011..5e4be3ca0 100644 --- a/packages/ui/src/fields/Email/index.tsx +++ b/packages/ui/src/fields/Email/index.tsx @@ -1,11 +1,9 @@ 'use client' -import type { ClientValidate, EmailField as EmailFieldType } from 'payload' +import type { ClientValidate, EmailFieldProps } from 'payload' import { getTranslation } from '@payloadcms/translations' import React, { useCallback } from 'react' -import type { FormFieldBase } from '../shared/index.js' - import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' import { useField } from '../../forms/useField/index.js' import { withCondition } from '../../forms/withCondition/index.js' @@ -16,14 +14,6 @@ import { FieldLabel } from '../FieldLabel/index.js' import { fieldBaseClass } from '../shared/index.js' import './index.scss' -export type EmailFieldProps = { - autoComplete?: string - name?: string - path?: string - placeholder?: EmailFieldType['admin']['placeholder'] - width?: string -} & FormFieldBase - const EmailFieldComponent: React.FC = (props) => { const { name, diff --git a/packages/ui/src/fields/FieldDescription/index.tsx b/packages/ui/src/fields/FieldDescription/index.tsx index 3c195f2eb..c7988265c 100644 --- a/packages/ui/src/fields/FieldDescription/index.tsx +++ b/packages/ui/src/fields/FieldDescription/index.tsx @@ -1,5 +1,5 @@ 'use client' -import type { FieldDescriptionProps } from 'payload' +import type { GenericDescriptionProps } from 'payload' import { getTranslation } from '@payloadcms/translations' import React from 'react' @@ -10,7 +10,7 @@ import './index.scss' const baseClass = 'field-description' -const DefaultFieldDescription: React.FC = (props) => { +const DefaultFieldDescription: React.FC = (props) => { const { className, description, marginPlacement } = props const { path } = useFieldProps() @@ -37,7 +37,7 @@ const DefaultFieldDescription: React.FC = (props) => { return null } -export const FieldDescription: React.FC = (props) => { +export const FieldDescription: React.FC = (props) => { const { CustomDescription } = props if (CustomDescription !== undefined) { diff --git a/packages/ui/src/fields/FieldError/index.tsx b/packages/ui/src/fields/FieldError/index.tsx index 49178cf61..71a303626 100644 --- a/packages/ui/src/fields/FieldError/index.tsx +++ b/packages/ui/src/fields/FieldError/index.tsx @@ -1,6 +1,6 @@ 'use client' -import type { ErrorProps } from 'payload' +import type { GenericErrorProps } from 'payload' import React from 'react' @@ -11,7 +11,7 @@ import './index.scss' const baseClass = 'field-error' -const DefaultFieldError: React.FC = (props) => { +const DefaultFieldError: React.FC = (props) => { const { alignCaret = 'right', message: messageFromProps, @@ -41,7 +41,7 @@ const DefaultFieldError: React.FC = (props) => { return null } -export const FieldError: React.FC = (props) => { +export const FieldError: React.FC = (props) => { const { CustomError } = props if (CustomError !== undefined) { diff --git a/packages/ui/src/fields/FieldLabel/index.tsx b/packages/ui/src/fields/FieldLabel/index.tsx index 2cb2dafc3..3f6a56864 100644 --- a/packages/ui/src/fields/FieldLabel/index.tsx +++ b/packages/ui/src/fields/FieldLabel/index.tsx @@ -1,6 +1,6 @@ 'use client' -import type { LabelProps } from 'payload' +import type { GenericLabelProps } from 'payload' import { getTranslation } from '@payloadcms/translations' import React from 'react' @@ -12,7 +12,7 @@ import { useTranslation } from '../../providers/Translation/index.js' import { generateFieldID } from '../../utilities/generateFieldID.js' import './index.scss' -const DefaultFieldLabel: React.FC = (props) => { +const DefaultFieldLabel: React.FC = (props) => { const { as: Element = 'label', htmlFor: htmlForFromProps, @@ -40,7 +40,7 @@ const DefaultFieldLabel: React.FC = (props) => { return null } -export const FieldLabel: React.FC = (props) => { +export const FieldLabel: React.FC = (props) => { const { CustomLabel } = props if (CustomLabel !== undefined) { diff --git a/packages/ui/src/fields/Group/index.tsx b/packages/ui/src/fields/Group/index.tsx index d10a07e90..fb9206819 100644 --- a/packages/ui/src/fields/Group/index.tsx +++ b/packages/ui/src/fields/Group/index.tsx @@ -1,11 +1,10 @@ 'use client' +import type { GroupFieldProps } from 'payload' + import { getTranslation } from '@payloadcms/translations' import React, { Fragment } from 'react' -import type { FieldMap } from '../../providers/ComponentMap/buildComponentMap/types.js' -import type { FormFieldBase } from '../shared/index.js' - import { useCollapsible } from '../../elements/Collapsible/provider.js' import { ErrorPill } from '../../elements/ErrorPill/index.js' import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' @@ -27,15 +26,7 @@ import { GroupProvider, useGroup } from './provider.js' const baseClass = 'group-field' -export type GroupFieldProps = { - fieldMap: FieldMap - forceRender?: boolean - hideGutter?: boolean - name?: string - width?: string -} & FormFieldBase - -const GroupFieldComponent: React.FC = (props) => { +export const GroupFieldComponent: React.FC = (props) => { const { CustomDescription, CustomLabel, diff --git a/packages/ui/src/fields/Hidden/index.tsx b/packages/ui/src/fields/Hidden/index.tsx index e07d27869..49568c8a9 100644 --- a/packages/ui/src/fields/Hidden/index.tsx +++ b/packages/ui/src/fields/Hidden/index.tsx @@ -1,25 +1,18 @@ 'use client' -import React, { useEffect } from 'react' -import type { FormFieldBase } from '../index.js' +import type { HiddenFieldProps } from 'payload' + +import React, { useEffect } from 'react' import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' import { useField } from '../../forms/useField/index.js' import { withCondition } from '../../forms/withCondition/index.js' -export type HiddenInputFieldProps = { - disableModifyingForm?: false - forceUsePathFromProps?: boolean - name?: string - path?: string - value?: unknown -} & FormFieldBase - /** * This is mainly used to save a value on the form that is not visible to the user. * For example, this sets the `ìd` property of a block in the Blocks field. */ -const HiddenFieldComponent: React.FC = (props) => { +const HiddenFieldComponent: React.FC = (props) => { const { name, disableModifyingForm = true, diff --git a/packages/ui/src/fields/JSON/index.tsx b/packages/ui/src/fields/JSON/index.tsx index 6bc10d2a7..240810727 100644 --- a/packages/ui/src/fields/JSON/index.tsx +++ b/packages/ui/src/fields/JSON/index.tsx @@ -1,5 +1,5 @@ 'use client' -import type { ClientValidate, JSONField as JSONFieldType } from 'payload' +import type { ClientValidate, JSONFieldProps } from 'payload' import React, { useCallback, useEffect, useState } from 'react' @@ -12,20 +12,10 @@ import './index.scss' const baseClass = 'json-field' -import type { FormFieldBase } from '../shared/index.js' - import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' import { FieldDescription } from '../FieldDescription/index.js' import { FieldError } from '../FieldError/index.js' -export type JSONFieldProps = { - editorOptions?: JSONFieldType['admin']['editorOptions'] - jsonSchema?: Record - name?: string - path?: string - width?: string -} & FormFieldBase - const JSONFieldComponent: React.FC = (props) => { const { name, diff --git a/packages/ui/src/fields/Number/index.tsx b/packages/ui/src/fields/Number/index.tsx index 026bc8853..5c2c72920 100644 --- a/packages/ui/src/fields/Number/index.tsx +++ b/packages/ui/src/fields/Number/index.tsx @@ -1,12 +1,11 @@ 'use client' -import type { NumberField as NumberFieldType } from 'payload' +import type { NumberFieldProps } from 'payload' import { getTranslation } from '@payloadcms/translations' import { isNumber } from 'payload/shared' import React, { useCallback, useEffect, useState } from 'react' import type { Option } from '../../elements/ReactSelect/types.js' -import type { FormFieldBase } from '../shared/index.js' import { ReactSelect } from '../../elements/ReactSelect/index.js' import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' @@ -19,19 +18,6 @@ import { FieldLabel } from '../FieldLabel/index.js' import { fieldBaseClass } from '../shared/index.js' import './index.scss' -export type NumberFieldProps = { - hasMany?: boolean - max?: number - maxRows?: number - min?: number - name?: string - onChange?: (e: number) => void - path?: string - placeholder?: NumberFieldType['admin']['placeholder'] - step?: number - width?: string -} & FormFieldBase - const NumberFieldComponent: React.FC = (props) => { const { name, diff --git a/packages/ui/src/fields/Password/index.tsx b/packages/ui/src/fields/Password/index.tsx index 161d4fe8c..353a5ee94 100644 --- a/packages/ui/src/fields/Password/index.tsx +++ b/packages/ui/src/fields/Password/index.tsx @@ -1,10 +1,8 @@ 'use client' -import type { ClientValidate, Description, Validate } from 'payload' +import type { ClientValidate, Description, FormFieldBase , Validate } from 'payload' import React, { useCallback } from 'react' -import type { FormFieldBase } from '../shared/index.js' - import { useField } from '../../forms/useField/index.js' import { withCondition } from '../../forms/withCondition/index.js' import { FieldError } from '../FieldError/index.js' diff --git a/packages/ui/src/fields/Point/index.tsx b/packages/ui/src/fields/Point/index.tsx index 1a568a737..7fc951c5b 100644 --- a/packages/ui/src/fields/Point/index.tsx +++ b/packages/ui/src/fields/Point/index.tsx @@ -1,5 +1,5 @@ 'use client' -import type { ClientValidate } from 'payload' +import type { ClientValidate, PointFieldProps } from 'payload' import { getTranslation } from '@payloadcms/translations' import React, { useCallback } from 'react' @@ -12,22 +12,12 @@ import './index.scss' const baseClass = 'point' -import type { FormFieldBase } from '../shared/index.js' - import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' import { FieldDescription } from '../FieldDescription/index.js' import { FieldError } from '../FieldError/index.js' import { FieldLabel } from '../FieldLabel/index.js' -export type PointFieldProps = { - name?: string - path?: string - placeholder?: string - step?: number - width?: string -} & FormFieldBase - -const PointFieldComponent: React.FC = (props) => { +export const PointFieldComponent: React.FC = (props) => { const { name, AfterInput, diff --git a/packages/ui/src/fields/RadioGroup/Radio/index.tsx b/packages/ui/src/fields/RadioGroup/Radio/index.tsx index cfffb46ff..86ea5fa2f 100644 --- a/packages/ui/src/fields/RadioGroup/Radio/index.tsx +++ b/packages/ui/src/fields/RadioGroup/Radio/index.tsx @@ -1,11 +1,9 @@ 'use client' -import type { OptionObject } from 'payload' +import type { OptionObject, RadioFieldProps } from 'payload' import { getTranslation } from '@payloadcms/translations' import React from 'react' -import type { OnChange } from '../index.js' - import { useEditDepth } from '../../../providers/EditDepth/index.js' import { useTranslation } from '../../../providers/Translation/index.js' import './index.scss' @@ -15,7 +13,7 @@ const baseClass = 'radio-input' export const Radio: React.FC<{ id: string isSelected: boolean - onChange: OnChange + onChange: RadioFieldProps['onChange'] option: OptionObject path: string readOnly?: boolean diff --git a/packages/ui/src/fields/RadioGroup/index.tsx b/packages/ui/src/fields/RadioGroup/index.tsx index e1660a0a4..671ae5efa 100644 --- a/packages/ui/src/fields/RadioGroup/index.tsx +++ b/packages/ui/src/fields/RadioGroup/index.tsx @@ -1,5 +1,5 @@ 'use client' -import type { Option } from 'payload' +import type { RadioFieldProps } from 'payload' import { optionIsObject } from 'payload/shared' import React, { useCallback } from 'react' @@ -14,24 +14,10 @@ import './index.scss' const baseClass = 'radio-group' -import type { FormFieldBase } from '../shared/index.js' - import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' import { FieldDescription } from '../FieldDescription/index.js' import { FieldError } from '../FieldError/index.js' -export type RadioFieldProps = { - layout?: 'horizontal' | 'vertical' - name?: string - onChange?: OnChange - options?: Option[] - path?: string - value?: string - width?: string -} & FormFieldBase - -export type OnChange = (value: T) => void - const RadioGroupFieldComponent: React.FC = (props) => { const { name, diff --git a/packages/ui/src/fields/Relationship/index.tsx b/packages/ui/src/fields/Relationship/index.tsx index 4823b16eb..42000cae4 100644 --- a/packages/ui/src/fields/Relationship/index.tsx +++ b/packages/ui/src/fields/Relationship/index.tsx @@ -1,12 +1,12 @@ 'use client' -import type { PaginatedDocs, Where } from 'payload' +import type { PaginatedDocs, RelationshipFieldProps, Where } from 'payload' import { wordBoundariesRegex } from 'payload/shared' import * as qs from 'qs-esm' import React, { useCallback, useEffect, useReducer, useRef, useState } from 'react' import type { DocumentDrawerProps } from '../../elements/DocumentDrawer/types.js' -import type { GetResults, Option, RelationshipFieldProps, Value } from './types.js' +import type { GetResults, Option, Value } from './types.js' import { ReactSelect } from '../../elements/ReactSelect/index.js' import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' @@ -33,8 +33,6 @@ const maxResultsPerRequest = 10 const baseClass = 'relationship' -export { RelationshipFieldProps } - const RelationshipFieldComponent: React.FC = (props) => { const { name, diff --git a/packages/ui/src/fields/Relationship/types.ts b/packages/ui/src/fields/Relationship/types.ts index 5b41d4dd2..5d2e95948 100644 --- a/packages/ui/src/fields/Relationship/types.ts +++ b/packages/ui/src/fields/Relationship/types.ts @@ -1,17 +1,5 @@ import type { I18nClient } from '@payloadcms/translations' -import type { ClientCollectionConfig, RelationshipField, SanitizedConfig } from 'payload' - -import type { FormFieldBase } from '../shared/index.js' - -export type RelationshipFieldProps = { - allowCreate?: RelationshipField['admin']['allowCreate'] - hasMany?: boolean - isSortable?: boolean - name: string - relationTo?: RelationshipField['relationTo'] - sortOptions?: RelationshipField['admin']['sortOptions'] - width?: string -} & FormFieldBase +import type { ClientCollectionConfig, SanitizedConfig } from 'payload' export type Option = { label: string diff --git a/packages/ui/src/fields/RichText/index.tsx b/packages/ui/src/fields/RichText/index.tsx index 42effa138..7cc03c87d 100644 --- a/packages/ui/src/fields/RichText/index.tsx +++ b/packages/ui/src/fields/RichText/index.tsx @@ -1,14 +1,6 @@ +import type { RichTextComponentProps } from 'payload' import type React from 'react' -import type { MappedField } from '../../providers/ComponentMap/buildComponentMap/types.js' -import type { FormFieldBase } from '../shared/index.js' - -export type RichTextFieldProps = { - name: string - richTextComponentMap?: Map - width?: string -} & FormFieldBase - -export const RichTextField: React.FC = () => { +export const RichTextField: React.FC = () => { return null } diff --git a/packages/ui/src/fields/Row/index.tsx b/packages/ui/src/fields/Row/index.tsx index 45af47741..d60ea75a7 100644 --- a/packages/ui/src/fields/Row/index.tsx +++ b/packages/ui/src/fields/Row/index.tsx @@ -1,7 +1,7 @@ 'use client' -import React from 'react' +import type { RowFieldProps } from 'payload' -import type { RowFieldProps } from './types.js' +import React from 'react' import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' import { RenderFields } from '../../forms/RenderFields/index.js' diff --git a/packages/ui/src/fields/Row/types.ts b/packages/ui/src/fields/Row/types.ts deleted file mode 100644 index 940bb2b64..000000000 --- a/packages/ui/src/fields/Row/types.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { FieldPermissions } from 'payload' - -import type { FieldMap } from '../../providers/ComponentMap/buildComponentMap/types.js' -import type { FormFieldBase } from '../shared/index.js' - -export type RowFieldProps = { - fieldMap: FieldMap - forceRender?: boolean - indexPath: string - path?: string - permissions?: FieldPermissions - width?: string -} & FormFieldBase diff --git a/packages/ui/src/fields/Select/Input.tsx b/packages/ui/src/fields/Select/Input.tsx index 689363190..844aef598 100644 --- a/packages/ui/src/fields/Select/Input.tsx +++ b/packages/ui/src/fields/Select/Input.tsx @@ -1,11 +1,10 @@ 'use client' -import type { OptionObject } from 'payload' +import type { OptionObject, SelectFieldProps } from 'payload' import { getTranslation } from '@payloadcms/translations' import React from 'react' import type { ReactSelectAdapterProps } from '../../elements/ReactSelect/types.js' -import type { SelectFieldProps } from './index.js' import { ReactSelect } from '../../elements/ReactSelect/index.js' import { useTranslation } from '../../providers/Translation/index.js' diff --git a/packages/ui/src/fields/Select/index.tsx b/packages/ui/src/fields/Select/index.tsx index ea3647c3f..715beab5e 100644 --- a/packages/ui/src/fields/Select/index.tsx +++ b/packages/ui/src/fields/Select/index.tsx @@ -1,10 +1,9 @@ 'use client' -import type { ClientValidate, Option, OptionObject } from 'payload' +import type { ClientValidate, Option, OptionObject, SelectFieldProps } from 'payload' import React, { useCallback } from 'react' import type { ReactSelectAdapterProps } from '../../elements/ReactSelect/types.js' -import type { FormFieldBase } from '../shared/index.js' import type { SelectInputProps } from './Input.js' import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' @@ -12,18 +11,6 @@ import { useField } from '../../forms/useField/index.js' import { withCondition } from '../../forms/withCondition/index.js' import { SelectInput } from './Input.js' -export type SelectFieldProps = { - hasMany?: boolean - isClearable?: boolean - isSortable?: boolean - name?: string - onChange?: (e: string | string[]) => void - options?: Option[] - path?: string - value?: string - width?: string -} & FormFieldBase - const formatOptions = (options: Option[]): OptionObject[] => options.map((option) => { if (typeof option === 'object' && (option.value || option.value === '')) { diff --git a/packages/ui/src/fields/Tabs/Tab/index.tsx b/packages/ui/src/fields/Tabs/Tab/index.tsx index 1c755ff8f..93b391932 100644 --- a/packages/ui/src/fields/Tabs/Tab/index.tsx +++ b/packages/ui/src/fields/Tabs/Tab/index.tsx @@ -1,9 +1,9 @@ 'use client' +import type { MappedTab } from 'payload' + import { getTranslation } from '@payloadcms/translations' import React, { useState } from 'react' -import type { MappedTab } from '../../../providers/ComponentMap/buildComponentMap/types.js' - import { ErrorPill } from '../../../elements/ErrorPill/index.js' import { WatchChildErrors } from '../../../forms/WatchChildErrors/index.js' import { useTranslation } from '../../../providers/Translation/index.js' diff --git a/packages/ui/src/fields/Tabs/index.tsx b/packages/ui/src/fields/Tabs/index.tsx index dc77a1bea..60f93f184 100644 --- a/packages/ui/src/fields/Tabs/index.tsx +++ b/packages/ui/src/fields/Tabs/index.tsx @@ -1,13 +1,10 @@ 'use client' -import type { DocumentPreferences } from 'payload' +import type { DocumentPreferences, TabsFieldProps } from 'payload' import { getTranslation } from '@payloadcms/translations' import { toKebabCase } from 'payload/shared' import React, { useCallback, useEffect, useState } from 'react' -import type { MappedTab } from '../../providers/ComponentMap/buildComponentMap/types.js' -import type { FormFieldBase } from '../shared/index.js' - import { useCollapsible } from '../../elements/Collapsible/provider.js' import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' import { RenderFields } from '../../forms/RenderFields/index.js' @@ -25,14 +22,6 @@ const baseClass = 'tabs-field' export { TabsProvider } -export type TabsFieldProps = { - forceRender?: boolean - name?: string - path?: string - tabs?: MappedTab[] - width?: string -} & FormFieldBase - const TabsFieldComponent: React.FC = (props) => { const { name, @@ -76,7 +65,7 @@ const TabsFieldComponent: React.FC = (props) => { }, [path, getPreference, preferencesKey, tabsPrefKey]) const handleTabChange = useCallback( - async (incomingTabIndex: number) => { + async (incomingTabIndex: number): Promise => { setActiveTabIndex(incomingTabIndex) const existingPreferences: DocumentPreferences = await getPreference(preferencesKey) @@ -142,7 +131,9 @@ const TabsFieldComponent: React.FC = (props) => { isActive={activeTabIndex === tabIndex} key={tabIndex} parentPath={path} - setIsActive={() => void handleTabChange(tabIndex)} + setIsActive={() => { + void handleTabChange(tabIndex) + }} tab={tab} /> ) diff --git a/packages/ui/src/fields/Text/index.tsx b/packages/ui/src/fields/Text/index.tsx index 2458c1eb9..99d6fe36a 100644 --- a/packages/ui/src/fields/Text/index.tsx +++ b/packages/ui/src/fields/Text/index.tsx @@ -1,10 +1,10 @@ 'use client' -import type { ClientValidate } from 'payload' +import type { ClientValidate, TextFieldProps } from 'payload' import React, { useCallback, useEffect, useState } from 'react' import type { Option } from '../../elements/ReactSelect/types.js' -import type { TextFieldProps, TextInputProps } from './types.js' +import type { TextInputProps } from './types.js' import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' import { useField } from '../../forms/useField/index.js' @@ -15,7 +15,7 @@ import { isFieldRTL } from '../shared/index.js' import { TextInput } from './Input.js' import './index.scss' -export { TextFieldProps, TextInput, TextInputProps } +export { TextInput, TextInputProps } const TextFieldComponent: React.FC = (props) => { const { diff --git a/packages/ui/src/fields/Text/types.ts b/packages/ui/src/fields/Text/types.ts index 74d9f1870..2d60f0dba 100644 --- a/packages/ui/src/fields/Text/types.ts +++ b/packages/ui/src/fields/Text/types.ts @@ -1,22 +1,7 @@ -import type { TextField } from 'payload' +import type { TextFieldProps } from 'payload' import type { ChangeEvent } from 'react' import type { Option, ReactSelectAdapterProps } from '../../elements/ReactSelect/types.js' -import type { FormFieldBase } from '../shared/index.js' - -export type TextFieldProps = { - hasMany?: boolean - inputRef?: React.MutableRefObject - maxLength?: number - maxRows?: number - minLength?: number - minRows?: number - name?: string - onKeyDown?: React.KeyboardEventHandler - path?: string - placeholder?: TextField['admin']['placeholder'] - width?: string -} & FormFieldBase export type SharedTextFieldProps = | { diff --git a/packages/ui/src/fields/Textarea/index.tsx b/packages/ui/src/fields/Textarea/index.tsx index 5bf8dbfb9..2d61204a2 100644 --- a/packages/ui/src/fields/Textarea/index.tsx +++ b/packages/ui/src/fields/Textarea/index.tsx @@ -1,10 +1,10 @@ 'use client' -import type { ClientValidate } from 'payload' +import type { ClientValidate, TextareaFieldProps } from 'payload' import { getTranslation } from '@payloadcms/translations' import React, { useCallback } from 'react' -import type { TextAreaInputProps, TextareaFieldProps } from './types.js' +import type { TextAreaInputProps } from './types.js' import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' import { useField } from '../../forms/useField/index.js' @@ -15,7 +15,7 @@ import { isFieldRTL } from '../shared/index.js' import { TextareaInput } from './Input.js' import './index.scss' -export { TextAreaInputProps, TextareaFieldProps, TextareaInput } +export { TextAreaInputProps, TextareaInput } const TextareaFieldComponent: React.FC = (props) => { const { diff --git a/packages/ui/src/fields/Textarea/types.ts b/packages/ui/src/fields/Textarea/types.ts index f597a20d2..b33a83aba 100644 --- a/packages/ui/src/fields/Textarea/types.ts +++ b/packages/ui/src/fields/Textarea/types.ts @@ -1,19 +1,7 @@ -import type { TextareaField as TextareaFieldType } from 'payload' +import type { TextareaFieldProps } from 'payload' import { type ChangeEvent } from 'react' -import type { FormFieldBase } from '../shared/index.js' - -export type TextareaFieldProps = { - maxLength?: number - minLength?: number - name?: string - path?: string - placeholder?: TextareaFieldType['admin']['placeholder'] - rows?: number - width?: string -} & FormFieldBase - export type TextAreaInputProps = { onChange?: (e: ChangeEvent) => void rows?: number diff --git a/packages/ui/src/fields/Upload/Input.tsx b/packages/ui/src/fields/Upload/Input.tsx index c5aab5f2f..c207349e4 100644 --- a/packages/ui/src/fields/Upload/Input.tsx +++ b/packages/ui/src/fields/Upload/Input.tsx @@ -1,13 +1,17 @@ 'use client' -import type { ClientCollectionConfig, FilterOptionsResult, UploadField } from 'payload' +import type { + ClientCollectionConfig, + FilterOptionsResult, + UploadField, + UploadFieldProps, +} from 'payload' import { getTranslation } from '@payloadcms/translations' import React, { useCallback, useEffect, useState } from 'react' import type { DocumentDrawerProps } from '../../elements/DocumentDrawer/types.js' import type { ListDrawerProps } from '../../elements/ListDrawer/types.js' -import type { UploadFieldProps } from './types.js' import { Button } from '../../elements/Button/index.js' import { useDocumentDrawer } from '../../elements/DocumentDrawer/index.js' diff --git a/packages/ui/src/fields/Upload/index.tsx b/packages/ui/src/fields/Upload/index.tsx index 7ee3cd001..363f044f0 100644 --- a/packages/ui/src/fields/Upload/index.tsx +++ b/packages/ui/src/fields/Upload/index.tsx @@ -1,9 +1,10 @@ 'use client' +import type { UploadFieldProps } from 'payload' + import React, { useCallback, useMemo } from 'react' import type { UploadInputProps } from './Input.js' -import type { UploadFieldProps } from './types.js' import { useFieldProps } from '../../forms/FieldPropsProvider/index.js' import { useField } from '../../forms/useField/index.js' diff --git a/packages/ui/src/fields/Upload/types.ts b/packages/ui/src/fields/Upload/types.ts deleted file mode 100644 index 91bd277ca..000000000 --- a/packages/ui/src/fields/Upload/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { UploadField } from 'payload' - -import type { FormFieldBase } from '../shared/index.js' - -export type UploadFieldProps = { - filterOptions?: UploadField['filterOptions'] - name?: string - path?: string - relationTo?: UploadField['relationTo'] - width?: string -} & FormFieldBase diff --git a/packages/ui/src/fields/shared/index.tsx b/packages/ui/src/fields/shared/index.tsx index 82ed8b5c5..726361044 100644 --- a/packages/ui/src/fields/shared/index.tsx +++ b/packages/ui/src/fields/shared/index.tsx @@ -1,42 +1,7 @@ -import type { - DocumentPreferences, - ErrorProps, - FieldDescriptionProps, - LabelProps, - Locale, - SanitizedLabelProps, - SanitizedLocalizationConfig, - User, - Validate, -} from 'payload' +import type { Locale, SanitizedLocalizationConfig } from 'payload' export const fieldBaseClass = 'field-type' -export type FormFieldBase = { - AfterInput?: React.ReactNode - BeforeInput?: React.ReactNode - CustomDescription?: React.ReactNode - CustomError?: React.ReactNode - CustomLabel?: React.ReactNode - className?: string - custom?: Record - descriptionProps?: FieldDescriptionProps - disabled?: boolean - docPreferences?: DocumentPreferences - errorProps?: ErrorProps - label?: LabelProps['label'] - labelProps?: SanitizedLabelProps - locale?: Locale - localized?: boolean - path?: string - readOnly?: boolean - required?: boolean - rtl?: boolean - style?: React.CSSProperties - user?: User - validate?: Validate -} - /** * Determines whether a field should be displayed as right-to-left (RTL) based on its configuration, payload's localization configuration and the adming user's currently enabled locale. diff --git a/packages/ui/src/forms/Form/createNestedFieldPath.ts b/packages/ui/src/forms/Form/createNestedFieldPath.ts index 3be2c9150..fcfdef69b 100644 --- a/packages/ui/src/forms/Form/createNestedFieldPath.ts +++ b/packages/ui/src/forms/Form/createNestedFieldPath.ts @@ -1,9 +1,7 @@ -import type { Field } from 'payload' +import type { Field, MappedField } from 'payload' import { fieldAffectsData } from 'payload/shared' -import type { MappedField } from '../../providers/ComponentMap/buildComponentMap/types.js' - export const createNestedFieldPath = (parentPath: string, field: Field): string => { if (parentPath) { if (fieldAffectsData(field)) { diff --git a/packages/ui/src/forms/RenderFields/RenderField.tsx b/packages/ui/src/forms/RenderFields/RenderField.tsx index 8d0aff7e7..63a76030c 100644 --- a/packages/ui/src/forms/RenderFields/RenderField.tsx +++ b/packages/ui/src/forms/RenderFields/RenderField.tsx @@ -1,14 +1,9 @@ 'use client' -import type { FieldPermissions, FieldTypes } from 'payload' +import type { FieldComponentProps, FieldPermissions, FieldTypes, MappedField } from 'payload' import React from 'react' -import type { - FieldComponentProps, - MappedField, -} from '../../providers/ComponentMap/buildComponentMap/types.js' - import { HiddenField } from '../../fields/Hidden/index.js' import { useFieldComponents } from '../../providers/FieldComponents/index.js' import { useOperation } from '../../providers/Operation/index.js' @@ -18,7 +13,9 @@ type Props = { CustomField: MappedField['CustomField'] custom?: Record disabled: boolean - fieldComponentProps?: FieldComponentProps + fieldComponentProps?: { + forceRender?: boolean + } & FieldComponentProps indexPath?: string isHidden?: boolean name?: string diff --git a/packages/ui/src/forms/RenderFields/types.ts b/packages/ui/src/forms/RenderFields/types.ts index ee9809599..5de018917 100644 --- a/packages/ui/src/forms/RenderFields/types.ts +++ b/packages/ui/src/forms/RenderFields/types.ts @@ -1,6 +1,4 @@ -import type { FieldPermissions, Operation } from 'payload' - -import type { FieldMap } from '../../providers/ComponentMap/buildComponentMap/types.js' +import type { FieldMap, FieldPermissions, Operation } from 'payload' export type Props = { className?: string diff --git a/packages/ui/src/forms/WatchChildErrors/buildPathSegments.ts b/packages/ui/src/forms/WatchChildErrors/buildPathSegments.ts index fdb94eda3..3e0a162ec 100644 --- a/packages/ui/src/forms/WatchChildErrors/buildPathSegments.ts +++ b/packages/ui/src/forms/WatchChildErrors/buildPathSegments.ts @@ -1,4 +1,4 @@ -import type { FieldMap } from '../../providers/ComponentMap/buildComponentMap/types.js' +import type { FieldMap } from 'payload' export const buildPathSegments = (parentPath: string, fieldMap: FieldMap): string[] => { const pathNames = fieldMap.reduce((acc, field) => { diff --git a/packages/ui/src/forms/WatchChildErrors/index.tsx b/packages/ui/src/forms/WatchChildErrors/index.tsx index 69c9fa1f0..7a9bab751 100644 --- a/packages/ui/src/forms/WatchChildErrors/index.tsx +++ b/packages/ui/src/forms/WatchChildErrors/index.tsx @@ -1,8 +1,7 @@ 'use client' +import type { FieldMap } from 'payload' import type React from 'react' -import type { FieldMap } from '../../providers/ComponentMap/buildComponentMap/types.js' - import { useThrottledEffect } from '../../hooks/useThrottledEffect.js' import { useAllFormFields, useFormSubmitted } from '../Form/context.js' import { buildPathSegments } from './buildPathSegments.js' diff --git a/packages/ui/src/hooks/useUseAsTitle.ts b/packages/ui/src/hooks/useUseAsTitle.ts index 52c70841b..68e2e889d 100644 --- a/packages/ui/src/hooks/useUseAsTitle.ts +++ b/packages/ui/src/hooks/useUseAsTitle.ts @@ -1,6 +1,4 @@ -import type { ClientCollectionConfig } from 'payload' - -import type { FieldMap, MappedField } from '../providers/ComponentMap/buildComponentMap/types.js' +import type { ClientCollectionConfig, FieldMap, MappedField } from 'payload' import { flattenFieldMap } from '../utilities/flattenFieldMap.js' diff --git a/packages/ui/src/providers/ComponentMap/buildComponentMap/fields.tsx b/packages/ui/src/providers/ComponentMap/buildComponentMap/fields.tsx index 7027c58c5..76d8a5030 100644 --- a/packages/ui/src/providers/ComponentMap/buildComponentMap/fields.tsx +++ b/packages/ui/src/providers/ComponentMap/buildComponentMap/fields.tsx @@ -1,53 +1,50 @@ import type { I18nClient } from '@payloadcms/translations' import type { + ArrayFieldProps, + BlocksFieldProps, CellComponentProps, + CheckboxFieldProps, + CodeFieldProps, + CollapsibleFieldProps, CustomComponent, + DateFieldProps, + EmailFieldProps, + ErrorProps, Field, + FieldComponentProps, FieldDescriptionProps, + FieldMap, FieldWithPath, + FormFieldBase, + GroupFieldProps, + JSONFieldProps, LabelProps, + MappedField, + MappedTab, + NumberFieldProps, Option, + PointFieldProps, + RadioFieldProps, + ReducedBlock, + RelationshipFieldProps, + RichTextComponentProps, + RowFieldProps, SanitizedConfig, + SelectFieldProps, + TabsFieldProps, + TextFieldProps, + TextareaFieldProps, + UploadFieldProps, } from 'payload' import { MissingEditorProp } from 'payload' -import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/shared' +import { deepCopyObject, fieldAffectsData, fieldIsPresentationalOnly } from 'payload/shared' import React, { Fragment } from 'react' -import type { ArrayFieldProps } from '../../../fields/Array/index.js' -import type { BlocksFieldProps } from '../../../fields/Blocks/index.js' -import type { CheckboxFieldProps } from '../../../fields/Checkbox/index.js' -import type { CodeFieldProps } from '../../../fields/Code/index.js' -import type { CollapsibleFieldProps } from '../../../fields/Collapsible/index.js' -import type { DateFieldProps } from '../../../fields/DateTime/index.js' -import type { EmailFieldProps } from '../../../fields/Email/index.js' -import type { GroupFieldProps } from '../../../fields/Group/index.js' -import type { JSONFieldProps } from '../../../fields/JSON/index.js' -import type { NumberFieldProps } from '../../../fields/Number/index.js' -import type { PointFieldProps } from '../../../fields/Point/index.js' -import type { RadioFieldProps } from '../../../fields/RadioGroup/index.js' -import type { RelationshipFieldProps } from '../../../fields/Relationship/types.js' -import type { RichTextFieldProps } from '../../../fields/RichText/index.js' -import type { RowFieldProps } from '../../../fields/Row/types.js' -import type { SelectFieldProps } from '../../../fields/Select/index.js' -import type { TabsFieldProps } from '../../../fields/Tabs/index.js' -import type { TextFieldProps } from '../../../fields/Text/types.js' -import type { TextareaFieldProps } from '../../../fields/Textarea/types.js' -import type { UploadFieldProps } from '../../../fields/Upload/types.js' -import type { FormFieldBase } from '../../../fields/shared/index.js' import type { WithServerSidePropsPrePopulated } from './index.js' -import type { - FieldComponentProps, - FieldMap, - MappedField, - MappedTab, - ReducedBlock, -} from './types.js' // eslint-disable-next-line payload/no-imports-from-exports-dir -import { FieldDescription } from '../../../exports/client/index.js' -// eslint-disable-next-line payload/no-imports-from-exports-dir -import { HiddenField } from '../../../exports/client/index.js' +import { FieldDescription, HiddenField } from '../../../exports/client/index.js' function generateFieldPath(parentPath, name) { let tabPath = parentPath || '' @@ -60,6 +57,20 @@ function generateFieldPath(parentPath, name) { return tabPath } +function prepareCustomComponentProps( + props: { + [key: string]: any + } & FieldComponentProps, +) { + return deepCopyObject({ + ...props, + fieldMap: undefined, + richTextComponentMap: undefined, + rows: undefined, + tabs: undefined, + }) +} + export const mapFields = (args: { WithServerSideProps: WithServerSidePropsPrePopulated config: SanitizedConfig @@ -134,7 +145,8 @@ export const mapFields = (args: { )) || null - let label = undefined + let label: FormFieldBase['label'] = undefined + if ('label' in field) { if (typeof field.label === 'string' || typeof field.label === 'object') { label = field.label @@ -143,93 +155,18 @@ export const mapFields = (args: { } } - const labelProps: LabelProps = { - label, - required: 'required' in field ? field.required : undefined, - schemaPath: path, - } - - const CustomLabelComponent = - ('admin' in field && - field.admin?.components && - 'Label' in field.admin.components && - field.admin.components?.Label) || - undefined - - // If we return undefined here (so if no CUSTOM label component is set), the field client component is responsible for falling back to the default label - const CustomLabel = - CustomLabelComponent !== undefined ? ( - - ) : undefined - - let description = undefined - if (field.admin && 'description' in field.admin) { - if ( - typeof field.admin?.description === 'string' || - typeof field.admin?.description === 'object' - ) { - description = field.admin.description - } else if (typeof field.admin?.description === 'function') { - description = field.admin?.description({ t }) - } - } - - const descriptionProps: FieldDescriptionProps = { - description, - } - - let CustomDescriptionComponent = undefined - if ( - field.admin?.components && - 'Description' in field.admin.components && - field.admin.components?.Description - ) { - CustomDescriptionComponent = field.admin.components.Description - } else if (description) { - CustomDescriptionComponent = FieldDescription - } - - const CustomDescription = - CustomDescriptionComponent !== undefined ? ( - - ) : undefined - - const errorProps = { - path, - } - - const CustomErrorComponent = - ('admin' in field && - field.admin?.components && - 'Error' in field.admin.components && - field.admin?.components?.Error) || - undefined - - const CustomError = - CustomErrorComponent !== undefined ? ( - - ) : undefined - // These fields are shared across all field types even if they are not used in the default field, as the custom field component can use them const baseFieldProps: FormFieldBase = { AfterInput, BeforeInput, - CustomDescription, - CustomError, - CustomLabel, custom: 'admin' in field && 'custom' in field.admin ? field.admin?.custom : undefined, - descriptionProps, disabled: 'admin' in field && 'disabled' in field.admin ? field.admin?.disabled : false, - errorProps, - label: labelProps?.label, + label, path, required: 'required' in field ? field.required : undefined, } - let fieldComponentProps: FieldComponentProps + let fieldComponentPropsBase: Omit let fieldOptions: Option[] @@ -250,7 +187,7 @@ export const mapFields = (args: { name: 'name' in field ? field.name : undefined, fieldType: field.type, isFieldAffectingData, - label: labelProps?.label || undefined, + label, labels: 'labels' in field ? field.labels : undefined, options: 'options' in field ? fieldOptions : undefined, relationTo: 'relationTo' in field ? field.relationTo : undefined, @@ -259,24 +196,9 @@ export const mapFields = (args: { switch (field.type) { case 'array': { - let CustomRowLabel: React.ReactNode - - if ( - 'admin' in field && - field.admin.components && - 'RowLabel' in field.admin.components && - field.admin.components.RowLabel - ) { - const CustomRowLabelComponent = field.admin.components.RowLabel - CustomRowLabel = ( - - ) - } - - const arrayFieldProps: Omit = { + const arrayFieldProps: ArrayFieldProps = { ...baseFieldProps, name: field.name, - CustomRowLabel, className: field.admin?.className, disabled: field.admin?.disabled, fieldMap: mapFields({ @@ -298,7 +220,7 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = arrayFieldProps + fieldComponentPropsBase = arrayFieldProps break } case 'blocks': { @@ -326,7 +248,7 @@ export const mapFields = (args: { return reducedBlock }) - const blocksField: Omit = { + const blocksField: BlocksFieldProps = { ...baseFieldProps, name: field.name, blocks, @@ -342,7 +264,7 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = blocksField + fieldComponentPropsBase = blocksField cellComponentProps.blocks = field.blocks.map((b) => ({ slug: b.slug, @@ -363,7 +285,7 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = checkboxField + fieldComponentPropsBase = checkboxField break } case 'code': { @@ -380,28 +302,12 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = codeField + fieldComponentPropsBase = codeField break } case 'collapsible': { - let CustomCollapsibleLabel: React.ReactNode - if ( - field?.admin?.components && - 'RowLabel' in field.admin.components && - field?.admin?.components?.RowLabel - ) { - const CustomCollapsibleLabelComponent = field.admin.components.RowLabel - CustomCollapsibleLabel = ( - - ) - } - - const collapsibleField: Omit = { + const collapsibleField: CollapsibleFieldProps = { ...baseFieldProps, - CustomLabel: CustomCollapsibleLabel, className: field.admin?.className, disabled: field.admin?.disabled, fieldMap: mapFields({ @@ -421,7 +327,7 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = collapsibleField as CollapsibleFieldProps // TODO: dunno why this is needed + fieldComponentPropsBase = collapsibleField // TODO: dunno why this is needed break } case 'date': { @@ -438,7 +344,7 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = dateField + fieldComponentPropsBase = dateField cellComponentProps.dateDisplayFormat = field.admin?.date?.displayFormat break } @@ -456,11 +362,11 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = emailField + fieldComponentPropsBase = emailField break } case 'group': { - const groupField: Omit = { + const groupField: GroupFieldProps = { ...baseFieldProps, name: field.name, className: field.admin?.className, @@ -481,7 +387,7 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = groupField + fieldComponentPropsBase = groupField break } case 'json': { @@ -498,7 +404,7 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = jsonField + fieldComponentPropsBase = jsonField break } case 'number': { @@ -518,7 +424,7 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = numberField + fieldComponentPropsBase = numberField break } case 'point': { @@ -533,7 +439,7 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = pointField + fieldComponentPropsBase = pointField break } case 'relationship': { @@ -554,7 +460,7 @@ export const mapFields = (args: { } cellComponentProps.relationTo = field.relationTo - fieldComponentProps = relationshipField + fieldComponentPropsBase = relationshipField break } case 'radio': { @@ -572,11 +478,11 @@ export const mapFields = (args: { } cellComponentProps.options = fieldOptions - fieldComponentProps = radioField + fieldComponentPropsBase = radioField break } case 'richText': { - const richTextField: RichTextFieldProps = { + const richTextField: RichTextComponentProps = { ...baseFieldProps, name: field.name, className: field.admin?.className, @@ -586,9 +492,11 @@ export const mapFields = (args: { style: field.admin?.style, width: field.admin?.width, } + if (!field?.editor) { throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor } + if (typeof field?.editor === 'function') { throw new Error('Attempted to access unsanitized rich text editor.') } @@ -603,6 +511,7 @@ export const mapFields = (args: { i18n, schemaPath: path, }) + richTextField.richTextComponentMap = result cellComponentProps.richTextComponentMap = result } @@ -615,12 +524,12 @@ export const mapFields = (args: { CustomCellComponent = RichTextCellComponent } - fieldComponentProps = richTextField + fieldComponentPropsBase = richTextField break } case 'row': { - const rowField: Omit = { + const rowField: Omit = { ...baseFieldProps, className: field.admin?.className, disabled: field.admin?.disabled, @@ -640,7 +549,7 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = rowField + fieldComponentPropsBase = rowField break } case 'tabs': { @@ -666,7 +575,7 @@ export const mapFields = (args: { return reducedTab }) - const tabsField: Omit = { + const tabsField: Omit = { ...baseFieldProps, name: 'name' in field ? (field.name as string) : undefined, className: field.admin?.className, @@ -678,7 +587,7 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = tabsField + fieldComponentPropsBase = tabsField break } case 'text': { @@ -697,7 +606,7 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = textField + fieldComponentPropsBase = textField break } case 'textarea': { @@ -716,11 +625,11 @@ export const mapFields = (args: { width: field.admin?.width, } - fieldComponentProps = textareaField + fieldComponentPropsBase = textareaField break } case 'ui': { - fieldComponentProps = baseFieldProps + fieldComponentPropsBase = baseFieldProps break } case 'upload': { @@ -738,7 +647,7 @@ export const mapFields = (args: { } cellComponentProps.relationTo = field.relationTo - fieldComponentProps = uploadField + fieldComponentPropsBase = uploadField break } case 'select': { @@ -757,7 +666,7 @@ export const mapFields = (args: { } cellComponentProps.options = fieldOptions - fieldComponentProps = selectField + fieldComponentPropsBase = selectField break } default: { @@ -765,6 +674,142 @@ export const mapFields = (args: { } } + const labelProps: Omit = prepareCustomComponentProps({ + ...fieldComponentPropsBase, + type: undefined, + schemaPath: path, + }) + + const CustomLabelComponent = + ('admin' in field && + field.admin?.components && + 'Label' in field.admin.components && + field.admin.components?.Label) || + undefined + + // If we return undefined here (so if no CUSTOM label component is set), + // the field client component is responsible for falling back to the default label + let CustomLabel: React.ReactNode = + CustomLabelComponent !== undefined ? ( + + ) : undefined + + switch (field.type) { + case 'array': { + let CustomRowLabel: React.ReactNode + + if ( + 'admin' in field && + field.admin.components && + 'RowLabel' in field.admin.components && + field.admin.components.RowLabel + ) { + const CustomRowLabelComponent = field.admin.components.RowLabel + CustomRowLabel = ( + + ) + } + + // @ts-expect-error + fieldComponentPropsBase.CustomRowLabel = CustomRowLabel + + break + } + + case 'collapsible': { + let CustomCollapsibleLabel: React.ReactNode + + if ( + field?.admin?.components && + 'RowLabel' in field.admin.components && + field?.admin?.components?.RowLabel + ) { + const CustomCollapsibleLabelComponent = field.admin.components.RowLabel + CustomCollapsibleLabel = ( + + ) + } + + CustomLabel = CustomCollapsibleLabel + + break + } + + default: + break + } + + let description = undefined + + if (field.admin && 'description' in field.admin) { + if ( + typeof field.admin?.description === 'string' || + typeof field.admin?.description === 'object' + ) { + description = field.admin.description + } else if (typeof field.admin?.description === 'function') { + description = field.admin?.description({ t }) + } + } + + const descriptionProps: FieldDescriptionProps = prepareCustomComponentProps({ + ...fieldComponentPropsBase, + type: undefined, + description, + }) + + let CustomDescriptionComponent = undefined + + if ( + field.admin?.components && + 'Description' in field.admin.components && + field.admin.components?.Description + ) { + CustomDescriptionComponent = field.admin.components.Description + } else if (description) { + CustomDescriptionComponent = FieldDescription + } + + const CustomDescription = + CustomDescriptionComponent !== undefined ? ( + + ) : undefined + + const errorProps: ErrorProps = prepareCustomComponentProps({ + ...fieldComponentPropsBase, + type: undefined, + path, + }) + + const CustomErrorComponent = + ('admin' in field && + field.admin?.components && + 'Error' in field.admin.components && + field.admin?.components?.Error) || + undefined + + const CustomError = + CustomErrorComponent !== undefined ? ( + + ) : undefined + + const fieldComponentProps: FieldComponentProps = { + ...fieldComponentPropsBase, + type: undefined, + CustomDescription, + CustomError, + CustomLabel, + descriptionProps, + errorProps, + labelProps, + } + const reducedField: MappedField = { name: 'name' in field ? field.name : undefined, type: field.type, @@ -772,7 +817,7 @@ export const mapFields = (args: { ) : undefined, CustomField: CustomFieldComponent ? ( - + ) : undefined, cellComponentProps, custom: field?.admin?.custom, @@ -803,7 +848,7 @@ export const mapFields = (args: { result.findIndex((f) => 'name' in f && f.isFieldAffectingData && f.name === 'id') > -1 if (!disableAddingID && !hasID) { - // TODO: For all fields (not just this one) we need to add the name to both .fieldComponentProps.name AND .name. This can probably be improved + // TODO: For all fields (not just this one) we need to add the name to both .fieldComponentPropsBase.name AND .name. This can probably be improved result.push({ name: 'id', type: 'text', @@ -815,6 +860,7 @@ export const mapFields = (args: { disableBulkEdit: true, fieldComponentProps: { name: 'id', + type: undefined, label: 'ID', }, fieldIsPresentational: false, diff --git a/packages/ui/src/providers/ComponentMap/buildComponentMap/types.ts b/packages/ui/src/providers/ComponentMap/buildComponentMap/types.ts index cf79ccb25..7f9ccf6d3 100644 --- a/packages/ui/src/providers/ComponentMap/buildComponentMap/types.ts +++ b/packages/ui/src/providers/ComponentMap/buildComponentMap/types.ts @@ -1,93 +1,4 @@ -import type { - Block, - BlockField, - CellComponentProps, - FieldTypes, - SanitizedCollectionConfig, - SanitizedGlobalConfig, - TabsField, -} from 'payload' - -import type { ArrayFieldProps } from '../../../fields/Array/index.js' -import type { BlocksFieldProps } from '../../../fields/Blocks/index.js' -import type { CheckboxFieldProps } from '../../../fields/Checkbox/types.js' -import type { CodeFieldProps } from '../../../fields/Code/index.js' -import type { CollapsibleFieldProps } from '../../../fields/Collapsible/index.js' -import type { DateFieldProps } from '../../../fields/DateTime/index.js' -import type { EmailFieldProps } from '../../../fields/Email/index.js' -import type { GroupFieldProps } from '../../../fields/Group/index.js' -import type { HiddenInputFieldProps } from '../../../fields/Hidden/index.js' -import type { JSONFieldProps } from '../../../fields/JSON/index.js' -import type { NumberFieldProps } from '../../../fields/Number/index.js' -import type { PointFieldProps } from '../../../fields/Point/index.js' -import type { RelationshipFieldProps } from '../../../fields/Relationship/index.js' -import type { RichTextFieldProps } from '../../../fields/RichText/index.js' -import type { RowFieldProps } from '../../../fields/Row/types.js' -import type { SelectFieldProps } from '../../../fields/Select/index.js' -import type { TabsFieldProps } from '../../../fields/Tabs/index.js' -import type { TextFieldProps } from '../../../fields/Text/types.js' -import type { TextareaFieldProps } from '../../../fields/Textarea/types.js' -import type { UploadFieldProps } from '../../../fields/Upload/types.js' - -export type MappedTab = { - fieldMap?: FieldMap - label: TabsField['tabs'][0]['label'] - name?: string -} - -export type ReducedBlock = { - LabelComponent: Block['admin']['components']['Label'] - custom?: Record - fieldMap: FieldMap - imageAltText?: string - imageURL?: string - labels: BlockField['labels'] - slug: string -} - -export type FieldComponentProps = - | ArrayFieldProps - | BlocksFieldProps - | CheckboxFieldProps - | CodeFieldProps - | CollapsibleFieldProps - | DateFieldProps - | EmailFieldProps - | GroupFieldProps - | HiddenInputFieldProps - | JSONFieldProps - | NumberFieldProps - | PointFieldProps - | RelationshipFieldProps - | RichTextFieldProps - | RowFieldProps - | SelectFieldProps - | TabsFieldProps - | TextFieldProps - | TextareaFieldProps - | UploadFieldProps - -export type MappedField = { - CustomCell?: React.ReactNode - CustomField?: React.ReactNode - cellComponentProps: CellComponentProps - custom?: Record - disableBulkEdit?: boolean - disableListColumn?: boolean - disableListFilter?: boolean - disabled?: boolean - fieldComponentProps: FieldComponentProps - fieldIsPresentational: boolean - isFieldAffectingData: boolean - isHidden?: boolean - isSidebar?: boolean - localized: boolean - name?: string - type: keyof FieldTypes - unique?: boolean -} - -export type FieldMap = MappedField[] +import type { FieldMap, SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload' export type ActionMap = { Edit: { diff --git a/packages/ui/src/providers/ComponentMap/index.tsx b/packages/ui/src/providers/ComponentMap/index.tsx index 79e0b4efb..ee961de62 100644 --- a/packages/ui/src/providers/ComponentMap/index.tsx +++ b/packages/ui/src/providers/ComponentMap/index.tsx @@ -1,7 +1,9 @@ 'use client' +import type { FieldMap, MappedField } from 'payload' + import React, { createContext, useCallback, useContext } from 'react' -import type { ComponentMap, FieldMap, MappedField } from './buildComponentMap/types.js' +import type { ComponentMap } from './buildComponentMap/types.js' export type IComponentMapContext = { componentMap: ComponentMap diff --git a/packages/ui/src/utilities/flattenFieldMap.ts b/packages/ui/src/utilities/flattenFieldMap.ts index a1557980e..010a40dc1 100644 --- a/packages/ui/src/utilities/flattenFieldMap.ts +++ b/packages/ui/src/utilities/flattenFieldMap.ts @@ -1,4 +1,4 @@ -import type { FieldMap } from '../providers/ComponentMap/buildComponentMap/types.js' +import type { FieldMap } from 'payload' /** * Flattens a collection's fields into a single array of fields, as long diff --git a/test/fields/collections/Text/AfterInput.tsx b/test/admin/collections/CustomFields/AfterInput.tsx similarity index 100% rename from test/fields/collections/Text/AfterInput.tsx rename to test/admin/collections/CustomFields/AfterInput.tsx diff --git a/test/fields/collections/Text/BeforeInput.tsx b/test/admin/collections/CustomFields/BeforeInput.tsx similarity index 100% rename from test/fields/collections/Text/BeforeInput.tsx rename to test/admin/collections/CustomFields/BeforeInput.tsx diff --git a/test/fields/collections/Text/CustomError.tsx b/test/admin/collections/CustomFields/CustomError.tsx similarity index 86% rename from test/fields/collections/Text/CustomError.tsx rename to test/admin/collections/CustomFields/CustomError.tsx index 7ff8665fd..1af8bae90 100644 --- a/test/fields/collections/Text/CustomError.tsx +++ b/test/admin/collections/CustomFields/CustomError.tsx @@ -3,7 +3,7 @@ import { useField, useFormFields, useFormSubmitted } from '@payloadcms/ui' import React from 'react' -const CustomError: React.FC = (props) => { +export const CustomError: React.FC = (props) => { const { path: pathFromProps } = props const submitted = useFormSubmitted() const { path } = useField(pathFromProps) @@ -18,5 +18,3 @@ const CustomError: React.FC = (props) => { return null } - -export default CustomError diff --git a/test/admin/components/FieldDescription/index.tsx b/test/admin/collections/CustomFields/FieldDescription/index.tsx similarity index 84% rename from test/admin/components/FieldDescription/index.tsx rename to test/admin/collections/CustomFields/FieldDescription/index.tsx index 5972bcfe4..62ad3eec4 100644 --- a/test/admin/components/FieldDescription/index.tsx +++ b/test/admin/collections/CustomFields/FieldDescription/index.tsx @@ -4,7 +4,7 @@ import type { DescriptionComponent } from 'payload' import { useFieldProps, useFormFields } from '@payloadcms/ui' import React from 'react' -export const FieldDescriptionComponent: DescriptionComponent = () => { +export const FieldDescriptionComponent: DescriptionComponent<'text'> = () => { const { path } = useFieldProps() const field = useFormFields(([fields]) => (fields && fields?.[path]) || null) const { value } = field || {} diff --git a/test/admin/collections/CustomFields/components/CustomSelect.tsx b/test/admin/collections/CustomFields/fields/Select/index.tsx similarity index 87% rename from test/admin/collections/CustomFields/components/CustomSelect.tsx rename to test/admin/collections/CustomFields/fields/Select/index.tsx index c333d66ad..783b3ff81 100644 --- a/test/admin/collections/CustomFields/components/CustomSelect.tsx +++ b/test/admin/collections/CustomFields/fields/Select/index.tsx @@ -2,10 +2,11 @@ import type { Option } from 'payload' -import { SelectField, useField } from '@payloadcms/ui' +import { SelectField, useField, useFieldProps } from '@payloadcms/ui' import React from 'react' -export const CustomSelect = ({ path }: { path: string }) => { +export const CustomSelect = () => { + const { path } = useFieldProps() const { setValue, value } = useField({ path }) const [options, setOptions] = React.useState<{ label: string; value: string }[]>([]) diff --git a/test/admin/collections/CustomFields/fields/Text/Description.tsx b/test/admin/collections/CustomFields/fields/Text/Description.tsx new file mode 100644 index 000000000..e166bbbb7 --- /dev/null +++ b/test/admin/collections/CustomFields/fields/Text/Description.tsx @@ -0,0 +1,9 @@ +import type { TextFieldDescriptionComponent } from 'payload' + +import React from 'react' + +export const CustomDescription: TextFieldDescriptionComponent = (props) => { + return ( +
{`The max length of this field is: ${props?.maxLength}`}
+ ) +} diff --git a/test/admin/collections/CustomFields/fields/Text/Label.tsx b/test/admin/collections/CustomFields/fields/Text/Label.tsx new file mode 100644 index 000000000..1274beed0 --- /dev/null +++ b/test/admin/collections/CustomFields/fields/Text/Label.tsx @@ -0,0 +1,7 @@ +import type { TextFieldLabelComponent } from 'payload' + +import React from 'react' + +export const CustomLabel: TextFieldLabelComponent = (props) => { + return
{`The max length of this field is: ${props?.maxLength}`}
+} diff --git a/test/admin/collections/CustomFields/index.ts b/test/admin/collections/CustomFields/index.ts index de155bef3..1e3c8ab9b 100644 --- a/test/admin/collections/CustomFields/index.ts +++ b/test/admin/collections/CustomFields/index.ts @@ -1,11 +1,55 @@ import type { CollectionConfig } from 'payload' import { customFieldsSlug } from '../../slugs.js' -import { CustomSelect } from './components/CustomSelect.js' +import { AfterInput } from './AfterInput.js' +import { BeforeInput } from './BeforeInput.js' +import { CustomError } from './CustomError.js' +import { FieldDescriptionComponent } from './FieldDescription/index.js' +import { CustomSelect } from './fields/Select/index.js' +import { CustomDescription } from './fields/Text/Description.js' +import { CustomLabel } from './fields/Text/Label.js' export const CustomFields: CollectionConfig = { slug: customFieldsSlug, fields: [ + { + name: 'customTextField', + type: 'text', + maxLength: 100, + admin: { + components: { + afterInput: [AfterInput], + beforeInput: [BeforeInput], + Label: CustomLabel, + Description: CustomDescription, + Error: CustomError, + }, + }, + minLength: 3, + }, + { + name: 'descriptionAsString', + type: 'text', + admin: { + description: 'Static field description.', + }, + }, + { + name: 'descriptionAsFunction', + type: 'text', + admin: { + description: () => 'Function description', + }, + }, + { + name: 'descriptionAsComponent', + type: 'text', + admin: { + components: { + Description: FieldDescriptionComponent, + }, + }, + }, { name: 'customSelectField', type: 'text', diff --git a/test/admin/collections/Posts.ts b/test/admin/collections/Posts.ts index e9051cfac..8e1d6008f 100644 --- a/test/admin/collections/Posts.ts +++ b/test/admin/collections/Posts.ts @@ -5,7 +5,6 @@ import { slateEditor } from '@payloadcms/richtext-slate' import { CustomCell } from '../components/CustomCell/index.js' import { DemoUIFieldCell } from '../components/DemoUIField/Cell.js' import { DemoUIField } from '../components/DemoUIField/Field.js' -import { FieldDescriptionComponent } from '../components/FieldDescription/index.js' import { slugPluralLabel, slugSingularLabel } from '../shared.js' import { postsCollectionSlug } from '../slugs.js' @@ -108,29 +107,6 @@ export const Posts: CollectionConfig = { position: 'sidebar', }, }, - { - name: 'descriptionAsString', - type: 'text', - admin: { - description: 'Static field description.', - }, - }, - { - name: 'descriptionAsFunction', - type: 'text', - admin: { - description: () => 'Function description', - }, - }, - { - name: 'descriptionAsComponent', - type: 'text', - admin: { - components: { - Description: FieldDescriptionComponent, - }, - }, - }, ], labels: { plural: slugPluralLabel, diff --git a/test/admin/e2e/1/e2e.spec.ts b/test/admin/e2e/1/e2e.spec.ts index b858b5093..f79987a1c 100644 --- a/test/admin/e2e/1/e2e.spec.ts +++ b/test/admin/e2e/1/e2e.spec.ts @@ -500,9 +500,90 @@ describe('admin1', () => { }) describe('custom fields', () => { + test('renders custom label component', async () => { + await page.goto(customFieldsURL.create) + await page.waitForURL(customFieldsURL.create) + await expect(page.locator('#custom-field-label')).toBeVisible() + }) + + test('renders custom description component', async () => { + await page.goto(customFieldsURL.create) + await page.waitForURL(customFieldsURL.create) + await expect(page.locator('#custom-field-description')).toBeVisible() + }) + + test('ensure custom components receive field props', async () => { + await page.goto(customFieldsURL.create) + await page.waitForURL(customFieldsURL.create) + await expect(page.locator('#custom-field-label')).toContainText( + 'The max length of this field is: 100', + ) + await expect(page.locator('#custom-field-description')).toContainText( + 'The max length of this field is: 100', + ) + }) + + describe('field descriptions', () => { + test('should render static field description', async () => { + await page.goto(customFieldsURL.create) + await page.waitForURL(customFieldsURL.create) + await expect(page.locator('.field-description-descriptionAsString')).toContainText( + 'Static field description.', + ) + }) + + test('should render functional field description', async () => { + await page.goto(customFieldsURL.create) + await page.waitForURL(customFieldsURL.create) + await page.locator('#field-descriptionAsFunction').fill('functional') + await expect(page.locator('.field-description-descriptionAsFunction')).toContainText( + 'Function description', + ) + }) + }) + + test('should render component field description', async () => { + await page.goto(customFieldsURL.create) + await page.waitForURL(customFieldsURL.create) + await page.locator('#field-descriptionAsComponent').fill('component') + await expect(page.locator('.field-description-descriptionAsComponent')).toContainText( + 'Component description: descriptionAsComponent - component', + ) + }) + + test('should render custom error component', async () => { + await page.goto(customFieldsURL.create) + await page.waitForURL(customFieldsURL.create) + const input = page.locator('input[id="field-customTextField"]') + await input.fill('ab') + await expect(input).toHaveValue('ab') + const error = page.locator('.custom-error:near(input[id="field-customTextField"])') + const submit = page.locator('button[type="button"][id="action-save"]') + await submit.click() + await expect(error).toHaveText('#custom-error') + }) + + test('should render beforeInput and afterInput', async () => { + await page.goto(customFieldsURL.create) + const input = page.locator('input[id="field-customTextField"]') + + const prevSibling = await input.evaluateHandle((el) => { + return el.previousElementSibling + }) + const prevSiblingText = await page.evaluate((el) => el.textContent, prevSibling) + expect(prevSiblingText).toEqual('#before-input') + + const nextSibling = await input.evaluateHandle((el) => { + return el.nextElementSibling + }) + const nextSiblingText = await page.evaluate((el) => el.textContent, nextSibling) + expect(nextSiblingText).toEqual('#after-input') + }) + describe('select field', () => { test('should render custom select options', async () => { await page.goto(customFieldsURL.create) + await page.waitForURL(customFieldsURL.create) await page.locator('#field-customSelectField .rs__control').click() await expect(page.locator('#field-customSelectField .rs__option')).toHaveCount(2) }) diff --git a/test/admin/e2e/2/e2e.spec.ts b/test/admin/e2e/2/e2e.spec.ts index c0b7db8db..4e8a061e5 100644 --- a/test/admin/e2e/2/e2e.spec.ts +++ b/test/admin/e2e/2/e2e.spec.ts @@ -782,29 +782,6 @@ describe('admin2', () => { }) }) }) - - describe('field descriptions', () => { - test('should render static field description', async () => { - await page.goto(postsUrl.create) - await expect(page.locator('.field-description-descriptionAsString')).toContainText( - 'Static field description.', - ) - }) - test('should render functional field description', async () => { - await page.goto(postsUrl.create) - await page.locator('#field-descriptionAsFunction').fill('functional') - await expect(page.locator('.field-description-descriptionAsFunction')).toContainText( - 'Function description', - ) - }) - test('should render component field description', async () => { - await page.goto(postsUrl.create) - await page.locator('#field-descriptionAsComponent').fill('component') - await expect(page.locator('.field-description-descriptionAsComponent')).toContainText( - 'Component description: descriptionAsComponent - component', - ) - }) - }) }) async function createPost(overrides?: Partial): Promise { diff --git a/test/admin/payload-types.ts b/test/admin/payload-types.ts index d13e67114..bd0ee8ff8 100644 --- a/test/admin/payload-types.ts +++ b/test/admin/payload-types.ts @@ -102,9 +102,6 @@ export interface Post { relationship?: (string | null) | Post; customCell?: string | null; sidebarField?: string | null; - descriptionAsString?: string | null; - descriptionAsFunction?: string | null; - descriptionAsComponent?: string | null; updatedAt: string; createdAt: string; _status?: ('draft' | 'published') | null; @@ -173,6 +170,10 @@ export interface CustomViewsTwo { */ export interface CustomField { id: string; + customTextField?: string | null; + descriptionAsString?: string | null; + descriptionAsFunction?: string | null; + descriptionAsComponent?: string | null; customSelectField?: string | null; updatedAt: string; createdAt: string; diff --git a/test/fields/collections/Number/e2e.spec.ts b/test/fields/collections/Number/e2e.spec.ts index 8a1d7d716..918dc7133 100644 --- a/test/fields/collections/Number/e2e.spec.ts +++ b/test/fields/collections/Number/e2e.spec.ts @@ -53,19 +53,16 @@ describe('Number', () => { }) await ensureCompilationIsDone({ page, serverURL }) }) + beforeEach(async () => { await reInitializeDB({ serverURL, snapshotKey: 'fieldsNumberTest', uploadsDir: path.resolve(dirname, './collections/Upload/uploads'), }) - - if (client) { - await client.logout() - } + if (client) await client.logout() client = new RESTClient(null, { defaultSlug: 'users', serverURL }) await client.login() - await ensureCompilationIsDone({ page, serverURL }) }) @@ -77,26 +74,17 @@ describe('Number', () => { test('should filter Number fields in the collection view - greaterThanOrEqual', async () => { await page.goto(url.list) - - // should have 3 entries await expect(page.locator('table >> tbody >> tr')).toHaveCount(3) - - // open the filter options await page.locator('.list-controls__toggle-where').click() await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible() await page.locator('.where-builder__add-first-filter').click() - const initialField = page.locator('.condition__field') const operatorField = page.locator('.condition__operator') const valueField = page.locator('.condition__value >> input') - - // select Number field to filter on await initialField.click() const initialFieldOptions = initialField.locator('.rs__option') await initialFieldOptions.locator('text=number').first().click() await expect(initialField.locator('.rs__single-value')).toContainText('Number') - - // select >= operator await operatorField.click() const operatorOptions = operatorField.locator('.rs__option') await operatorOptions.last().click() @@ -108,14 +96,11 @@ describe('Number', () => { await valueField.fill('3') await expect(valueField).toHaveValue('3') await wait(300) - - // should have 2 entries after filtering await expect(page.locator('table >> tbody >> tr')).toHaveCount(2) }) test('should create', async () => { const input = 5 - await page.goto(url.create) const field = page.locator('#field-number') await field.fill(String(input)) @@ -125,7 +110,6 @@ describe('Number', () => { test('should create hasMany', async () => { const input = 5 - await page.goto(url.create) const field = page.locator('.field-hasMany') await field.click() @@ -138,19 +122,16 @@ describe('Number', () => { test('should bypass min rows validation when no rows present and field is not required', async () => { await page.goto(url.create) await saveDocAndAssert(page) - await expect(page.locator('.payload-toast-container')).toContainText('successfully') + expect(true).toBe(true) // the above fn contains the assertion }) test('should fail min rows validation when rows are present', async () => { const input = 5 - await page.goto(url.create) await page.locator('.field-withMinRows').click() - await page.keyboard.type(String(input)) await page.keyboard.press('Enter') await page.click('#action-save', { delay: 100 }) - await expect(page.locator('.payload-toast-container')).toContainText( 'The following field is invalid: withMinRows', ) diff --git a/test/fields/collections/Text/CustomLabel.tsx b/test/fields/collections/Text/CustomLabel.tsx deleted file mode 100644 index 96ad9b403..000000000 --- a/test/fields/collections/Text/CustomLabel.tsx +++ /dev/null @@ -1,18 +0,0 @@ -'use client' - -import { useFieldProps } from '@payloadcms/ui' -import React from 'react' - -const CustomLabel = ({ schemaPath }) => { - const { path: pathFromContext } = useFieldProps() - - const path = pathFromContext ?? schemaPath // pathFromContext will be undefined in list view - - return ( - - ) -} - -export default CustomLabel diff --git a/test/fields/collections/Text/e2e.spec.ts b/test/fields/collections/Text/e2e.spec.ts index 36ec866e4..1526382cf 100644 --- a/test/fields/collections/Text/e2e.spec.ts +++ b/test/fields/collections/Text/e2e.spec.ts @@ -154,40 +154,6 @@ describe('Text', () => { await expect(description).toHaveText('en description') }) - test('should render custom label', async () => { - await page.goto(url.create) - const label = page.locator('label.custom-label[for="field-customLabel"]') - await expect(label).toHaveText('#label') - }) - - test('should render custom error', async () => { - await page.goto(url.create) - const input = page.locator('input[id="field-customError"]') - await input.fill('ab') - await expect(input).toHaveValue('ab') - const error = page.locator('.custom-error:near(input[id="field-customError"])') - const submit = page.locator('button[type="button"][id="action-save"]') - await submit.click() - await expect(error).toHaveText('#custom-error') - }) - - test('should render beforeInput and afterInput', async () => { - await page.goto(url.create) - const input = page.locator('input[id="field-beforeAndAfterInput"]') - - const prevSibling = await input.evaluateHandle((el) => { - return el.previousElementSibling - }) - const prevSiblingText = await page.evaluate((el) => el.textContent, prevSibling) - expect(prevSiblingText).toEqual('#before-input') - - const nextSibling = await input.evaluateHandle((el) => { - return el.nextElementSibling - }) - const nextSiblingText = await page.evaluate((el) => el.textContent, nextSibling) - expect(nextSiblingText).toEqual('#after-input') - }) - test('should create hasMany with multiple texts', async () => { const input = 'five' const furtherInput = 'six' diff --git a/test/fields/collections/Text/index.ts b/test/fields/collections/Text/index.ts index 1c7c54716..24aa8ac48 100644 --- a/test/fields/collections/Text/index.ts +++ b/test/fields/collections/Text/index.ts @@ -1,9 +1,5 @@ import type { CollectionConfig } from 'payload' -import { AfterInput } from './AfterInput.js' -import { BeforeInput } from './BeforeInput.js' -import CustomError from './CustomError.js' -import CustomLabel from './CustomLabel.js' import { defaultText, textFieldsSlug } from './shared.js' const TextFields: CollectionConfig = { @@ -93,35 +89,6 @@ const TextFields: CollectionConfig = { ], }, }, - { - name: 'customLabel', - type: 'text', - admin: { - components: { - Label: CustomLabel, - }, - }, - }, - { - name: 'customError', - type: 'text', - admin: { - components: { - Error: CustomError, - }, - }, - minLength: 3, - }, - { - name: 'beforeAndAfterInput', - type: 'text', - admin: { - components: { - afterInput: [AfterInput], - beforeInput: [BeforeInput], - }, - }, - }, { name: 'hasMany', type: 'text', diff --git a/test/fields/payload-types.ts b/test/fields/payload-types.ts index 3e8c6a7ee..85031f46d 100644 --- a/test/fields/payload-types.ts +++ b/test/fields/payload-types.ts @@ -61,7 +61,7 @@ export interface Config { 'payload-migrations': PayloadMigration; }; db: { - defaultIDType: number; + defaultIDType: string; }; globals: { tabsWithRichText: TabsWithRichText; @@ -92,7 +92,7 @@ export interface UserAuthOperations { * via the `definition` "lexical-fields". */ export interface LexicalField { - id: number; + id: string; title: string; lexicalSimple?: { root: { @@ -133,7 +133,7 @@ export interface LexicalField { * via the `definition` "lexical-migrate-fields". */ export interface LexicalMigrateField { - id: number; + id: string; title: string; lexicalWithLexicalPluginData?: { root: { @@ -228,7 +228,7 @@ export interface LexicalMigrateField { * via the `definition` "lexical-localized-fields". */ export interface LexicalLocalizedField { - id: number; + id: string; title: string; lexicalBlocksSubLocalized?: { root: { @@ -268,7 +268,7 @@ export interface LexicalLocalizedField { * via the `definition` "users". */ export interface User { - id: number; + id: string; canViewConditionalField?: boolean | null; updatedAt: string; createdAt: string; @@ -286,7 +286,7 @@ export interface User { * via the `definition` "array-fields". */ export interface ArrayField { - id: number; + id: string; title?: string | null; items: { text: string; @@ -350,7 +350,7 @@ export interface ArrayField { * via the `definition` "block-fields". */ export interface BlockField { - id: number; + id: string; blocks: ( | { text: string; @@ -677,7 +677,7 @@ export interface BlockField { | null; relationshipBlocks?: | { - relationship?: (number | null) | TextField; + relationship?: (string | null) | TextField; id?: string | null; blockName?: string | null; blockType: 'relationships'; @@ -691,7 +691,7 @@ export interface BlockField { * via the `definition` "text-fields". */ export interface TextField { - id: number; + id: string; text: string; localizedText?: string | null; i18nText?: string | null; @@ -702,9 +702,6 @@ export interface TextField { overrideLength?: string | null; fieldWithDefaultValue?: string | null; dependentOnFieldWithDefaultValue?: string | null; - customLabel?: string | null; - customError?: string | null; - beforeAndAfterInput?: string | null; hasMany?: string[] | null; validatesHasMany?: string[] | null; localizedHasMany?: string[] | null; @@ -720,7 +717,7 @@ export interface TextField { * via the `definition` "checkbox-fields". */ export interface CheckboxField { - id: number; + id: string; checkbox: boolean; updatedAt: string; createdAt: string; @@ -730,7 +727,7 @@ export interface CheckboxField { * via the `definition` "code-fields". */ export interface CodeField { - id: number; + id: string; javascript?: string | null; typescript?: string | null; json?: string | null; @@ -744,7 +741,7 @@ export interface CodeField { * via the `definition` "collapsible-fields". */ export interface CollapsibleField { - id: number; + id: string; text: string; group?: { textWithinGroup?: string | null; @@ -776,7 +773,7 @@ export interface CollapsibleField { * via the `definition` "conditional-logic". */ export interface ConditionalLogic { - id: number; + id: string; text: string; toggleField?: boolean | null; fieldToToggle?: string | null; @@ -801,7 +798,7 @@ export interface ConditionalLogic { * via the `definition` "date-fields". */ export interface DateField { - id: number; + id: string; default: string; timeOnly?: string | null; timeOnlyWithCustomFormat?: string | null; @@ -816,9 +813,10 @@ export interface DateField { * via the `definition` "email-fields". */ export interface EmailField { - id: number; + id: string; email: string; localizedEmail?: string | null; + emailWithAutocomplete?: string | null; i18nEmail?: string | null; defaultEmail?: string | null; defaultEmptyString?: string | null; @@ -837,7 +835,7 @@ export interface EmailField { * via the `definition` "radio-fields". */ export interface RadioField { - id: number; + id: string; radio?: ('one' | 'two' | 'three') | null; updatedAt: string; createdAt: string; @@ -847,7 +845,7 @@ export interface RadioField { * via the `definition` "group-fields". */ export interface GroupField { - id: number; + id: string; group: { text: string; defaultParent?: string | null; @@ -922,7 +920,7 @@ export interface RowField { * via the `definition` "indexed-fields". */ export interface IndexedField { - id: number; + id: string; text: string; uniqueText?: string | null; uniqueRequiredText: string; @@ -951,7 +949,7 @@ export interface IndexedField { * via the `definition` "json-fields". */ export interface JsonField { - id: number; + id: string; json?: { foo?: 'bar' | 'foobar'; number?: 10 | 5; @@ -976,7 +974,7 @@ export interface JsonField { * via the `definition` "number-fields". */ export interface NumberField { - id: number; + id: string; number?: number | null; min?: number | null; max?: number | null; @@ -997,7 +995,7 @@ export interface NumberField { * via the `definition` "point-fields". */ export interface PointField { - id: number; + id: string; /** * @minItems 2 * @maxItems 2 @@ -1023,49 +1021,49 @@ export interface PointField { * via the `definition` "relationship-fields". */ export interface RelationshipField { - id: number; + id: string; text?: string | null; relationship: | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | { relationTo: 'array-fields'; - value: number | ArrayField; + value: string | ArrayField; }; relationHasManyPolymorphic?: | ( | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | { relationTo: 'array-fields'; - value: number | ArrayField; + value: string | ArrayField; } )[] | null; - relationToSelf?: (number | null) | RelationshipField; - relationToSelfSelectOnly?: (number | null) | RelationshipField; - relationWithDynamicDefault?: (number | null) | User; + relationToSelf?: (string | null) | RelationshipField; + relationToSelfSelectOnly?: (string | null) | RelationshipField; + relationWithDynamicDefault?: (string | null) | User; relationHasManyWithDynamicDefault?: { relationTo: 'users'; - value: number | User; + value: string | User; } | null; - relationshipWithMin?: (number | TextField)[] | null; - relationshipWithMax?: (number | TextField)[] | null; - relationshipHasMany?: (number | TextField)[] | null; + relationshipWithMin?: (string | TextField)[] | null; + relationshipWithMax?: (string | TextField)[] | null; + relationshipHasMany?: (string | TextField)[] | null; array?: | { - relationship?: (number | null) | TextField; + relationship?: (string | null) | TextField; id?: string | null; }[] | null; relationshipWithMinRows?: | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; }[] | null; updatedAt: string; @@ -1076,7 +1074,7 @@ export interface RelationshipField { * via the `definition` "rich-text-fields". */ export interface RichTextField { - id: number; + id: string; title: string; lexicalCustomFields: { root: { @@ -1151,7 +1149,7 @@ export interface RichTextField { * via the `definition` "select-fields". */ export interface SelectField { - id: number; + id: string; select?: ('one' | 'two' | 'three') | null; selectReadOnly?: ('one' | 'two' | 'three') | null; selectHasMany?: ('one' | 'two' | 'three' | 'four' | 'five' | 'six')[] | null; @@ -1169,7 +1167,7 @@ export interface SelectField { * via the `definition` "tabs-fields-2". */ export interface TabsFields2 { - id: number; + id: string; tabsInArray?: | { text?: string | null; @@ -1187,7 +1185,7 @@ export interface TabsFields2 { * via the `definition` "tabs-fields". */ export interface TabsField { - id: number; + id: string; sidebarField?: string | null; array: { text: string; @@ -1296,9 +1294,9 @@ export interface TabsField { * via the `definition` "uploads". */ export interface Upload { - id: number; + id: string; text?: string | null; - media?: number | Upload | null; + media?: string | Upload | null; richText?: { root: { type: string; @@ -1331,9 +1329,9 @@ export interface Upload { * via the `definition` "uploads2". */ export interface Uploads2 { - id: number; + id: string; text?: string | null; - media?: number | Uploads2 | null; + media?: string | Uploads2 | null; updatedAt: string; createdAt: string; url?: string | null; @@ -1351,8 +1349,8 @@ export interface Uploads2 { * via the `definition` "uploads3". */ export interface Uploads3 { - id: number; - media?: number | Uploads3 | null; + id: string; + media?: string | Uploads3 | null; richText?: { root: { type: string; @@ -1385,7 +1383,7 @@ export interface Uploads3 { * via the `definition` "ui-fields". */ export interface UiField { - id: number; + id: string; text: string; updatedAt: string; createdAt: string; @@ -1395,10 +1393,10 @@ export interface UiField { * via the `definition` "payload-preferences". */ export interface PayloadPreference { - id: number; + id: string; user: { relationTo: 'users'; - value: number | User; + value: string | User; }; key?: string | null; value?: @@ -1418,7 +1416,7 @@ export interface PayloadPreference { * via the `definition` "payload-migrations". */ export interface PayloadMigration { - id: number; + id: string; name?: string | null; batch?: number | null; updatedAt: string; @@ -1429,7 +1427,7 @@ export interface PayloadMigration { * via the `definition` "tabsWithRichText". */ export interface TabsWithRichText { - id: number; + id: string; tab1?: { rt1?: { root: { diff --git a/test/live-preview/payload-types.ts b/test/live-preview/payload-types.ts index 2816a3130..906006fba 100644 --- a/test/live-preview/payload-types.ts +++ b/test/live-preview/payload-types.ts @@ -22,6 +22,9 @@ export interface Config { 'payload-preferences': PayloadPreference; 'payload-migrations': PayloadMigration; }; + db: { + defaultIDType: string; + }; globals: { header: Header; footer: Footer; @@ -36,13 +39,16 @@ export interface UserAuthOperations { email: string; }; login: { - password: string; email: string; + password: string; }; registerFirstUser: { email: string; password: string; }; + unlock: { + email: string; + }; } /** * This interface was referenced by `Config`'s JSON-Schema diff --git a/test/plugin-form-builder/payload-types.ts b/test/plugin-form-builder/payload-types.ts index 9e976d94d..aacc681db 100644 --- a/test/plugin-form-builder/payload-types.ts +++ b/test/plugin-form-builder/payload-types.ts @@ -18,6 +18,9 @@ export interface Config { 'payload-preferences': PayloadPreference; 'payload-migrations': PayloadMigration; }; + db: { + defaultIDType: string; + }; globals: {}; locale: 'en' | 'es' | 'de'; user: User & { @@ -29,13 +32,16 @@ export interface UserAuthOperations { email: string; }; login: { - password: string; email: string; + password: string; }; registerFirstUser: { email: string; password: string; }; + unlock: { + email: string; + }; } /** * This interface was referenced by `Config`'s JSON-Schema diff --git a/test/plugin-seo/payload-types.ts b/test/plugin-seo/payload-types.ts index 8ee16f98c..fedbade76 100644 --- a/test/plugin-seo/payload-types.ts +++ b/test/plugin-seo/payload-types.ts @@ -18,6 +18,9 @@ export interface Config { 'payload-preferences': PayloadPreference; 'payload-migrations': PayloadMigration; }; + db: { + defaultIDType: string; + }; globals: {}; locale: 'en' | 'es' | 'de'; user: User & { @@ -29,13 +32,16 @@ export interface UserAuthOperations { email: string; }; login: { - password: string; email: string; + password: string; }; registerFirstUser: { email: string; password: string; }; + unlock: { + email: string; + }; } /** * This interface was referenced by `Config`'s JSON-Schema diff --git a/test/versions/payload-types.ts b/test/versions/payload-types.ts index 0f0262c78..f9846c56a 100644 --- a/test/versions/payload-types.ts +++ b/test/versions/payload-types.ts @@ -22,6 +22,9 @@ export interface Config { 'payload-preferences': PayloadPreference; 'payload-migrations': PayloadMigration; }; + db: { + defaultIDType: string; + }; globals: { 'autosave-global': AutosaveGlobal; 'draft-global': DraftGlobal; @@ -38,13 +41,16 @@ export interface UserAuthOperations { email: string; }; login: { - password: string; email: string; + password: string; }; registerFirstUser: { email: string; password: string; }; + unlock: { + email: string; + }; } /** * This interface was referenced by `Config`'s JSON-Schema