Skip to content

Commit 721919f

Browse files
authored
feat(richtext-lexical): add maxDepth property to various lexical features (#6242)
1 parent d3e27e8 commit 721919f

File tree

13 files changed

+146
-59
lines changed

13 files changed

+146
-59
lines changed

docs/fields/relationship.mdx

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,28 @@ keywords: relationship, fields, config, configuration, documentation, Content Ma
2626

2727
### Config
2828

29-
| Option | Description |
30-
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
31-
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
32-
| **`relationTo`** \* | Provide one or many collection `slug`s to be able to assign relationships to. |
33-
| **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-relationship-options). |
34-
| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many relations instead of only one. |
35-
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with `hasMany`. |
36-
| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with `hasMany`. |
37-
| **`maxDepth`** | Sets a number limit on iterations of related documents to populate when queried. [Depth](/docs/getting-started/concepts#depth) |
38-
| **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. |
39-
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
40-
| **`validate`** | Provide a custom validation function that will be executed on both the Admin panel and the backend. [More](/docs/fields/overview#validation) |
41-
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
42-
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/config), include its data in the user JWT. |
43-
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
44-
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
45-
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
46-
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
47-
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
48-
| **`required`** | Require this field to have a value. |
49-
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-config) for more details. |
50-
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
29+
| Option | Description |
30+
|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
31+
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
32+
| **`relationTo`** \* | Provide one or many collection `slug`s to be able to assign relationships to. |
33+
| **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-relationship-options). |
34+
| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many relations instead of only one. |
35+
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with `hasMany`. |
36+
| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with `hasMany`. |
37+
| **`maxDepth`** | Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](/docs/getting-started/concepts#field-level-max-depth) |
38+
| **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. |
39+
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
40+
| **`validate`** | Provide a custom validation function that will be executed on both the Admin panel and the backend. [More](/docs/fields/overview#validation) |
41+
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
42+
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/config), include its data in the user JWT. |
43+
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
44+
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
45+
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
46+
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
47+
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
48+
| **`required`** | Require this field to have a value. |
49+
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-config) for more details. |
50+
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
5151

5252
_\* An asterisk denotes that a property is required._
5353

