feat: threads field config through components and strictly types props (#7754)

## Description

Threads the field config to all "field subcomponents" through props,
i.e. field label, description, error, etc. This way, the field config
that controls any particular component is easily accessible and strongly
typed, i.e. `props.field.maxLength`. This is true for both server and
client components, whose server-side props are now also contextually
typed. This behavior was temporarily removed in #7474 due to bloating
HTML, but has since been resolved in #7620. This PR also makes
significant improvements to component types by exporting explicit types
for _every component of every field_, each with its own client/server
variation. Now, a custom component can look something like this:

```tsx
import type { TextFieldLabelServerComponent } from 'payload'

import React from 'react'

export const CustomLabel: TextFieldLabelServerComponent = (props) => {
  return (
    <div>{`The max length of this field is: ${props?.field?.maxLength}`}</div>
  )
}
```

The following types are now available:

```ts
import type {
  TextFieldClientComponent,
  TextFieldServerComponent,
  TextFieldLabelClientComponent,
  TextFieldLabelServerComponent,
  TextFieldDescriptionClientComponent,
  TextFieldDescriptionServerComponent,
  TextFieldErrorClientComponent,
  TextFieldErrorServerComponent,
  // ...and so one for each field
} from 'payload'
```

BREAKING CHANGES:

In order to strictly type these components, a few breaking changes have
been made _solely to type definitions_. This only effects you if you are
heavily using custom components.

Old
```ts
import type { ErrorComponent, LabelComponent, DescriptionComponent } from 'payload'
```

New:
```ts
import type {
  FieldErrorClientComponent,
  FieldErrorServerComponent,
  FieldLabelClientComponent,
  FieldLabelServerComponent,
  FieldDescriptionClientComponent,
  FieldDescriptionServerComponent,
  // Note: these are the generic, underlying types of the more stricter types described above ^
  // For example, you should use the type that is explicit for your particular field and environment
  // i.e. `TextFieldLabelClientComponent` and not simply `FieldLabelClientComponent`
} from 'payload'
```

- [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] Bug fix (non-breaking change which fixes an issue)

## 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
This commit is contained in:
Jacob Fletcher
2024-08-20 00:25:10 -04:00
committed by GitHub
parent 9e6e8357b8
commit 3a91deb0a4
99 changed files with 1380 additions and 621 deletions

View File

@@ -54,6 +54,7 @@ const RichTextField: React.FC<LoadedSlateFieldProps> = (props) => {
descriptionProps,
elements,
errorProps,
field,
field: {
name,
_path: pathFromProps,
@@ -316,9 +317,15 @@ const RichTextField: React.FC<LoadedSlateFieldProps> = (props) => {
width,
}}
>
<FieldLabel Label={Label} label={label} required={required} {...(labelProps || {})} />
<FieldLabel
Label={Label}
label={label}
required={required}
{...(labelProps || {})}
field={field}
/>
<div className={`${baseClass}__wrap`}>
<FieldError CustomError={Error} path={path} {...(errorProps || {})} />
<FieldError CustomError={Error} field={field} path={path} {...(errorProps || {})} />
<Slate
editor={editor}
key={JSON.stringify({ initialValue, path })} // makes sure slate is completely re-rendered when initialValue changes, bypassing the slate-internal value memoization. That way, external changes to the form will update the editor
@@ -449,7 +456,7 @@ const RichTextField: React.FC<LoadedSlateFieldProps> = (props) => {
</div>
</div>
</Slate>
<FieldDescription Description={Description} {...(descriptionProps || {})} />
<FieldDescription Description={Description} field={field} {...(descriptionProps || {})} />
</div>
</div>
)