Commit Graph

5936 Commits

Author SHA1 Message Date
Alessio Gravili
59414bd8f1 feat(richtext-lexical): support copy & pasting and drag & dopping files/images into the editor (#13868)
This PR adds support for inserting images into the rich text editor via
both **copy & paste** and **drag & drop**, whether from local files or
image DOM nodes.

It leverages the bulk uploads UI to provide a smooth workflow for:
- Selecting the target collection
- Filling in any required fields defined on the uploads collection
- Uploading multiple images at once

This significantly improves the UX for adding images to rich text, and
also works seamlessly when pasting images from external editors like
Google Docs or Microsoft Word.

Test pre-release: `3.57.0-internal.801ab5a`

## Showcase - drag & drop images from computer


https://github.com/user-attachments/assets/c558c034-d2e4-40d8-9035-c0681389fb7b

## Showcase - copy & paste images from computer


https://github.com/user-attachments/assets/f36faf94-5274-4151-b141-00aff2b0efa4

## Showcase - copy & paste image DOM nodes


https://github.com/user-attachments/assets/2839ed0f-3f28-4e8d-8b47-01d0cb947edc

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211217132290841
2025-09-24 15:04:46 +00:00
Jessica Rynkar
062c1d7e89 fix: pagination returning duplicate results when timestamps: false (#13920)
### What
Fixes a bug where collection docs paginate incorrectly when `timestamps:
false` is set — the same docs were appearing across multiple pages.

### Why
The `find` query sanitizes the `sort` parameter.  

- With `timestamps: true`, it defaults to `createdAt`
- With `timestamps: false`, it falls back to `_id`

That logic is correct, but in `find.ts` we always passed `timestamps:
true`, ignoring the collection config. With the right sort applied,
pagination works as expected.

### How
`find.ts` now passes `collectionConfig.timestamps` to
`buildSortParam()`, ensuring the correct sort field is chosen.

---

Fixes #13888
2025-09-24 14:44:41 +01:00
Patrik
96c66125dd fix(plugin-import-export): collectionSlug field regression from hidden field changes (#13917)
### What?

- Fixes collectionSlug field not being populated, breaking export
downloads
- Works around recent UI changes that prevent custom components from
rendering for `admin.hidden` fields
- Removes `admin.hidden: true` - as the component already ensures a
hidden state by returning `null`

### Why?

- This merged [PR](https://github.com/payloadcms/payload/pull/13869)
changed how `admin.hidden` fields are rendered, causing them to bypass
custom field components entirely. The `collectionSlug` field relied on a
custom `CollectionField` component to set its value from the
ImportExportProvider context.

### How?

- Removes `admin.hidden: true`
- Field remains visually hidden with `null` return but custom component
logic now executes properly
2025-09-24 06:01:02 -07:00
Patrik
79b25577b9 fix(plugin-search): transaction state errors in parallel reindex operations (#13915)
### What?

Fixed flaky MongoDB transaction state errors ("Attempted illegal state
transition from `[TRANSACTION_ABORTED]` to `[TRANSACTION_COMMITTED]`")
in plugin-search int tests.

### Why?

When reindexing multiple collections in parallel, individual collection
failures were calling `killTransaction()` on a shared
transaction that other parallel operations were still using, causing
MongoDB transaction state conflicts and test flakiness.

### How?

- Moved transaction cleanup to outer catch block only
- Removed individual `killTransaction` calls that created race
conditions
- Allow parallel operations to handle their own errors without aborting
the shared transaction
2025-09-23 13:59:46 -07:00
Jarrod Flesch
68882aa9bc fix(plugin-search): returns doc instead of empty return (#13916)
Returns doc instead of nothing/undefined inside the syncDocAsSearchIndex
function when the plugin encounters a document it has already synced.
2025-09-23 16:58:31 -04:00
Patrik
9c20eb34d4 fix(next): groupBy for polymorphic relationships (#13781)
### What?

Fixed groupBy functionality for polymorphic relationships, which was
throwing errors.

<img width="1099" height="996" alt="Screenshot 2025-09-11 at 3 10 32 PM"
src="https://github.com/user-attachments/assets/bd11d557-7f21-4e09-8fe6-6a43d777d82c"
/>

### Why?

The groupBy feature failed for polymorphic relationships because:
- `relationshipConfig` was undefined when `relationTo` is an array
(polymorphic)
- ObjectId serialization errors when passing database objects to React
client components
- hasMany relationships weren't properly flattened into individual
groups
- "No Value" groups appeared first instead of populated groups

### How?

- Handle polymorphic relationship structure `{relationTo, value}`
correctly by finding the right collection config using `relationTo`
- Add proper collection config lookup for each relation in polymorphic
relationships during populate
- Flatten hasMany relationship arrays so documents with `[Category1,
Category2]` create separate groups for each

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211331842191589
2025-09-23 10:14:35 -07:00
Jarrod Flesch
5c81342bce fix(ui): folder filters hidden behind results (#13908)
Fixes https://github.com/payloadcms/payload/issues/13886

Search filters would be hidden behind the table results. This PR adds
css to adjust the z-index of the searchbar when it has an active popup.
2025-09-23 12:24:06 -04:00
Jarrod Flesch
4975b8dd4b fix(ui): custom folder slug in browse-by (#13909)
Fixes https://github.com/payloadcms/payload/issues/13887

ensureSafeCollectionsChange was using the `folderSlug` imported from the
constants file instead of using the `slug` passed into the
createFolderCollection function.
2025-09-23 12:23:26 -04:00
Jarrod Flesch
984f1b39c5 fix(ui): query folder children with depth and select (#13910)
Fixes https://github.com/payloadcms/payload/issues/13890

Query folder children with depth to ensure that the data needed to
display the result items is returned.
2025-09-23 12:22:53 -04:00
Jacob Fletcher
00a673e491 feat(next): regenerate live preview url on save (#13631)
Closes #12785.

Although your live preview URL can be dynamic based on document data, it
is never recalculated after initial mount. This means if your URL is
dependent of document data that was just changed, such as a "slug"
field, the URL of the iframe does not reflect that change as expected
until the window is refreshed or you navigate back.

This also means that server-side live preview will crash when your
front-end attempts to query using a slug that no longer exists. Here's
the general flow: slug changes, autosave runs, iframe refreshes (url has
old slug), 404.

Now, we execute your live preview function on submit within form state,
and the window responds to the new URL as expected, refreshing itself
without losing its connection.

Here's the result:


https://github.com/user-attachments/assets/7dd3b147-ab6c-4103-8b2f-14d6bc889625

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211094211063140
2025-09-23 09:37:15 -04:00
Паламар Роман
5b5eaebfdf fix(translations): correct Ukrainian language translations (#13639)
### Problem

Some Ukrainian translations contained typos and unnatural phrasing,
which could confuse users or look unpolished.

Fixed spelling mistakes and corrected mistranslations to make the text
clear and natural for Ukrainian users.
As a native Ukrainian speaker, I just went through and cleaned up any
awkward or incorrect phrases.

#### Details

- Changed `"Рахунок"` to `"Обліковий запис"` because the original term
implies a financial account, whereas this context refers to a user
account.
- Changed `"Спеціальне замовлення"` to `"Користувацьке"` because it
better conveys "custom" in the software context.
- Changed `"Редагування взято на себе"` to `"Редагування перехоплено"`
to reflect the action of taking over editing more accurately.
- Changed `"Спорожнити кошик"` to `"Очистити кошик"` for a clearer,
user-friendly wording.
- Changed `"Перейняти"` to `"Перехопити"` to better match the context of
taking control of editing.
- Changed `"Правда"`/`"Неправда"` to `"Так"`/`"Ні"` for more natural,
concise boolean representation in Ukrainian.
- Changed `"Поточний проект"` / `"Раніше був проект"` to `"Поточна
чернетка"` / `"Раніше була чернетка"` to correctly reflect the meaning
of draft in this context.
- Changed `"Локаль"` / `"Локалі"` to `"Локалізація"` / `"Локалізації"`
for consistency across all translation strings.
- Changed `"Відновлення..."` instead of previous value, which seems to
be prompt leak of original translation script (`general.restoring`)
2025-09-23 15:39:18 +03:00
Alessio Gravili
39143c9d12 fix: add missing translation key (#13902)
The monorepo currently does not build because we're missing a
translation for a newly added language
2025-09-22 23:58:58 +00:00
Alessio Gravili
d8dace6509 feat: conditional blocks (#13801)
This PR introduces support for conditionally setting allowable block
types via a new `field.filterOptions` property on the blocks field.

Closes the following feature requests:
https://github.com/payloadcms/payload/discussions/5348,
https://github.com/payloadcms/payload/discussions/4668 (partly)

## Example

```ts
fields: [
  {
      name: 'enabledBlocks',
      type: 'text',
      admin: {
        description:
          "Change the value of this field to change the enabled blocks of the blocksWithDynamicFilterOptions field. If it's empty, all blocks are enabled.",
      },
    },
    {
      name: 'blocksWithFilterOptions',
      type: 'blocks',
      filterOptions: ['block1', 'block2'],
      blocks: [
        {
          slug: 'block1',
          fields: [
            {
              type: 'text',
              name: 'block1Text',
            },
          ],
        },
        {
          slug: 'block2',
          fields: [
            {
              type: 'text',
              name: 'block2Text',
            },
          ],
        },
        {
          slug: 'block3',
          fields: [
            {
              type: 'text',
              name: 'block3Text',
            },
          ],
        },
      ],
    },
    {
      name: 'blocksWithDynamicFilterOptions',
      type: 'blocks',
      filterOptions: ({ siblingData: _siblingData, data }) => {
        const siblingData = _siblingData as { enabledBlocks: string }

        if (siblingData?.enabledBlocks !== data?.enabledBlocks) {
          // Just an extra assurance that the field is working as intended
          throw new Error('enabledBlocks and siblingData.enabledBlocks must be identical')
        }
        return siblingData?.enabledBlocks?.length ? [siblingData.enabledBlocks] : true
      },
      blocks: [
        {
          slug: 'block1',
          fields: [
            {
              type: 'text',
              name: 'block1Text',
            },
          ],
        },
        {
          slug: 'block2',
          fields: [
            {
              type: 'text',
              name: 'block2Text',
            },
          ],
        },
        {
          slug: 'block3',
          fields: [
            {
              type: 'text',
              name: 'block3Text',
            },
          ],
        },
      ],
    },
]
```


https://github.com/user-attachments/assets/e38a804f-22fa-4fd2-a6af-ba9b0a5a04d2

# Rationale

## Why not `block.condition`?

- Individual blocks are often reused in multiple contexts, where the
logic for when they should be available may differ. It’s more
appropriate for the blocks field (typically tied to a single collection)
to determine availability.
- Hiding existing blocks when they no longer satisfy a condition would
cause issues - for example, reordering blocks would break or cause block
data to disappear. Instead, this implementation ensures consistency by
throwing a validation error if a block is no longer allowed. This aligns
with the behavior of `filterOptions` in relationship fields, rather than
`condition`.

## Why not call it `blocksFilterOptions`?

Although the type differs from relationship fields, this property is
named `filterOptions` (and not `blocksFilterOptions`) for consistency
across field types. For example, the Select field also uses
`filterOptions` despite its type being unique.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211334752795631
2025-09-22 14:20:25 -07:00
Sasha
e99e054d7c fix: findDistinct with polymorphic relationships (#13875)
Fixes `findDistinct` with polymorphic relationships and also fixes a bug
from https://github.com/payloadcms/payload/pull/13840 when
`findDistinct` didn't work properly for `hasMany` relationships in
mongodb if `sort` is the same as `field`

---------

Co-authored-by: Patrik Kozak <35232443+PatrikKozak@users.noreply.github.com>
2025-09-22 12:23:17 -07:00
Patrik
82d98ab375 fix(ui): revert-to-published button showing on new drafts (#13897)
### What?

Hide the "**Revert to published**" button when creating a new draft that
has never been published.

### Why?

Previously, the button was visible on new drafts, which was confusing
because there was no published version to revert to.

### How?

Updated the revert button condition to also require `hasPublishedDoc`.
2025-09-22 12:07:57 -07:00
Alessio Gravili
66f5d1429d fix(richtext-lexical): richtext field duplicates description custom component (#13880)
The lexical field component was accidentally rendering the description
component twice.

Fixes https://github.com/payloadcms/payload/issues/13644
2025-09-22 14:39:55 -04:00
Alessio Gravili
228e8f281a fix: admin.hidden not respected for RSCs, render-field server function not respecting field-overrides client-side (#13869)
This PR fixes 2 issues:

- the `fieldConfig.admin.hidden` property had no effect for react server
components, because the RSC was returned before we're checking for
`admin.hidden` in RenderFields
- the `render-field` server function did not propagate fieldConfig
overrides to the clientProps. This means overriding `admin.Label` had no
effect

Adds e2e tests for both issues
2025-09-22 14:36:41 -04:00
Patrik
cb23aaf5dc fix(translations): missing noLabelGroup translation key for Tamil translations (#13896) 2025-09-22 16:47:35 +00:00
Anil Shebin S J
7975fe3e16 feat(translations): add Tamil translations (#13788)
Add Tamil translations

They hadn't been implemented yet

Added new translation files following the existing structure and
translated all relevant messages into Tamil.

<!--

Thank you for the PR! Please go through the checklist below and make
sure you've completed all the steps.

Please review the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository if you haven't already.

The following items will ensure that your PR is handled as smoothly as
possible:

- PR Title must follow conventional commits format. For example, `feat:
my new feature`, `fix(plugin-seo): my fix`.
- Minimal description explained as if explained to someone not
immediately familiar with the code.
- Provide before/after screenshots or code diffs if applicable.
- Link any related issues/discussions from GitHub or Discord.
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?

### Why?

### How?

Fixes #

-->

Signed-off-by: Anil Shebin S J <anilshebin@gmail.com>
2025-09-22 10:13:53 -04:00
Sasha
1072171f97 feat: support hasMany virtual relationship fields (#13879)
This PR adds support for the following configuration:
```ts
const config = {
  collections: [
    {
      slug: 'categories',
      fields: [
        {
          name: 'title',
          type: 'text',
        },
      ],
    },
    {
      slug: 'posts',
      fields: [
        {
          name: 'title',
          type: 'text',
        },
        {
          name: 'categories',
          type: 'relationship',
          relationTo: 'categories',
          hasMany: true,
        },
      ],
    },
    {
      slug: 'examples',
      fields: [
        {
          name: 'postCategoriesTitles',
          type: 'text',
          virtual: 'post.categories.title',
          // hasMany: true - added automatically during the sanitization
        },
        {
          type: 'relationship',
          relationTo: 'posts',
          name: 'post',
        },
        {
          name: 'postsTitles',
          type: 'text',
          virtual: 'posts.title',
          // hasMany: true - added automatically during the sanitization
        },
        {
          type: 'relationship',
          relationTo: 'posts',
          name: 'posts',
          hasMany: true,
        },
      ],
    },
  ],
}
```

In the result:
`postsTitles` - will be always populated with an array of posts titles.
`postCategoriesTitles` - will be always populated with an array of the
categories titles that are related to this post

The virtual `text` field is sanitizated to `hasMany: true`
automatically, but you can specify that manually as well.
2025-09-19 21:04:07 +03:00
German Jablonski
207caa570c fix(richtext-lexical): prevent disallowed headings to be pasted (#13765)
Fixes #13705

With this PR, if the editor detects a disallowed heading in
`HeadingFeature`, it automatically converts it to the lowest allowed
heading.

I've also verified that disallowed headings aren't introduced when
pasting from the clipboard if the HeadingFeature isn't registered at
all. The reason this works is because the LexicalEditor doesn't have the
HeadingNode in that case.
2025-09-19 11:02:18 -07:00
Jens Becker
77cac30046 perf(ui): use select API in RelationshipProvider to speed up load times in RelationshipCell (#13832)
### What?
Optimize the `RelationshipProvider` to only select the `useAsTitle`
field when fetching documents via the REST API. This reduces payload
size and speeds up loading of the related document title in
the`RelationshipCell` in the table view.

### Why?
Previously, when a document had a relationship field, the full document
data was requested in the table view, even though the relationship cell
only shows the title in the UI. On large collections, this caused
unnecessary overhead and slower UI performance.

### How?
Applies a select to the REST API request made in the
`RelationshipProvider`, limiting the responses to the `useAsTitle` field
only.

### Notes
- I’m not entirely sure whether this introduces a breaking change. If it
does, could you suggest a way to make this behavior opt-in?
- For upload enabled collections, the full document must be requested,
because the relationship cell needs access to fields like `mimeType`,
`thumbailURL` etc.
- I hope we can find a way to get this merged. In the Payload projects I
work on, this change has significantly improved list view performance.


Similar to #13228
2025-09-19 11:20:55 -04:00
Sasha
22ae9fa9d0 fix(db-postgres): localized relationships inside blocks (#13760)
Fixes https://github.com/payloadcms/payload/issues/12463 and
https://github.com/payloadcms/payload/issues/13747
2025-09-19 09:28:12 -04:00
Anders Semb Hermansen
b7f6e3c294 refactor(translations): correct i18n translation for Norwegian (#13854)
Improve, correct and fix inconsistencies in Norwegian translations
2025-09-18 15:05:13 -07:00
Alessio Gravili
1c89291fac feat(richtext-lexical): utility render lexical field on-demand (#13657)
## 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
2025-09-18 15:01:12 -07:00
Jarrod Flesch
425172c061 fix: exits handlers loop if response is returned (#13866)
Fixes https://github.com/payloadcms/payload/issues/13762

Adjusts custom upload handlers for loop to match the documentation,
exiting the loop if a response is returned.
2025-09-18 15:24:04 -04:00
Jarrod Flesch
667c4f1634 fix(ui): bulk upload with locale param (#13865)
Fixes https://github.com/payloadcms/payload/issues/13859

Attaches locale to bulk upload post requests.
2025-09-18 14:39:13 -04:00
Jarrod Flesch
82aade2239 fix(ui): ensures visible list view thumbnails with enableListViewSelectAPI (#13864)
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.
2025-09-18 14:11:40 -04:00
Jarrod Flesch
83b6e3757d fix(graphql): graphql tab schema not handling tabs correctly (#13850)
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.
2025-09-18 09:34:50 -07:00
Jacob Fletcher
42b5935772 chore: consolidate canAccessAdmin logic (#13849)
Consolidates the logic for admin access control for server functions,
etc. behind a standard `canAccessAdmin` function.
2025-09-18 09:35:33 -04:00
Jarrod Flesch
5241113809 fix(ui): respect editorOptions, prevent field from flashing on save (#13848)
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.
2025-09-17 15:28:56 -04:00
Jacob Fletcher
9a8e3f817f chore(deps): bump @faceless-ui/modal to v3.0.0 (#13842)
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.
2025-09-17 15:51:11 +00:00
Jarrod Flesch
33228d9014 fix(ui): set prefetch false on Link buttons (#13846)
Fixes https://github.com/payloadcms/payload/issues/13834

Brings back `prefetch={false}` from
https://github.com/payloadcms/payload/pull/9020/files#diff-a2b1253ad6d1c9dde331641afc52893d73be7d3449c25e44b81066a839fef85dR152

I believe the prop was mistakenly removed, this PR adds it back.
2025-09-17 15:50:20 +00:00
Sasha
d0543a463f fix: support hasMany: true relationships in findDistinct (#13840)
Previously, the `findDistinct` operation didn't work correctly for
relationships with `hasMany: true`. This PR fixes it.
2025-09-17 18:21:24 +03:00
Jarrod Flesch
a26d8d9554 fix: removes select argument from create operation db calls (#13841)
Extension of https://github.com/payloadcms/payload/pull/13809

Fixes https://github.com/payloadcms/payload/issues/13769

Removes select arg from create operation and allows afterRead to filter
out select fields.

Leaving the select argument will create documents with only the selected
data.
2025-09-17 10:40:31 -04:00
Elliot DeNolf
ae3b923139 chore(release): v3.56.0 [skip ci] 2025-09-17 09:31:12 -04:00
Jarrod Flesch
c0684e354c fix(ui): pass locale arg in query params for folder operations (#13837)
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.
2025-09-17 09:16:54 -04:00
Alessio Gravili
8d3b146d2d fix(next): unnamed, unlabeled groups displayed without label in version view (#13831)
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
2025-09-17 11:38:07 +03:00
Alessio Gravili
433c5136fc fix(next): sparse block and arrays diffs were not rendered correctly (#13829)
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
2025-09-17 11:36:34 +03:00
Jonathan Elmgren
91e7f0c2e1 fix(ui): correct field path in inline create drawer for auth fields (#13815)
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.
2025-09-16 22:29:54 +00:00
Jarrod Flesch
dc732b8f52 fix(ui): populate nested fields for enableListViewSelectAPI (#13827)
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
  }
}
```
2025-09-16 21:23:08 +00:00
Sasha
a9553925f6 fix: add missed pagination property to findVersions and findGlobalVersions and handle it properly (#13763)
* 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.
2025-09-16 17:05:54 -04:00
Jacob Fletcher
731c4fb700 fix(ui): cross-domain server-side live preview throws postMessage error (#13825)
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
2025-09-16 16:02:56 -04:00
Sasha
24ace70b58 fix(db-mongodb): support 2x and more relationship sorting (#13819)
Previously, sorting like:
`sort: 'relationship.anotherRelationship.title'` (and more nested)
didn't work with the MongoDB adapter, this PR fixes this.
2025-09-16 21:03:48 +03:00
Patrik
5c02d17726 fix(ui): undefined operators for virtual field with unsupported field types (#13820)
### 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>
2025-09-16 10:27:34 -07:00
Patrik
3b13867aee fix: skip validation when trashing documents with empty required fields (#13807)
### 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
2025-09-16 07:09:39 -07:00
Slava Nossar
3acdbf6b25 feat: pass args to task onSuccess and onFail callbacks (#13269)
### 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>
2025-09-16 01:37:09 +00:00
Jacob Fletcher
a3acfeb871 feat(ui): export FieldPathContext (#13806)
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
2025-09-15 21:27:08 +00:00
Philipp Schneider
a2c31fa44a fix(ui): prevent form validation after draft submit error (#12918)
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>
2025-09-15 17:10:47 -04:00
Patrik
faed3aaf26 fix: versions created with incomplete data when using select parameter (#13809)
### 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
2025-09-15 14:00:04 -07:00