## Why this exists
Lexical in Payload is a React Server Component (RSC). Historically that
created three headaches:
1. You couldn’t render the editor directly from the client.
2. Features like blocks, tables, upload and link drawers require the
server to know the shape of nested sub‑fields at render time. If you
tried to render on demand, the server didn’t know those schemas.
3. The rich text field is designed to live inside a Form. For simple use
cases, setting up a full form just to manage editor state was
cumbersome.
## What’s new
We now ship a client component, `<RenderLexical />`, that renders a
Lexical editor **on demand** while still covering the full feature set.
On mount, it calls a server action to render the editor on the server
using the new `render-field` server action. That server render gives
Lexical everything it needs (including nested field schemas) and returns
a ready‑to‑hydrate editor.
## Example - Rendering in custom component within existing Form
```tsx
'use client'
import type { JSONFieldClientComponent } from 'payload'
import { buildEditorState, RenderLexical } from '@payloadcms/richtext-lexical/client'
import { lexicalFullyFeaturedSlug } from '../../slugs.js'
export const Component: JSONFieldClientComponent = (args) => {
return (
<div>
Fully-Featured Component:
<RenderLexical
field={{ name: 'json' }}
initialValue={buildEditorState({ text: 'defaultValue' })}
schemaPath={`collection.${lexicalFullyFeaturedSlug}.richText`}
/>
</div>
)
}
```
## Example - Rendering outside of Form, manually managing richText
values
```ts
'use client'
import type { DefaultTypedEditorState } from '@payloadcms/richtext-lexical'
import type { JSONFieldClientComponent } from 'payload'
import { buildEditorState, RenderLexical } from '@payloadcms/richtext-lexical/client'
import React, { useState } from 'react'
import { lexicalFullyFeaturedSlug } from '../../slugs.js'
export const Component: JSONFieldClientComponent = (args) => {
const [value, setValue] = useState<DefaultTypedEditorState | undefined>(() =>
buildEditorState({ text: 'state default' }),
)
const handleReset = React.useCallback(() => {
setValue(buildEditorState({ text: 'state default' }))
}, [])
return (
<div>
Default Component:
<RenderLexical
field={{ name: 'json' }}
initialValue={buildEditorState({ text: 'defaultValue' })}
schemaPath={`collection.${lexicalFullyFeaturedSlug}.richText`}
setValue={setValue as any}
value={value}
/>
<button onClick={handleReset} style={{ marginTop: 8 }} type="button">
Reset Editor State
</button>
</div>
)
}
```
## How it works (under the hood)
- On first render, `<RenderLexical />` calls the server function
`render-field` (wired into @payloadcms/next), passing a schemaPath.
- The server loads the exact field config and its client schema map for
that path, renders the Lexical editor server‑side (so nested features
like blocks/tables/relationships are fully known), and returns the
component tree.
- While waiting, the client shows a small shimmer skeleton.
- Inside Forms, RenderLexical plugs into the parent form via useField;
outside Forms, you can fully control the value by passing
value/setValue.
## Type Improvements
While implementing the `buildEditorState` helper function for our test
suite, I noticed some issues with our `TypedEditorState` type:
- nodes were no longer narrowed by their node.type types
- upon fixing this issue, the type was no longer compatible with the
generated types. To address this, I had to weaken the generated type a
bit.
In order to ensure the type will keep functioning as intended from now
on, this PR also adds some type tests
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211110462564644
Fixes https://github.com/payloadcms/payload/issues/13856
When using `enableListViewSelectAPI` on upload collections the thumbnail
images require data, this PR ensures that the required data is always
selected.
Fixes https://github.com/payloadcms/payload/issues/13833
When generating graphql schemas, named tabs were not properly being
accounted for. This PR fixes that and ensure that the correct schema
types are generated for named tabs.
Fixes https://github.com/payloadcms/payload/issues/13774
EditorOptions were not being respected properly. The fix for this was to
set the following on the Editor component:
```ts
detectIndentation: false,
insertSpaces: undefined,
tabSize: undefined,
```
### Other fixes
This PR also fixed the flash when JSON fields were saved. It removed the
need for the `editorKey` which was causing the entire field to re-mount
when the json value changed. We had this work around so data could be
set externally and the height would be automatically calculated when the
editor mounted. But since the JSON value did not have a stable reference
there was no way for react to memoize it, so the key would change every
time the document was saved.
Now we pass down a `recalculatedHeightAt` which allows data to be edited
externally still, but tells the component to recalculate its height
without forcing the component to re-mount.
Installs
[@faceless-ui/modal@3.0.0](https://github.com/faceless-ui/modal/releases/tag/v3.0.0),
which now has React v19 stable listed as its peer deps. This will
prevent dependency mismatch errors when installing node modules as
`react@19.0.0-rc.0` is no longer expected.
Fixes#13818
Folder provider operations were not passing the locale query param,
causing issues when moving items into folders that had a required
localized field.
Before, unnamed, unlabelled group were part of an unlabeled collapsible
in the version diff view. This means, all it displayed was a clickable,
empty rectangle. This looks like a bug rather than intended.
This PR displays a new <Unnamed Group> label in these cases.
It also fixes the incorrect `NamedGroupFieldClient` type. Previously,
that type did not include the `name` property, even though it was
available on the client.
Before:
<img width="2372" height="688" alt="Screenshot 2025-09-16 at 18 57
45@2x"
src="https://github.com/user-attachments/assets/0f351f84-a00f-4067-aa40-d0e8fbfd5f0b"
/>
After:
<img width="2326" height="598" alt="Screenshot 2025-09-16 at 18 56
14@2x"
src="https://github.com/user-attachments/assets/bddee841-8218-4a90-a052-9875c5f252c0"
/>
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211375615406676
Assuming you have 3 block/array rows and you only modify the middle one
- the version view would still display row 1 and row 3:
<img width="2354" height="1224" alt="Screenshot 2025-09-16 at 16 15
22@2x"
src="https://github.com/user-attachments/assets/5f823276-fda2-4192-a7d3-482f2a2228f9"
/>
After this PR, it's now displayed correctly:
<img width="2368" height="980" alt="Screenshot 2025-09-16 at 16 15
09@2x"
src="https://github.com/user-attachments/assets/7fc5ee25-f925-4c41-b62a-9b33652e19f9"
/>
## The Fix
The generated version fields will contain holes in the `rows` array for
rows that have no changes. The fix is to simply skip rendering those
rows. We still need to keep those holes in order to maintain the correct
row indexes.
Additionally, this PR improves the naming of some legacy variables and
function arguments that I missed out on during the version view
overhaul:
> comparison => valueFrom
> version => valueTo
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211267837905382
Wrap `EmailField` and `TextField` with `FieldPathContext` to ensure the
inputs bind to their own paths (`"email"`, `"username"`) instead of
inheriting the relationship field name (e.g., `"author"`).
This restores correct `name` / `schemaPath` binding and fixes form
submission.
Regression introduced in #11973.
Fixes#13764.
This approach is consistent with the discussion in
[#13806](https://github.com/payloadcms/payload/pull/13806), where it was
suggested that enforcing field paths explicitly is the right direction.
**What?**
Fixes regression where inline create drawer fields (`EmailField`,
`TextField`) incorrectly inherited the parent relationship field name,
breaking form submission.
**Why?**
Without this fix, the email input in inline create forms was bound to
the relationship field name (e.g., `"author"`) instead of `"email"`,
causing authentication-enabled collections to fail when creating users
inline.
**How?**
* Wrap `EmailField` with `FieldPathContext value="email"`
* Wrap `TextField` with `FieldPathContext value="username"`
* Ensures inputs register under the correct paths in form state.
Fixes#13764.
Fixes an issue with the new experimental `enableListViewSelectAPI`
config option.
Group fields were not populating properly in the list view
### Before (incorrect)
```ts
{
group.field: true
}
```
### After (correct)
```ts
{
group: {
field: true
}
}
```
* The `pagination` property was missing in `findVersions` and
`findGlobalVersions` Local API operations, although the actual functions
did have it -
1b93c4becc/packages/payload/src/collections/operations/findVersions.ts (L25)
* The handling of the `pagination` property in those functions was
broken, this PR fixes it.
When using server-side live preview across domains, the initial
`postMessage` to the iframe throws the following error:
```txt
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://your-frontend.com') does not match the recipient window's origin ('https://your-backend.com').
```
This error is misleading, however, as it's thrown even when the iframe's
source exactly matches the post message's `targetOrigin`.
For example:
```ts
recipient.postMessage(message, targetOrigin)
```
The problem is that the initial message is sent before the iframe is
ready to receive it, resulting in the parent window posting to itself.
This is not a problem when the front-end is running on the same server,
but if the recipient changes while initializing, the target origin will
be mismatched.
Worth noting that this is not an issue with client-side live preview.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211376499297320
### What?
Fixes crash when virtual fields use field types not supported by the
WhereBuilder (e.g. 'group' fields).
<img width="1336" height="459" alt="Screenshot 2025-09-16 at 11 00
30 AM"
src="https://github.com/user-attachments/assets/3adbb507-3033-4f52-b7cc-3c5bad74900b"
/>
### Why?
Users reported a crash with error "Cannot read properties of undefined
(reading 'operators')" when entering collection list views with virtual
fields that have `type: 'group'`. The issue occurred because:
- Virtual fields can use any field type including 'group'
- The `fieldTypes` object in WhereBuilder only supports specific field
types (text, number, select, etc.)
- Code tried to access `fieldTypes[field.type].operators` when
`field.type` was 'group', causing undefined access
### How?
- Refactored virtual field handling to use a unified processing flow
instead of separate early returns
- Virtual fields now set the `pathPrefix` to their virtual path and
continue through normal field validation
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211315667956059
---------
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
### What?
Skip field validation when trashing documents with empty required
fields.
### Why?
When trashing a document that was saved as a draft with empty required
fields, Payload would run full validation and fail with "The following
fields are invalid" errors. This happened because trash operations were
treated as regular updates that require full field validation, even
though trashing is just a metadata change (setting `deletedAt`) and
shouldn't be blocked by content validation issues.
### How?
- Modified `skipValidation` logic in `updateDocument()` to skip
validation when `deletedAt` is being set in the update data
Fixes#13706
### What?
Passes the same args provided to a task's `shouldRestore` function to
both the `onSuccess` & `onFail` callbacks
### Why?
Currently `onSuccess` and `onFail` are quite useless without any
context, this will allow for a wider range of functionality:
- Checking if it's the last failure
- Access to the task `input`
- Access to `req` to allow logging, email notifications, etc.
### How?
1. Created a new `TaskCallbackArgs` type, which replicates the args of
the `ShouldRestoreFn` type.
2. Add a `TaskCallbackFn` type
3. Update the function calls of both `onSuccess` and `onFail`.
### Questions
- I wasn't sure about the typing of `input` – I can see `input: input!`
being used elsewhere for task errors so I replicated that.
- Same for `taskStatus`, I added a type check but I'm not sure if this
is the right approach (what would scenario would result in a `null`
value?). Should `TaskCallbackArgs['taskStatus']` be typed to allow for
`null` values?
---------
Co-authored-by: Alessio Gravili <alessio@gravili.de>
Fixes https://github.com/payloadcms/payload/issues/12286. Supersedes
https://github.com/payloadcms/payload/pull/12290.
As of
[v3.35.0](https://github.com/payloadcms/payload/releases/tag/v3.35.0),
you are no longer able to directly pass a `path` prop to a custom field
component.
For example:
```tsx
'use client'
import React from 'react'
import { TextField } from '@payloadcms/ui'
import type { TextFieldClientComponent } from 'payload'
export const MyCustomField: TextFieldClientComponent = (props) => {
return (
<TextField
{...props}
path="path.to.some.other.field" // This will not be respected, because this field's context takes precedence
/>
)
}
```
This was introduced in #11973 where we began passing a new
`potentiallyStalePath` arg to the `useField` hook that takes the path
from context as priority. This change was necessary in order to fix
stale paths during row manipulation while the server is processing.
To ensure field components respect your custom path, you need to wrap
your components with their own `FieldPathContext`:
```tsx
'use client'
import React from 'react'
import { TextField, FieldPathContext } from '@payloadcms/ui'
import type { TextFieldClientComponent } from 'payload'
export const MyCustomField: TextFieldClientComponent = (props) => {
return (
<FieldPathContext path="path.to.some.other.field">
<TextField {...props} />
</FieldPathContext>
)
}
```
It's possible we can remove this in the future. I explored this in
#12290, but it may require some more substantial changes in
architecture. These exports are labeled experimental to allow for any
potential changes in behavior that we may need to make in the future.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1210533177582945
Follow-up to #12893.
### What?
Removes cases where submitting a document as a draft in a collection
with versioning enabled would cause publishing validation errors to be
displayed on further document form changes. An example case is when
saving the draft failed due to a `beforeChange` hook throwing an
`APIError`.
### How
The behavior change is that the form state is marked as un-submitted
post a submit failure as a draft. The form not being considered as
submitted results in `packages/ui/src/views/Edit/index.tsx` to use
`skipValidation: true`.
---------
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
### What?
Remove `select` parameter from database operations in update operation
during version creation to fix malformed version data.
### Why?
The `select` parameter was being passed to `saveVersion()` in update
operations, causing versions to only contain selected fields
### How?
- Removed `select` parameter from `payload.db.updateOne()` calls in
update operations
- Removed `select` parameter from `saveVersion()` call in update
operation
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211334352350013
## New jobs:handle-schedules bin script
Similarly to `payload jobs:run`, this PR adds a new
`jobs:handle-schedules` bin script which only handles scheduling.
## Allows jobs:run bin script to handle scheduling
Similarly to how [payload
autoRun](https://payloadcms.com/docs/jobs-queue/queues#cron-jobs)
handles both running and scheduling jobs by default, you can now set the
`payload jobs:run` bin script to also handle scheduling. This is opt-in:
```sh
pnpm payload jobs:run --cron "*/5 * * * *" --queue myQueue --handle-schedules # This will both schedule jobs according to the configuration and run them
```
## Cron schedules for all bin scripts
Previously, only the `payload jobs:run` bin script accepted a cron flag.
The `payload jobs:handle-schedules` would have required the same logic
to also handle a cron flag.
Instead of opting for this duplicative logic, I'm now handling cron
logic before we determine which script to run. This means: it's simpler
and requires less duplicative code.
**This allows all other bin scripts (including custom ones) to use the
`--cron` flag**, enabling cool use-cases like scheduling your own custom
scripts - no additional config required!
Example:
```sh
pnpm payload run ./myScript.ts --cron "0 * * * *"
```
Video Example:
https://github.com/user-attachments/assets/4ded738d-2ef9-43ea-8136-f47f913a7ba8
## More reliable job system crons
When using autorun or `--cron`, if one cron run takes longer than the
cron interval, the second cron would run before the first one finishes.
This can be especially dangerous when running jobs using a bin script,
potentially causing race conditions, as the first cron run will take
longer due to payload initialization overhead (only for first cron run,
consecutive ones use cached payload). Now, consecutive cron runs will
wait for the first one to finish by using the `{ protect: true }`
property of Croner.
This change will affect both autorun and bin scripts.
## Cleanup
- Centralized payload instance cleanup (payload.destroy()) for all bin
scripts
- The `getPayload` function arguments were not properly typed. Arguments
like `disableOnInit: true` are already supported, but the type did not
reflect that. This simplifies the type and makes it more accurate.
## Fixes
- `allQueues` argument for `payload jobs:run` was not respected
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211124797199077
Fixes#13756
The findByID endpoint, by default, expects the data for localized fields
to be an object, values mapped to the locale. This is not the case for
client-side live preview, as we send already-flattened data to the
findByID endpoint.
For localized fields where the value is an object (richText/json/group),
the afterRead hook handler would attempt to flatten the field value,
even though it was already flattened.
## Solution
The solution is to expose a `flattenLocales` arg to the findByID
endpoint (default: true) and pass `flattenLocales: false` from the
client-side live preview request handler.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211334752795627
Fixes#12924
### What?
If an access check request resulted in no access to a doc, then the
sanitizer will remove even the `collections` field from the sanitized
permissions. This throws a server error when the `collections` field is
attempted to be accessed with some collection slug. The error is "Cannot
read properties of undefined".
### Why?
An example of how this might come about:
I am using multi-tenancy. I have some conditions about which users can
see other user's information (do they share any tenants). One workflow
is such that a "super admin" (user A) of a tenant removes an "admin"
(user B) inside their tenant. This is done by removing the tenant from
the user's list. After user A's update succeeds, payload defaults to
trying to reload user B's page. However, the two no longer share a
tenant. The access controls correctly evaluate that user A should no
longer be able to see/update/etc user B, but there is a bug in how the
objects are handled (see below).
The sanitizer removes the .collections field from the
sanitizedPermissions, so we get a server error about the key missing
"Cannot read properties of undefined (reading 'users')" in my case.
### How?
Instead of immediately keying into the sanitizedPermissions, we see if
the collections exist. If not, we make sure to return a well-defined
object with no permissions.
Before:
```ts
return sanitizedPermissions.collections![config.slug]!
```
After:
```ts
const collectionPermissions = sanitizedPermissions?.collections?.[config.slug]
return collectionPermissions ?? { fields: {} }
```
---------
Co-authored-by: Alessio Gravili <alessio@gravili.de>
### What?
Updated a few words to better represent what they mean in Dutch.
### Why?
The words that are used now are synonyms but have the wrong meaning in
the context of the UI.
### How?
Clear - Duidelijk - This means like "This explanation was very clear"
Changed it to "Wissen", which is to clear filters.
Close - Dichtbij - This means close by, like "This drive is just around
to corner, it is close by"
Changed it to "Sluiten", which is to close for example a dialog
Similar spirit as #13714.
Permissions are embedded into the page response, exposing some field
names to unauthenticated users.
For example, when setting `read: () => false` on a field, that field's
name is now included in the response due to its presence in the
permissions object.
We now search the HTML source directly in the test, similar to "view
source" in the browser, which will be much effective at preventing
regression going forward.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211347942663256
Follow-up to #13714.
Fully sanitizes the unauthenticated client config to exclude much of the
users collection, including fields, etc. These are not required of the
login flow and are now completely omitted along with other unnecessary
properties.
This is closely aligned with the goals of the original PR, and as an
added bonus, makes the config _even smaller_ than it already was for
unauthenticated users.
Needs #13790.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211332845301588
### What?
Fix two live preview issues affecting client-side navigation:
1. Stale preview data leaking between pages using `useLivePreview`.
2. Erroneous fetches to `/api/undefined` and incorrect content rendering
when preview events lack slugs.
### Why?
The live-preview module cached merged preview data globally, which
persisted across route changes, causing a new page to render with the
previous page’s data.
The client attempted to merge when preview events didn’t specify
collectionSlug or globalSlug, producing an endpoint of undefined and
triggering requests to /api/undefined, sometimes overwriting state with
mismatched content.
### How?
Clear the internal cache at the time of `subscribe()` so each page using
`useLivePreview` starts from a clean slate.
In `handleMessage`, only call `mergeData` when `collectionSlug` or
`globalSlug` is present; otherwise return `initialData` and perform no
request.
Fixes#13792
Redirecting from login to any non-auth collection crashes the page with
the following error:
```
Cannot read properties of null (reading 'fields')
```
TL;DR: the page-level config context was threading stale methods from
the root config provider.
#### Background
The client config is now gated behind authentication as of #13714, so it
changes based on authentication status. If the root layout is mounted
before authentication, it puts the unauthenticated client config into
state for the entire app to consume.
On login, the root layout does not re-render, so the page itself needs
to generate a fresh client config and sync it up.
This leads to race conditions, however, where if the login page included
a `?redirect=` param, the redirect would take place _before_ the
page-level client config could sync to the layout, and ultimately crash
the page. This was addressed in #13786.
While this fixed redirects to the "users" collection, this collection is
_already_ included in the client config (soon to be omitted by #13785).
So if you redirect to any other collection, the above error occurs.
#### Problem
The page-level config context is only overriding the `config` property,
keeping stale methods from the root config provider. This means calling
`getEntityConfig` during this moment in the time would reference the
stale config, although `config` itself would be fresh.
#### Solution
Wrap the page with an entirely new context provider. Do not thread
inherited methods from the root provider, this way all new methods get
instantiated using the fresh config.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211332845301596
### What?
Opening a document with an expired lock and richtext caused the richtext
to be read-only.
### Why?
The changes in #13579 made the richtext read only if isLocked is set.
But the server side implementation of isLocked did not consider expired
locks.
### How?
Update the server-side getIsLocked to also consider expired locks by not
loading them.
## Problem
When logging in with a `?redirect=`, the target page may crash. This
happens because the update from `SyncClientConfig` is applied in a
`useEffect`, which runs **_after_** the target page's client components
render. As a result, those components read the unauthenticated client
config on first render - even though they expect the authenticated one.
## Steps To Reproduce
1. logout
2. login with ?redirect=
3. document client component incorrectly still receives unauthenticated
client config on render
4. THEN the SyncClientConfig useEffect runs. Too late
5. Potential error (depending on the page, e.g. document view) -
document client component expects sth to be there, but it is not
## Solution
This PR replaces `SyncClientConfig` with a `RootPageConfigProvider`.
This new provider shadows the root layout’s `ConfigProvider` and ensures
the correct client config is available **_immediately_**.
It still updates the config using `useEffect` (the same
way`SyncClientConfig` did), but with one key difference:
- During the brief window between the redirect and the effect running,
it overrides the root layout’s config and provides the fresh,
authenticated config from the root page via the `RootConfigContext`.
This guarantees that client components on the target page receive the
correct config on first render, preventing errors caused by reading the
outdated unauthenticated config.
## Additional change - get rid of `UnsanitizedClientConfig` and
`sanitizeClientConfig`
Those functions added unnecessary complexity, just to build the
blocksMap. I removed those and perform the building of the `blocksMap`
server-side - directly in `createClientConfig`.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211334752795621
Follow up to #11375.
When setting `filterOptions` on relationship or upload fields _that are
nested within a named field_, those options won't be applied to the
`Filter` component in the list view.
This is because of how we key the results when resolving `filterOptions`
on the server. Instead of using the field path as expected, we were
using the field name, causing a failed lookup on the front-end. This
also solves an issue where two fields with the same name would override
each other's `filterOptions`, since field names alone are not unique.
Unrelated: this PR also does some general housekeeping to e2e test
helpers.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211332845301583
When sending REST API requests with multipart/form-data, e.g. PATCH or
POST within the admin panel, a request body larger than 1MB throws the
following error:
```
Unterminated string in JSON at position...
```
This is because there are sensible defaults imposed by the HTML form
data parser (currently using
[busboy](https://github.com/fastify/busboy)). If your documents exceed
this limit, you may run into this error when editing them within the
admin panel.
To support large documents over 1MB, use the new `bodyParser` property
on the root config:
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
bodyParser: {
limits: {
fieldSize: 2 * 1024 * 1024, // This will allow requests containing up to 2MB of multipart/form-data
}
}
}
```
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211317005907885
Adds support for the `beforeOperation` hook on globals. Runs before all
other hooks to either modify the arguments that operations receive, or
perform side-effects before an operation begins.
```ts
import type { GlobalConfig } from 'payload'
const MyGlobal: GlobalConfig = {
// ...
hooks: {
beforeOperation: []
}
}
```
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1211317005907890
Fixes an issue when user has no collection preferences saved. When no
collection level preferences are saved, the result is `undefined`. This
change ensures there is a result before accessing `res.value`.
### What?
Presentational Fields such as
[Row](https://payloadcms.com/docs/fields/row) are described as only
effecting the admin panel. If they do not impact data, their types
should not include hooks in the fields config.
### Why?
Developers can currently assign hooks to these fields, expecting them to
work, when in reality they are not called.
### How?
Omit `hooks` from `FieldBase`
Fixes#11507
---------
Co-authored-by: German Jablonski <43938777+GermanJablo@users.noreply.github.com>
Fixes#13665
This PR fixes the issue by searching only for the first field with the
given name.
I used a Set to iterate over the array of fields only once.
The question remains: What happens if someone wants to search for a
nested field that has the same name as a top-level field?
I think this is a much less likely scenario than the one that happened
to OP in #13665, and in that case the user would probably want to change
the name of the nested field.
If we see this as a problem in the future, we can consider something
like using paths (`tabName.fieldName`).
For review, the example snippet that appears in the issue can be used.
## Problem
GraphQL join fields nested inside tabs and collapsibles return empty
results due to missing `collectionSlug` parameter propagation in the
tabs and collapsible resolver.
## Root Cause
The `tabs` resolver in `fieldToSchemaMap.ts` doesn't pass
`collectionSlug` to nested field resolvers, causing polymorphic
relationship queries to fail when constructing MongoDB filters.
## Solution
- Added `collectionSlug` parameter to tabs resolver function signature
- Pass `collectionSlug` to `buildObjectType` for named tabs
- Pass `collectionSlug` to `addSubField` for unnamed tabs
## Testing
- [x] Verified join fields work correctly in tab-nested scenarios
- [x] Tested with multiple collection types
- [x] Confirmed no breaking changes to existing functionality
Fixes#13345
We have got this running successfully in production and are looking
forward for this to get merged so we do not have to patch the core
anymore :)
---------
Co-authored-by: Alessio Gravili <alessio@gravili.de>
### What?
Added support for `disableListColumn` and `disableListFilter` admin
properties on imageSize configurations that automatically apply to all
fields within the corresponding size group.
### Why?
Upload collections with multiple image sizes can clutter the admin list
view with many size-specific columns and filters. This feature allows
developers to selectively hide size fields from list views while keeping
them accessible in the document edit view.
### How?
Modified `getBaseFields.ts` to inherit admin properties from imageSize
configuration and apply them to all nested fields (url, width, height,
mimeType, filesize, filename) within each size group. The implementation
uses conditional spread operators to only apply these properties when
explicitly set to `true`, maintaining backward compatibility.