Commit Graph

5101 Commits

Author SHA1 Message Date
Jacob Fletcher
3229b9a4a6 docs: dedicated custom components docs (#10987)
Adds a dedicated "Custom Components" section to the docs.

As users become familiar with building custom components, not all areas
that support customization are well documented. Not only this, but the
current pattern does not allow for deep elaboration on these concepts
without their pages growing to an unmanageable size. Custom components
in general is a large enough topic to merit a standalone section with
subpages. This change will make navigation much more intuitive, help
keep page size down, and provide room to document every single available
custom component with snippets to show exactly how they are typed, etc.

This is a substantial change to the docs, here is the overview: 

- The "Admin > Customizing Components" doc is now located at "Custom
Components > overview"
- The "Admin > Views" doc is now located at "Custom Components > Custom
Views"
- There is a new "Custom Components > Edit View" doc
- There is a new "Custom Components > List View" doc
- The information about root components within the "Admin > Customizing
Components" doc has been moved to a new "Custom Components > Root
Components" doc
- The information about custom providers within the "Admin > Customizing
Components" doc has been moved to a new "Custom Components > Custom
Providers" doc

Similar to the goals of #10743, #10742, and #10741.

Fixes #10872 and initial scaffolding for #10353.

Dependent on #11126.

This change will require the following redirects to be set up:

- `/docs/admin/hooks` → `/docs/admin/react-hooks`
- `/docs/admin/components` → `/docs/custom-components/overview`
- `/docs/admin/views` → `/docs/custom-components/views`
2025-02-17 14:08:40 -05:00
Jacob Fletcher
b80010b1a1 feat: view component types (#11126)
It is currently very difficult to build custom edit and list views or
inject custom components into these views because these views and
components are not explicitly typed. Instances of these components were
not fully type safe as well, i.e. when rendering them via
`RenderServerComponent`, there was little to no type-checking in most
cases.

There is now a 1:1 type match for all views and view components and they
now receive type-checking at render time.

The following types have been newly added and/or improved:

List View:

  - `ListViewClientProps`
  - `ListViewServerProps`
  - `BeforeListClientProps`
  - `BeforeListServerProps`
  - `BeforeListTableClientProps`
  - `BeforeListTableServerProps`
  - `AfterListClientProps`
  - `AfterListServerProps`
  - `AfterListTableClientProps`
  - `AfterListTableServerProps`
  - `ListViewSlotSharedClientProps`

Document View:

  - `DocumentViewClientProps`
  - `DocumentViewServerProps`
  - `SaveButtonClientProps`
  - `SaveButtonServerProps`
  - `SaveDraftButtonClientProps`
  - `SaveDraftButtonServerProps`
  - `PublishButtonClientProps`
  - `PublishButtonServerProps`
  - `PreviewButtonClientProps`
  - `PreviewButtonServerProps`

Root View:

  - `AdminViewClientProps`
  - `AdminViewServerProps`

General:

  - `ViewDescriptionClientProps`
  - `ViewDescriptionServerProps`

A few other changes were made in a non-breaking way:

  - `Column` is now exported from `payload`
  - `ListPreferences` is now exported from `payload`
  - `ListViewSlots` is now exported from `payload`
  - `ListViewClientProps` is now exported from `payload`
- `AdminViewProps` is now an alias of `AdminViewServerProps` (listed
above)
- `ClientSideEditViewProps` is now an alias of `DocumentViewClientProps`
(listed above)
- `ServerSideEditViewProps` is now an alias of `DocumentViewServerProps`
(listed above)
- `ListComponentClientProps` is now an alias of `ListViewClientProps`
(listed above)
- `ListComponentServerProps` is now an alias of `ListViewServerProps`
(listed above)
- `CustomSaveButton` is now marked as deprecated because this is only
relevant to the config (see correct type above)
- `CustomSaveDraftButton` is now marked as deprecated because this is
only relevant to the config (see correct type above)
- `CustomPublishButton` is now marked as deprecated because this is only
relevant to the config (see correct type above)
- `CustomPreviewButton` is now marked as deprecated because this is only
relevant to the config (see correct type above)
 
This PR _does not_ apply these changes to _root_ components, i.e.
`afterNavLinks`. Those will come in a future PR.

Related: #10987.
2025-02-17 14:08:23 -05:00
Sasha
938472bf1f fix: populate is ignored for nested relationships (#11227)
### What?
As described in https://github.com/payloadcms/payload/issues/11209,
previously, the `populate` argument was ignored for nested
relationships.

### Why?
`populate` should work for nested relationships, no matter where they
are in the tree.

### How?
Preserves the `populate` argument in the payload data loader.

Fixes https://github.com/payloadcms/payload/issues/11209
2025-02-17 19:44:21 +02:00
Alessio Gravili
6b9d81a746 fix: ensure leavesFirst option works correctly in traverseFields utility (#11219)
We have to ensure the arguments are handled wherever we push to the callback stack, not when we execute the callback stack.
2025-02-17 02:14:46 +00:00
Sasha
513ba636af fix(db-postgres): ensure countDistinct works correctly and achieve better performance when the query has table joins (#11208)
The fix, added in https://github.com/payloadcms/payload/pull/11096
wasn't sufficient enough. It did handle the case when the same query
path / table was joined twice and caused incorrect `totalDocs`, but it
didn't handle the case when `JOIN` returns more than 1 rows, which 2
added new assertions here check.

Now, we use `COUNT(*)` only if we don't have any joined tables. If we
do, instead of using `SELECT (COUNT DISTINCT id)` which as described in
the previous PR is _very slow_ for large tables, we use the following
query:

```sql
SELECT COUNT(1) OVER() as count -- window function, executes for each row only once
FROM users
LEFT JOIN -- ... here additional rows are added
WHERE -- ...
GROUP BY users.id -- this ensures we're counting only users without additional rows from joins. 
LIMIT 1 -- Since COUNT(1) OVER() executes and resolves before doing LIMIT, we can safely apply LIMIT 1.
```
2025-02-16 14:08:08 +02:00
Sasha
2ae670e0e4 test(db-mongodb): unit test assertion for relationship sanitization inside blockReferences (#11195)
This PR adds a new unit test assertion to existing
https://github.com/payloadcms/payload/blob/main/packages/db-mongodb/src/utilities/sanitizeRelationshipIDs.spec.ts
that ensures relationships are sanitized to `ObjectID`s correctly when
saved to the database for relationships inside the new `blockReferences`
https://github.com/payloadcms/payload/pull/10905
2025-02-16 14:07:58 +02:00
Riley Langbein
779f511fbf fix(ui): properly handle singular and plural bulk edit labels (#11198)
Bulk-many components are always using the plural format in their title,
even if only one document has been selected.

This fix checks the selection count and if its greater than 1 it will
show the plural format otherwise it will show the singular format.
2025-02-15 23:25:39 +00:00
Alessio Gravili
dc36572fbf feat(richtext-lexical): export INSERT_BLOCK_COMMAND and INSERT_INLINE_BLOCK_COMMAND (#11193)
Lexical checks commands by reference equality. This means that even if you re-define those commands in your own codebase using the same command `type` string, they will be treated as different commands.

If you wanted to dispatch the block creation command in your own codebase (e.g. from a different lexical feature, or any component within the editor), this will not be possible right now. See https://discord.com/channels/967097582721572934/1339557113898340352/1339557113898340352

This PR exports them from `@payloadcms/richtext-lexical/client`
2025-02-14 21:36:00 +00:00
Said Akhrarov
cba5c7bcac fix(ui): hide edit button on deleted relationship options (#11005)
### What?
This PR fixes an issue where a deleted relationship entry would lead to
a runtime error if the user clicked on the edit button in ui due to not
having a `doc` available in `handleServerFunction`.

### Why?
To prevent runtime errors during expected usage.

### How?
By hiding the edit button in entries that have been deleted. This is
done for entries where the user does not have read access already.

Fixes #11004

Before:

[Editing---Post-userdelete--before--Payload.webm](https://github.com/user-attachments/assets/33180eba-9be3-418f-92d2-3bad93e3dfae)

After:

[Editing---Post-userdelete--after--Payload.webm](https://github.com/user-attachments/assets/ba1a736b-3422-4fe0-93ae-7e8e6496d1bd)
2025-02-14 14:45:55 -05:00
Patrik
70db44f964 fix(next): document header padding on tablet sized screens (#11192)
This PR fixes an issue where padding around the `DocumentHeader`
component disappears at the `mid-break` viewport size.

The issue was caused by .doc-header applying padding-left: 0 and
padding-right: 0, which overrode the intended padding from the parent
Gutter component in certain scenarios.
2025-02-14 13:27:10 -05:00
Said Akhrarov
077fb3ab30 fix(ui): respect locale in buildTableState (#11147)
### What?
This PR fixes an issue where the `join` field table was not respecting
the locale selected in the admin ui localizer.

This also introduces an e2e test to the existing suite to catch this
issue.

### Why?
To properly render `join` field table data according to chosen and
configured locales.

### How?
Threading `req.locale` through to the `payload.find` call in
`buildTableState`.

Fixes #11134

Before:

[Editing---Category--join-locales-before-Payload.webm](https://github.com/user-attachments/assets/d77b71bb-f849-4be2-aa96-26dbfedb52d4)

After:

[Editing---Category--join-locales-after-Payload.webm](https://github.com/user-attachments/assets/0d1f7351-adf4-4bad-ac82-0fee67f8b66a)
2025-02-14 12:23:49 -05:00
Jarrod Flesch
b65ae073d2 fix(plugin-multi-tenant): corrects default value for tenantsArrayTenantFieldName (#11189)
Incorrect default value on exported `tenantsArrayField` field. Should
have been `tenant` but was using `tenants`. This affected the
multi-tenant example which uses a custom tenants array field.

You would not notice this issue unless you were using:
```ts
tenantsArrayField: {
  includeDefaultField: false,
}
```

Fixes https://github.com/payloadcms/payload/issues/11125
2025-02-14 10:55:19 -05:00
Sasha
b1734b0d38 fix(ui): hide array field "add" button if admin.readOnly: true is set (#11184)
Fixes https://github.com/payloadcms/payload/issues/11178
2025-02-14 09:06:46 -05:00
Jacob Fletcher
0a3820a487 fix(ui): unable to use browser back navigation after visiting list view (#11172) 2025-02-14 07:47:00 -05:00
Dan Ribbens
dd28959916 fix: join field does not show validation error (#11170)
### What?

Assuming you have a hook in your collection that is looking for certain
conditions to be met related to the join field. The way you would
prevent it is to throw a `new ValidationError()` with errors containing
the path of the field. Previously, the error message for the field would
not show in the admin UI at all.

### Why?

Users need to be able to see any custom error messages for joins field
in the UI so they can address the issue.

### How?

Adds an error class and display the FieldError in the Join field in the
UI component.
2025-02-13 23:09:04 -05:00
Said Akhrarov
12f51bad5f fix(db-mongodb): remove duplicative indexing of timestamps (#11028)
### What?
This PR removes a pair unnecessary calls to `schema.index` against the
timestamp fields. The issue is when a user sets `indexSortableFields` as
this is what will ultimately pass the predicate which then creates
duplicate indexes.

### Why?
These calls are redundant as `index` is [already
passed](https://github.com/payloadcms/payload/blob/main/packages/db-mongodb/src/models/buildSchema.ts#L69)
to the underlying fields base schema options in the process of
formatting and will already be indexed.

These warnings were surfaced after the bump to mongoose to version 8.9.5
as [in 8.9.3 mongoose began throwing these warnings to indicate
duplicative
indexes](https://github.com/Automattic/mongoose/releases/tag/8.9.3).

### How?
By removing these calls and, as a result, silencing the warnings thrown
by mongoose.
2025-02-13 23:07:24 -05:00
Alessio Gravili
4c8cafd6a6 perf: deduplicate blocks used in multiple places using new config.blocks property (#10905)
If you have multiple blocks that are used in multiple places, this can quickly blow up the size of your Payload Config. This will incur a performance hit, as more data is
1.  sent to the client (=> bloated `ClientConfig` and large initial html) and
2. processed on the server (permissions are calculated every single time you navigate to a page - this iterates through all blocks you have defined, even if they're duplicative)

This can be optimized by defining your block **once** in your Payload Config, and just referencing the block slug whenever it's used, instead of passing the entire block config. To do this, the block can be defined in the `blocks` array of the Payload Config. The slug can then be passed to the `blockReferences` array in the Blocks Field - the `blocks` array has to be empty for compatibility reasons.

```ts
import { buildConfig } from 'payload'
import { lexicalEditor, BlocksFeature } from '@payloadcms/richtext-lexical'

// Payload Config
const config = buildConfig({
  // Define the block once
  blocks: [
    {
      slug: 'TextBlock',
      fields: [
        {
          name: 'text',
          type: 'text',
        },
      ],
    },
  ],
  collections: [
    {
      slug: 'collection1',
      fields: [
        {
          name: 'content',
          type: 'blocks',
          // Reference the block by slug
          blockReferences: ['TextBlock'],
          blocks: [], // Required to be empty, for compatibility reasons
        },
      ],
    },
     {
      slug: 'collection2',
      fields: [
        {
          name: 'editor',
          type: 'richText',
          editor: lexicalEditor({
            BlocksFeature({
              // Same reference can be reused anywhere, even in the lexical editor, without incurred performance hit
              blocks: ['TextBlock'],
            })
          })
        },
      ],
    },
  ],
})
```

## v4.0 Plans

In 4.0, we will remove the `blockReferences` property, and allow string block references to be passed directly to the blocks `property`. Essentially, we'd remove the `blocks` property and rename `blockReferences` to `blocks`.

The reason we opted to a new property in this PR is to avoid breaking changes. Allowing strings to be passed to the `blocks` property will prevent plugins that iterate through fields / blocks from compiling.

## PR Changes

- Testing: This PR introduces a plugin that automatically converts blocks to block references. This is done in the fields__blocks test suite, to run our existing test suite using block references.

- Block References support: Most changes are similar. Everywhere we iterate through blocks, we have to now do the following:
1. Check if `field.blockReferences` is provided. If so, only iterate through that.
2. Check if the block is an object (= actual block), or string
3. If it's a string, pull the actual block from the Payload Config or from `payload.blocks`.

The exception is config sanitization and block type generations. This PR optimizes them so that each block is only handled once, instead of every time the block is referenced.

## Benchmarks

60 Block fields, each block field having the same 600 Blocks.

### Before:
**Initial HTML:** 195 kB
**Generated types:** takes 11 minutes, 461,209 lines

https://github.com/user-attachments/assets/11d49a4e-5414-4579-8050-e6346e552f56

### After:
**Initial HTML:** 73.6 kB
**Generated types:** takes 2 seconds, 35,810 lines

https://github.com/user-attachments/assets/3eab1a99-6c29-489d-add5-698df67780a3

### After Permissions Optimization (follow-up PR)
Initial HTML: 73.6 kB

https://github.com/user-attachments/assets/a909202e-45a8-4bf6-9a38-8c85813f1312


## Future Plans

1. This PR does not yet deduplicate block references during permissions calculation. We'll optimize that in a separate PR, as this one is already large enough
2. The same optimization can be done to deduplicate fields. One common use-case would be link field groups that may be referenced in multiple entities, outside of blocks. We might explore adding a new `fieldReferences` property, that allows you to reference those same `config.blocks`.
2025-02-14 00:08:20 +00:00
lucasbajoua
d47c980509 fix(ui): url encode imageCacheTag for media on dashboard (#11164)
### What?
URL encodes the imageCacheTag query param used to render Media on the
Admin Dashboard

### Why?
The format of the timestamp used as the `imageCacheTag` is causing an
`InvalidQueryStringException` when hosting with Cloudfront + Lambda
(SST/OpenNext)
[See issue](https://github.com/payloadcms/payload/issues/11163)

### How?
Uses `encodeURIComponent` on instances where the `imageCacheTag` is
being formatted for the request URL. (In EditUpload, Thumbnail, and
PreviewSizes)

Fixes #11163

---------

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2025-02-13 21:42:52 +00:00
Kendell Joseph
7f124cfe93 fix(ui): json schema (#11123)
Fixes https://github.com/payloadcms/payload/issues/10166

[fix: json schema #11123 - Watch
Video](https://www.loom.com/share/0f5199234ad1486f910a39165de837e5)

- Using the same `URI` with the same `schema` will throw an error.
- Using the same `URI` with a different `schema` will throw a warning
(but still work).

If you want to use the same schema on a different field, you need to
define a different URI.
2025-02-13 16:26:57 -05:00
Said Akhrarov
6901b2639d fix(ui): prevent omitting fileSize from non-images (#11146)
### What?
This PR displays file size in upload cards for all upload mimetypes. The
current behavior hides this metric from the user if the file mimetype
does not start with `image`.

### Why?
Showing end-users and editors a file size is universally useful - not
only for images, but for all types of files that can be uploaded via the
upload field.

### How?
By making the predicate that adds this metric less restrictive. Instead
of checking if the mimetype is image-like, it checks if the file size is
truthy.

Before:

![image](https://github.com/user-attachments/assets/949e3be9-6dca-43c3-b2f8-a7e91307e48e)

After:

![image](https://github.com/user-attachments/assets/cb500390-dc64-48e3-a87c-e4ec4d19d019)
2025-02-13 16:05:12 -05:00
Jacob Fletcher
16d75a7c7b feat(ui): refines progress bar animation curve (#11167)
Refines the animation curve used in the new progress bar for route
transitions. Uses an exponential acceleration and decay so that the
indicator progresses quickly at the onset, then gradually decelerates at
it approaches completion. Also caps the progress at ~90%.

Introduced in #9275.
2025-02-13 20:37:30 +00:00
Jacob Fletcher
de68ef4548 fix(ui): adds delay to progress bar for fast networks (#11157)
On fast networks where page transitions are quick, such as local dev in
most cases, the progress bar should not render. This leads to a constant
flashing of the progress bar at the top of the screen and does not
provide any value.

The fix is to add a delay to the initial rendering of the progress bar,
and only show if the transition takes longer than _n_ milliseconds. This
value can be adjusted as needed, but right now is set to 150ms.

Introduced in #9275.
2025-02-13 12:35:41 -05:00
Jacob Fletcher
f4639c418f chore(deps): bumps @monaco-editor/react to v4.7.0 to suppress react 19 warnings (#11161)
The `@monaco-editor/react` package now includes React 19 in its peer
dependencies thanks to
https://github.com/suren-atoyan/monaco-react/pull/651. This package was
also incorrectly listed in `payload` as a regular dependency, but since
it's only used for type imports, it should be listed a dev dependency
instead.
2025-02-13 12:24:53 -05:00
Jacob Fletcher
cd1117515b refactor(ui): deprecates Link props (#11155)
Deprecates all cases where `Link` could be sent as a prop. This was a
relic from the past, where we attempted to make our UI library
router-agnostic. This was a pipe dream and created more problems than it
solved, for example the logout button was missing this prop, causing it
to render an anchor tag and perform a hard navigation (caught in #9275).

Does so in a non-breaking way, where these props are now optional and
simply unused, as opposed to removing them outright.
2025-02-13 11:10:57 -05:00
Jacob Fletcher
3f550bc0ec feat: route transitions (#9275)
Due to nature of server-side rendering, navigation within the admin
panel can lead to slow page response times. This can lead to the feeling
of an unresponsive app after clicking a link, for example, where the
page remains in a stale state while the server is processing. This is
especially noticeable on slow networks when navigating to data heavy or
process intensive pages.

To alleviate the bad UX that this causes, the user needs immediate
visual indication that _something_ is taking place. This PR renders a
progress bar in the admin panel which is immediately displayed when a
user clicks a link, and incrementally grows in size until the new route
has loaded in.

Inspired by https://github.com/vercel/react-transition-progress.

Old:

https://github.com/user-attachments/assets/1820dad1-3aea-417f-a61d-52244b12dc8d

New:

https://github.com/user-attachments/assets/99f4bb82-61d9-4a4c-9bdf-9e379bbafd31

To tie into the progress bar, you'll need to use Payload's new `Link`
component instead of the one provided by Next.js:

```diff
- import { Link } from 'next/link'
+ import { Link } from '@payloadcms/ui'
```

Here's an example:

```tsx
import { Link } from '@payloadcms/ui'

const MyComponent = () => {
  return (
    <Link href="/somewhere">
      Go Somewhere
    </Link>
  )
}
```

In order to trigger route transitions for a direct router event such as
`router.push`, you'll need to wrap your function calls with the
`startRouteTransition` method provided by the `useRouteTransition` hook.

```ts
'use client'
import React, { useCallback } from 'react'
import { useTransition } from '@payloadcms/ui'
import { useRouter } from 'next/navigation'

const MyComponent: React.FC = () => {
  const router = useRouter()
  const { startRouteTransition } = useRouteTransition()
 
  const redirectSomewhere = useCallback(() => {
    startRouteTransition(() => router.push('/somewhere'))
  }, [startRouteTransition, router])
 
  // ...
}
```

In the future [Next.js might provide native support for
this](https://github.com/vercel/next.js/discussions/41934#discussioncomment-12077414),
and if it does, this implementation can likely be simplified.

Of course there are other ways of achieving this, such as with
[Suspense](https://react.dev/reference/react/Suspense), but they all
come with a different set of caveats. For example with Suspense, you
must provide a fallback component. This means that the user might be
able to immediately navigate to the new page, which is good, but they'd
be presented with a skeleton UI while the other parts of the page stream
in. Not necessarily an improvement to UX as there would be multiple
loading states with this approach.

There are other problems with using Suspense as well. Our default
template, for example, contains the app header and sidebar which are not
rendered within the root layout. This means that they need to stream in
every single time. On fast networks, this would also lead to a
noticeable "blink" unless there is some mechanism by which we can detect
and defer the fallback from ever rendering in such cases. Might still be
worth exploring in the future though.
2025-02-13 09:48:13 -05:00
Jessica Chowdhury
6bfa66c9ff chore: typo in migrate:fresh command (#11140)
### What?
The migration CLI help says `migration:fresh` is available to use - this
doesn't exist, the command should be `migrate:fresh`.

Closes #10965 & #10967
2025-02-12 16:23:11 +00:00
Germán Jabloñski
6eee787493 chore: add typescript-strict-plugin to the payload package for incremental file-by-file migration [skip lint] (#11133)
### What?

Implement the
[typescript-strict-plugin](https://github.com/allegro/typescript-strict-plugin)
plugin in the payload (core) package.

### Why?

1. One strategy for incremental migration is to enable strictness rules
in tsconfig, fix some errors, and push them without committing the
changes to tsconfig.json. However, this is not feasible for a package as
large as Payload that has over 1000 typescript errors. Until the work is
done, new contributions would undo the work being done.
2. Even if no migration work is done after this PR, this change already
improves the strictness of the package. 89 of the 311 files within the
package already satisfy strict mode. This PR only adds a comment
`@ts-strict-ignore` to files that had at least one compilation error.
This way, the propagation of errors in those files is stopped.
3. New files created in the package are strict by default (this was the
main improvement in version 2 of `typescript-strict-plugin`).

I recommend starting the migration with this package because it is the
one that almost all the others depend on. Once we finish this package,
we can repeat the same strategy on another one, or use the strategy I
mentioned in point 1 if the package is small.

### Note

If you don't see errors in the IDE when you uncomment `//
@ts-strict-ignore`, try restarting the typescript server or VSCode


### How to contribute to the migration ❤️

1. Remove `// @ts-strict-ignore` comments from 1 or more files
2. Fix the pending errors (they should appear in your IDE's intellisense
or when running `cd packages/payload` + `pnpm build:types`
3. Submit your PR!

Important: You don't need to fix everything at once! Furthermore, I
recommend breaking this down into very small PRs to trace potential
issues later if there are any. So if you have 5 minutes, tackle a small
file—every bit counts! 🤗
2025-02-12 11:06:03 -05:00
Hulpoi George-Valentin
30c77d8137 fix(ui): safe call within useEffect teardown (#11135)
`NavProvider` useEffects teardown is trying to set `style` on an element
that may not exist. The original code produces the following error:


![image](https://github.com/user-attachments/assets/11a83fbe-67eb-42a9-bd78-749ea98b67c5)

![image](https://github.com/user-attachments/assets/28ed2534-2387-416b-8191-d68b478161aa)

Therefore, a condition has been added to check if `navRef.current` is
truthy.
2025-02-12 15:30:31 +00:00
Germán Jabloñski
09ada20ce8 chore(richtext-lexical): fix unchecked indexed access, make richtext-lexical full ts strict (part 5/5) (#11132)
This PR concludes the series to make `richtext-lexical` full strict in
TypeScript 🥳
2025-02-12 12:28:28 +00:00
David Hu
9068bdacae fix(richtext-lexical): add container div to table element to allow horizontal scroll in HTML and JSX converters (#11119)
### What?
Add a `div` wrapper to `table` tag in `TableFeature`

### Why?
This allows for adding horizontal scrolling to the table. We use table
in our blog, however, on mobile, the content is wider than the screen
width, and causes a horizontal scroll of all the content. I attached a
video to show. You can see it by visiting the page on mobile
https://magichour.ai/blog/10-best-ai-video-generators


https://github.com/user-attachments/assets/55778765-697e-426d-ac8a-1b0913adac13


Adding this container div allow me to target the div with a style
```css
.lexical-table-container {
  overflow-x: scroll;
}
```

### How?
![Screenshot 2025-02-11 at 11 26
18 AM](https://github.com/user-attachments/assets/873050b3-79b9-49ec-85ed-297286813577)

I tested this change by manually editing the HTML in our blog to include
the `div` with the overflow style, and it fixes the issue.

Also, verified just adding the `div` did not change anything related to
the rendered output.
2025-02-12 09:10:49 -03:00
Alessio Gravili
155f9f80fe feat: add siblingFields arg to field hooks (#11117)
This PR adds a new `siblingFields` argument to field hooks. This allows
us to dramatically simplify the `lexicalHTML` field, which previously
had to use a complex `findFieldPathAndSiblingFields` function that
deeply traverses the entire `CollectionConfig` just to find the sibling
fields.
2025-02-11 14:22:31 -07:00
Alessio Gravili
2056e9b740 fix(richtext-lexical): reliably install exact lexical version by removing it from peerDeps (#11122)
This will hopefully allow pnpm to reliably install the correct lexical version, as lexical is now solely part of our `dependencies`. Currently, pnpm completely disregards lexical version bumps until the user deletes both the lockfile and their `node_modules` folder.

The downside of this is that pnpm will no longer throw a warning if payload is installed in a project with a mismatching lexical version. However, noone read that warning anyways, and our runtime dependency checker is more reliable.
2025-02-11 21:01:33 +00:00
Germán Jabloñski
002e921ede chore(richtext-lexical): improve types of UploadData (#10982)
One step closer to being able to remove `noUncheckedIndexedAccess` in
`packages/richtext-lexical/tsconfig.json`.

I'm introducing UploadData_P4 which is a more precise version of
UploadData. I'm doing it as a different type because there's a chance
it'll be a breaking change for some users.

UploadData is used in many places, but I'm currently replacing it only
in
`packages/richtext-lexical/src/exports/react/components/RichText/converter/converters/upload.tsx`,
because in the other files it's too rooted to other types like
UploadNode.
2025-02-11 14:24:05 -05:00
Sasha
ececa65c78 fix(next): pre-flight OPTIONS request errors from the graphql endpoint (#11103)
Fixes https://github.com/payloadcms/payload/issues/11101
2025-02-11 13:22:16 -05:00
Germán Jabloñski
7a400a7a79 fix(richtext-lexical): unindent button in toolbar is never active (#11089)
Fixes #11082

In addition to fixing the bug described in that issue, I'm fixing the
problem where when outdenting, indent 0 blocks stay the same. The new
logic verifies that all selected blocks can be outdented.

It remains to be done the same with the tab and shift + tab commands.
2025-02-11 13:21:58 -05:00
Jacob Fletcher
2a0094def7 fix(ui): relationship filterOptions not applied within the list view (#11008)
Fixes #10440. When `filterOptions` are set on a relationship field,
those same filters are not applied to the `Filter` component within the
list view. This is because `filterOptions` is not being thread into the
`RelationshipFilter` component responsible for populating the available
options.

To do this, we first need to be resolve the filter options on the server
as they accept functions. Once resolved, they can be prop-drilled into
the proper component and appended onto the client-side "where" query.

Reliant on #11080.
2025-02-11 13:20:55 -05:00
Elliot DeNolf
480c6e7c09 chore(release): v3.23.0 [skip ci] 2025-02-11 12:53:51 -05:00
Elliot DeNolf
da77f99df4 fix(payload-cloud): handle socket closures (#11113)
- Port #11015 to handle sockets
- Fix `AccessDenied` errors to properly return 404 in specific scenarios
- Add optional `debug` flag
2025-02-11 12:50:46 -05:00
Jacob Fletcher
da6511eba9 fix(ui): relationship filter renders stale values when changing fields (#11080)
Fixes #9873. The relationship filter in the "where" builder renders
stale values when switching between fields or adding additional "and"
conditions. This was because the `RelationshipFilter` component was not
responding to changes in the `relationTo` prop and failing to reset
internal state when these events took place.

While it sounds like a simple fix, it was actually quite extensive. The
`RelationshipFilter` component was previously relying on a `useEffect`
that had a callback in its dependencies. This was causing the effect to
run uncontrollably using old references. To avoid this, we use the new
`useEffectEvent` approach which allows the underlying effect to run much
more precisely. Same with the `Condition` component that wraps it. We
now run callbacks directly within event handlers as much as possible,
and rely on `useEffectEvent` _only_ for debounced value changes.

This component was also unnecessarily complex...and still is to some
degree. Previously, it was maintaining two separate refs, one to track
the relationships that have yet to fully load, and another to track the
next pages of each relationship that need to load on the next run. These
have been combined into a single ref that tracks both simultaneously, as
this data is interrelated.

This change also does some much needed housekeeping to the
`WhereBuilder` by improving types, defaulting the operator field, etc.

Related: #11023 and #11032

Unrelated: finds a few more instances where the new `addListFilter`
helper from #11026 could be used. Also removes a few duplicative tests.
2025-02-11 09:45:41 -05:00
Paul
c18c58e1fb feat(ui): add timezone support to scheduled publish (#11090)
This PR extends timezone support to scheduled publish UI and collection,
the timezone will be stored on the `input` JSON instead of the
`waitUntil` date field so that we avoid needing a schema migration for
SQL databases.


![image](https://github.com/user-attachments/assets/0cc6522b-1b2f-4608-a592-67e3cdcdb566)

If a timezone is selected then the displayed date in the table will be
formatted for that timezone.

Timezones remain optional here as they can be deselected in which case
the date will behave as normal, rendering and formatting to the user's
local timezone.

For the backend logic that can be left untouched since the underlying
date values are stored in UTC the job runners will always handle this
relative time by default.

Todo:
- [x] add e2e to this drawer too to ensure that dates are rendered as
expected
2025-02-10 19:48:52 -05:00
Paul
36168184b5 fix(ui): incorrectly incrementing version counts if maxPerDoc is set to 0 (#11097)
Fixes https://github.com/payloadcms/payload/issues/9891

We were incorrectly setting max version count to 0 if it was configured
as maxPerDoc `0` due to `Math.min`
2025-02-10 23:28:40 +00:00
Sasha
98fec35368 fix(db-postgres): incorrect pagination results when querying hasMany relationships multiple times (#11096)
Fixes https://github.com/payloadcms/payload/issues/10810

This was caused by using `COUNT(*)` aggregation instead of
`COUNT(DISTINCT table.id)`. However, we want to use `COUNT(*)` because
`COUNT(DISTINCT table.id)` is slow on large tables. Now we fallback to
`COUNT(DISTINCT table.id)` only when `COUNT(*)` cannot work properly.

Example of a query that leads to incorrect `totalDocs`:
```ts
const res = await payload.find({
  collection: 'directors',
  limit: 10,
  where: {
    or: [
      {
        movies: {
          equals: movie2.id,
        },
      },
      {
        movies: {
          equals: movie1.id,
        },
      },
      {
        movies: {
          equals: movie1.id,
        },
      },
    ],
  },
})
```
2025-02-11 01:16:18 +02:00
Jarrod Flesch
fde526e07f fix: set initialValues alongside values during onSuccess (#10825)
### What?
Initial values should be set from the server when `acceptValues` is
true.

### Why?
This is needed since we take the values from the server after a
successful form submission.

### How?
Add `initialValue` into `serverPropsToAccept` when `acceptValues` is
true.

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

---------

Co-authored-by: Alessio Gravili <alessio@gravili.de>
2025-02-10 16:49:06 -05:00
James Mikrut
5dadccea39 feat: adds new jobs.shouldAutoRun property (#11092)
Adds a `shouldAutoRun` property to the `jobs` config to be able to have
fine-grained control over if jobs should be run. This is helpful in
cases where you may have many horizontally scaled compute instances, and
only one instance should be responsible for running jobs.
2025-02-10 21:43:20 +00:00
Jarrod Flesch
d2fe9b0807 fix(db-mongodb): ensures same level operators are respected (#11087)
### What?
If you had multiple operator constraints on a single field, the last one
defined would be the only one used.

Example:
```ts
where: {
  id: {
    in: [doc2.id],
    not_in: [], // <-- only respected this operator constraint
  },
}
```

and
```ts
where: {
  id: {
    not_in: [],
    in: [doc2.id], // <-- only respected this operator constraint
  },
}
```

They would yield different results.

### Why?
The results were not merged into an `$and` query inside parseParams.

### How?
Merges the results within an `$and` constraint.

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

Supersedes https://github.com/payloadcms/payload/pull/11011
2025-02-10 16:29:08 -05:00
Markus
95ec57575d fix(storage-s3): sockets not closing (#11015)
### What?
Within collections using the `storage-s3` plugins, we eventually start
receiving the following warnings:

`@smithy/node-http-handler:WARN socket usage at capacity=50 and 156
additional requests are enqueued. See
https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html
or increase socketAcquisitionWarningTimeout=(millis) in the
NodeHttpHandler config.`

Also referenced in this issue: #6382

The
[solution](https://github.com/payloadcms/payload/issues/6382#issuecomment-2325468104)
provided by @denolfe in that issue only delayed the reappearance of the
problem somewhat, but did not resolve it.

### Why?
As far as I understand, in the `staticHandler` of the plugin, when
getting items from storage, and they are currently cached, the cached
results are immediately returned without handling the stream. As per
[this](https://github.com/aws/aws-sdk-js-v3/blob/main/supplemental-docs/CLIENTS.md#nodejs-requesthandler)
entry in the aws-sdk docs, if the streaming response is not read, or
manually destroyed, a socket might not properly close.

### How?
Before returning the cached items, manually destroy the streaming
response to make certain the socket is being properly closed.
Additionally, add an error check to also consume/destroy the streaming
response in case an error occurs, to not leave orphaned sockets.

Fixes #6382
2025-02-10 15:17:30 -05:00
Paul
430ebd42ff feat: add timezone support on date fields (#10896)
Adds support for timezone selection on date fields.

### Summary

New `admin.timezones` config:

```ts
{
  // ...
  admin: {
    // ...
    timezones: {
      supportedTimezones: ({ defaultTimezones }) => [
        ...defaultTimezones,
        { label: '(GMT-6) Monterrey, Nuevo Leon', value: 'America/Monterrey' },
      ],
      defaultTimezone: 'America/Monterrey',
    },
  }
}
```

New `timezone` property on date fields:

```ts
{
  type: 'date',
  name: 'date',
  timezone: true,
}
```

### Configuration

All date fields now accept `timezone: true` to enable this feature,
which will inject a new field into the configuration using the date
field's name to construct the name for the timezone column. So
`publishingDate` will have `publishingDate_tz` as an accompanying
column. This new field is inserted during config sanitisation.

Dates continue to be stored in UTC, this will help maintain dates
without needing a migration and it makes it easier for data to be
manipulated as needed. Mongodb also has a restriction around storing
dates only as UTC.

All timezones are stored by their IANA names so it's compatible with
browser APIs. There is a newly generated type for `SupportedTimezones`
which is reused across fields.

We handle timezone calculations via a new package `@date-fns/tz` which
we will be using in the future for handling timezone aware scheduled
publishing/unpublishing and more.

### UI

Dark mode

![image](https://github.com/user-attachments/assets/fcebdb7f-be01-4382-a1ce-3369f72b4309)

Light mode

![image](https://github.com/user-attachments/assets/dee2f1c6-4d0c-49e9-b6c8-a51a83a5e864)
2025-02-10 15:02:53 -05:00
Germán Jabloñski
fa18923317 fix(richtext-lexical): improve keyboard navigation on DecoratorNodes (#11022)
Fixes #8506



https://github.com/user-attachments/assets/a5e26f18-2557-4f19-bd89-73f246200fa5
2025-02-10 19:22:25 +00:00
James Mikrut
91a0f90649 fix(next): allows relative live preview urls (#11083)
We now properly allow relative live preview URLs which is handy if
you're deploying on a platform like Vercel and do not know what the
preview domain is going to end up being at build time.

This PR also removes some problematic code in the website template which
hard-codes the protocol to `https://` in production even if you're
running locally.

Fixes #11070
2025-02-10 18:20:34 +00:00
Alessio Gravili
9fb7160c2c fix(richtext-lexical): toggling between internal and custom links does not update fields (#11074)
Fixes https://github.com/payloadcms/payload/issues/11062

In https://github.com/payloadcms/payload/pull/9869 we fixed a bug where `data` passed to lexical fields did not reflect the document data. Our LinkFeature, however, was depending on this incorrect behavior. This PR updates the LinkFeature field conditions to depend on the `siblingData` instead of `data`
2025-02-10 07:05:23 +00:00