docs/getting-started/concepts.mdx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,28 @@ To populate `user.author.department` in it's entirety you could specify `?depth=
156156
}
157157
```
158158

159+
#### Field-level max depth
160+
161+
Fields like relationships or uploads can have a `maxDepth` property that limits the depth of the population for that field. Here are some examples:
162+
163+
Depth: 10
164+
Current depth when field is accessed: 1
165+
`maxDepth`: undefined
166+
167+
In this case, the field would be populated to 9 levels of population.
168+
169+
Depth: 10
170+
Current depth when field is accessed: 0
171+
`maxDepth`: 2
172+
173+
In this case, the field would be populated to 2 levels of population, despite there being a remaining depth of 8.
174+
175+
Depth: 10
176+
Current depth when field is accessed: 2
177+
`maxDepth`: 1
178+
179+
In this case, the field would not be populated, as the current depth (2) has exceeded the `maxDepth` for this field (1).
180+
159181
<Banner type="warning">
160182
<strong>Note:</strong>
161183
<br />

packages/graphql/src/schema/buildObjectType.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,9 +490,12 @@ function buildObjectType({
490490
if (editor?.populationPromises) {
491491
const fieldPromises = []
492492
const populationPromises = []
493+
const populateDepth =
494+
field?.maxDepth !== undefined && field?.maxDepth < depth ? field?.maxDepth : depth
495+
493496
editor?.populationPromises({
494497
context,
495-
depth,
498+
depth: populateDepth,
496499
draft: args.draft,
497500
field,
498501
fieldPromises,

packages/payload/src/fields/config/schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ export const richText = baseField.keys({
477477
validate: joi.func().required(),
478478
})
479479
.unknown(),
480+
maxDepth: joi.number(),
480481
})
481482

482483
export const date = baseField.keys({

packages/payload/src/fields/config/types.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,11 @@ export type UploadField = FieldBase & {
446446
}
447447
}
448448
filterOptions?: FilterOptions
449+
/**
450+
* Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached.
451+
*
452+
* {@link https://payloadcms.com/docs/getting-started/concepts#field-level-max-depth}
453+
*/
449454
maxDepth?: number
450455
relationTo: string
451456
type: 'upload'
@@ -506,6 +511,11 @@ export type SelectField = FieldBase & {
506511
type SharedRelationshipProperties = FieldBase & {
507512
filterOptions?: FilterOptions
508513
hasMany?: boolean
514+
/**
515+
* Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached.
516+
*
517+
* {@link https://payloadcms.com/docs/getting-started/concepts#field-level-max-depth}
518+
*/
509519
maxDepth?: number
510520
type: 'relationship'
511521
} & (
@@ -588,6 +598,12 @@ export type RichTextField<
588598
editor?:
589599
| RichTextAdapter<Value, AdapterProps, AdapterProps>
590600
| RichTextAdapterProvider<Value, AdapterProps, AdapterProps>
601+
/**
602+
* Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached.
603+
*
604+
* {@link https://payloadcms.com/docs/getting-started/concepts#field-level-max-depth}
605+
*/
606+
maxDepth?: number
591607
type: 'richText'
592608
} & ExtraProperties
593609

packages/payload/src/fields/hooks/afterRead/promise.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,13 @@ export const promise = async ({
150150
const editor: RichTextAdapter = field?.editor
151151
// This is run here AND in the GraphQL Resolver
152152
if (editor?.populationPromises) {
153+
const populateDepth =
154+
field?.maxDepth !== undefined && field?.maxDepth < depth ? field?.maxDepth : depth
155+
153156
editor.populationPromises({
154157
context,
155158
currentDepth,
156-
depth,
159+
depth: populateDepth,
157160
draft,
158161
field,
159162
fieldPromises,

packages/richtext-lexical/src/field/features/link/drawer/baseFields.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const getBaseFields = (
88
config: SanitizedConfig,
99
enabledCollections: false | string[],
1010
disabledCollections: false | string[],
11+
maxDepth?: number,
1112
): FieldWithRichTextRequiredEditor[] => {
1213
let enabledRelations: string[]
1314

@@ -97,6 +98,7 @@ export const getBaseFields = (
9798
}
9899
: null,
99100
label: ({ t }) => t('fields:chooseDocumentToLink'),
101+
maxDepth,
100102
relationTo: enabledRelations,
101103
required: true,
102104
})

packages/richtext-lexical/src/field/features/link/feature.server.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ export type LinkFeatureServerProps = ExclusiveLinkCollectionsProps & {
5050
defaultFields: FieldWithRichTextRequiredEditor[]
5151
}) => FieldWithRichTextRequiredEditor[])
5252
| FieldWithRichTextRequiredEditor[]
53+
/**
54+
* Sets a maximum population depth for the internal doc default field of link, regardless of the remaining depth when the field is reached.
55+
* This behaves exactly like the maxDepth properties of relationship and upload fields.
56+
*
57+
* {@link https://payloadcms.com/docs/getting-started/concepts#field-level-max-depth}
58+
*/
59+
maxDepth?: number
5360
}
5461

5562
export const LinkFeature: FeatureProviderProviderServer<LinkFeatureServerProps, ClientProps> = (
@@ -67,6 +74,7 @@ export const LinkFeature: FeatureProviderProviderServer<LinkFeatureServerProps,
6774
_config,
6875
props.enabledCollections,
6976
props.disabledCollections,
77+
props.maxDepth,
7078
)
7179

7280
const sanitizedFields = (await sanitizeFields({

packages/richtext-lexical/src/field/features/link/plugins/floatingLinkEditor/utilities.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ export function transformExtraFields(
1616
config: SanitizedConfig,
1717
enabledCollections?: false | string[],
1818
disabledCollections?: false | string[],
19+
maxDepth?: number,
1920
): FieldWithRichTextRequiredEditor[] {
2021
const baseFields: FieldWithRichTextRequiredEditor[] = getBaseFields(
2122
config,
2223
enabledCollections,
2324
disabledCollections,
25+
maxDepth,
2426
)
2527

2628
let fields: FieldWithRichTextRequiredEditor[]

packages/richtext-lexical/src/field/features/relationship/feature.server.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import type { FeatureProviderProviderServer } from '../types.js'
33
import { createNode } from '../typeUtilities.js'
44
import { RelationshipFeatureClientComponent } from './feature.client.js'
55
import { RelationshipNode } from './nodes/RelationshipNode.js'
6-
import { relationshipPopulationPromise } from './populationPromise.js'
6+
import { relationshipPopulationPromiseHOC } from './populationPromise.js'
77

8-
export type RelationshipFeatureProps =
8+
export type ExclusiveRelationshipFeatureProps =
99
| {
1010
/**
1111
* The collections that should be disabled. Overrides the `enableRichTextRelationship` property in the collection config.
@@ -27,6 +27,16 @@ export type RelationshipFeatureProps =
2727
enabledCollections?: string[]
2828
}
2929

30+
export type RelationshipFeatureProps = ExclusiveRelationshipFeatureProps & {
31+
/**
32+
* Sets a maximum population depth for this relationship, regardless of the remaining depth when the respective field is reached.
33+
* This behaves exactly like the maxDepth properties of relationship and upload fields.
34+
*
35+
* {@link https://payloadcms.com/docs/getting-started/concepts#field-level-max-depth}
36+
*/
37+
maxDepth?: number
38+
}
39+
3040
export const RelationshipFeature: FeatureProviderProviderServer<
3141
RelationshipFeatureProps,
3242
RelationshipFeatureProps
@@ -39,7 +49,7 @@ export const RelationshipFeature: FeatureProviderProviderServer<
3949
nodes: [
4050
createNode({
4151
node: RelationshipNode,
42-
populationPromises: [relationshipPopulationPromise],
52+
populationPromises: [relationshipPopulationPromiseHOC(props)],
4353
// TODO: Add validation similar to upload
4454
}),
4555
],

0 commit comments

Comments
 (0)