Compare commits

...

596 Commits

Author SHA1 Message Date
Elliot DeNolf
8b95218577 chore(release): v3.0.0-beta.43 [skip ci] 2024-06-07 17:45:28 -04:00
Jarrod Flesch
a79d23c631 chore: adjusts test config for draft validation (#6678) 2024-06-07 16:01:03 -04:00
Jarrod Flesch
52c81ad525 feat: adds draft validation option (#6677)
## Description

Allows draft validation to be enabled at the config level.

You can enable this by:
```ts
// ...collectionConfig
versions: {
  drafts: {
    validate: true // defaults to false
  }
}
```
2024-06-07 15:22:03 -04:00
Paul
8ec836737e chore: add turbo resolveAlias mock alias to hide webpack warnings (#6676) 2024-06-07 17:23:35 +00:00
Paul
e4a90294ea feat(plugin-redirects)!: update fields overrides to use a function (#6675)
## Description

Updates the `fields` override in plugin redirects to allow for
overriding

```ts
// before
overrides: {
  fields: [
    {
      type: 'text',
      name: 'customField',
    },
  ],
},

// current
overrides: {
  fields: ({ defaultFields }) => {
    return [
      ...defaultFields,
      {
        type: 'text',
        name: 'customField',
      },
    ]
  },
},
```


## Type of change

- [x] New feature (non-breaking change which adds functionality)
- [x] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
2024-06-07 14:41:09 +00:00
Jacob Fletcher
7c8d562f03 fix(next): live preview device position when using zoom (#6665) 2024-06-07 10:17:49 -04:00
Alessio Gravili
11c3a65e63 feat(richtext-*): allow omitting the root editor property (#6660)
No need to add lexical/slate to the bundle if someone decides not to
make use of richText fields within payload at all
2024-06-06 17:57:03 +00:00
Paul
8dd5e4dc24 fix: max versions config not being respected on globals (#6654)
Closes https://github.com/payloadcms/payload/issues/6646

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
2024-06-06 17:21:32 +00:00
Alessio Gravili
9bd9e7a986 feat!: upgrade minimum node 20 version from 20.6.0 to 20.9.0 (#6659)
**BREAKING**:
- This bumps the minimum required node version from node 20.6.0 to node
20.9.0. This is because 20.6.0 breaks type generation due to a CJS node
bug, and 20.9.0 is the next v20 LTS version. The minimum node 18 version
stays the same (18.20.2)
2024-06-06 17:15:21 +00:00
Elliot DeNolf
66e00f8172 chore(release): v3.0.0-beta.42 [skip ci] 2024-06-06 12:21:02 -04:00
Jacob Fletcher
b95e25e937 docs: adds live preview csp troubleshooting tips (#6655) 2024-06-06 10:59:34 -04:00
Alessio Gravili
5722c530a1 chore: fix vscode jest extension debugger (#6653) 2024-06-06 14:53:06 +00:00
Jacob Fletcher
267c23616d fix(ui): global documents disabled after save (#6652) 2024-06-06 10:48:12 -04:00
Alessio Gravili
19f8cbcf76 docs: new and improve lexical docs, hoist up all headings (#6639) 2024-06-05 17:08:15 -04:00
Elliot DeNolf
aee3ee21d1 chore(release): v3.0.0-beta.41 [skip ci] 2024-06-05 16:25:52 -04:00
Dan Ribbens
ff82bb5661 fix(db-postgres)!: create predefined migration missing json schema output (#6641)
## Description

fixes #6630

# BREAKING CHANGES

This only applies to you if you using db-postgres and have created the
`v2-v3-relationships` migration released in
[v3.0.0-beta.39](https://github.com/payloadcms/payload/releases/tag/v3.0.0-beta.39)
from @payloadcms/db-postgres <= v3.0.0-beta.40.

### Steps to fix

- Delete the existing v2-v3-relationships migration file. 
- If changes were made to your config since the previous migration was
made, you will need to revert those by checking out a previous commit in
your version control.
- Recreate the migration using `payload migrate:create --file
@payloadcms/db-postgres/relationships-v2-v3` to make the migration with
the snapshot .json file.
2024-06-05 16:15:01 -04:00
Alessio Gravili
8a78134b72 chore(eslint): improve perfectionist configuration (#6644) 2024-06-05 19:50:49 +00:00
Elliot DeNolf
1dbb29e847 chore(templates): add generated templates [no-lint] (#6604)
Generate static template variations
2024-06-05 15:39:28 -04:00
Alessio Gravili
2077da8665 fix: update file-type dependency and fix dependency version mismatch (#6638) 2024-06-05 10:55:02 -04:00
Alessio Gravili
b2751f75d5 feat: support Next.js basePath config property (#6628) 2024-06-04 21:38:28 -04:00
Alessio Gravili
1afd221795 feat(ui): expose RowLabelProps (#6627) 2024-06-04 20:53:55 +00:00
Alessio Gravili
aeb4df894a fix(translations): explicitly declare date-fns v3 as a dependency (#6626) 2024-06-04 16:41:41 -04:00
Jacob Fletcher
f91c19e1fd chore(deps): bumps faceless pkgs to react 19-rc (#6622) 2024-06-04 14:05:07 -04:00
Patrik
bcd277eaaa fix: resizing animated images (#6623)
Fixes resizing of animated images

V2 PR [here](https://github.com/payloadcms/payload/pull/6621)
2024-06-04 13:57:46 -04:00
Alessio Gravili
da35afbd8f feat(richtext-lexical)!: upgrade lexical from 0.15.0 to 0.16.0 and port over relevant playground changes (#6620)
**BREAKING:**
- This upgrades the required version of lexical from 0.15.0 to 0.16.0.
If you are using lexical directly in your project, possibly due to
custom features, there might be breaking changes for you. Please consult
the lexical 0.16.0 changelog:
https://github.com/facebook/lexical/releases/tag/v0.16.0
2024-06-04 15:49:46 +00:00
Alessio Gravili
cafc13a7d5 fix: migration file cannot be imported (#6616) 2024-06-04 14:10:45 +00:00
Paul
2b6bff3c1d chore: update website template seed script (#6615)
## Type of change

- [x] Chore (non-breaking change which does not add functionality)
2024-06-04 13:58:01 +00:00
Paul
95f499d2bd chore: update website template to use new formBuilderPlugin fields override (#6614)
## Type of change

- [x] Chore (non-breaking change which does not add functionality)
2024-06-04 13:13:35 +00:00
Elliot DeNolf
6659fd1b97 chore(release): v3.0.0-beta.40 [skip ci] 2024-06-03 22:37:24 -04:00
Alessio Gravili
45b02d8827 fix: pin ajv to 8.14.0, as 8.15.0 is broken (#6606) 2024-06-04 00:25:13 +00:00
Alessio Gravili
59cde0dbb3 feat: match next.js env file loading behavior in bin scripts & importConfig, clean up installed packages & mismatching package versions (#6601) 2024-06-03 21:23:05 +00:00
Paul
1aece399f7 fix(plugin-form-builder): fix bug with optional chain operator in field overrides logic (#6602)
## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
2024-06-03 16:34:18 +00:00
Alessio Gravili
c3589ded08 fix(db-postgres): type issue in migratePostgresV2toV3 call if ts strict mode is enabled (#6585) 2024-05-31 22:37:08 -04:00
Alessio Gravili
c7fbd76dae fix(richtext-lexical): minor design improvements (#6575) 2024-05-30 20:50:32 +00:00
Alessio Gravili
6b9c796218 fix: critical getPredefinedMigration dependency error (#6578) 2024-05-30 20:47:44 +00:00
Alessio Gravili
5cb49c3307 fix(richtext-*): fix client features were not loaded properly, improve performance of LexicalProvider, slate cell component was non-functional, support richtext adapter Cell RSCs (#6573) 2024-05-30 16:26:06 -04:00
Alessio Gravili
f41bb05c70 feat(richtext-lexical)!: configurable fixed toolbar (#6560)
**BREAKING**: useEditorFocusProvider has been removed and merged with
useEditorConfigContext. You can now find information about the focused
editor, parent editors and child editors within useEditorConfigContext
2024-05-30 20:08:22 +00:00
Elliot DeNolf
c68189788c chore(release): v3.0.0-beta.39 [skip ci] 2024-05-30 14:26:03 -04:00
Jacob Fletcher
e603c83f55 fix(next): ssr live preview was not dispatching document save events (#6572) 2024-05-30 14:20:22 -04:00
Dan Ribbens
edfa85bcd5 feat(db-postgres)!: relationship column (#6339)
BREAKING CHANGE:

Moves `upload` field and `relationship` fields with `hasMany: false` &
`relationTo: string` from the many-to-many `_rels` join table to simple
columns. This only affects Postgres database users.

## TL;DR

We have dramatically simplified the storage of simple relationships in
relational databases to boost performance and align with more expected
relational paradigms. If you are using the beta Postgres adapter, and
you need to keep simple relationship data, you'll need to run a
migration script that we provide you.

### Background

For example, prior to this update, a collection of "posts" with a simple
`hasMany: false` and `relationTo: 'categories'` field would have a
`posts_rels` table where the category relations would be stored.

This was somewhat unnecessary as simple relations like this can be
expressed with a `category_id` column which is configured as a foreign
key. This also introduced added complexity for dealing directly with the
database if all you have are simple relations.

### Who needs to migrate

You need to migrate if you are using the beta Postgres database adapter
and any of the following applies to you.

- If you have versions enabled on any collection / global
- If you use the `upload` field 
- If you have relationship fields that are `hasMany: false` (default)
and `relationTo` to a single category ([has
one](https://payloadcms.com/docs/fields/relationship#has-one)) relations

### We have a migration for you

Even though the Postgres adapter is in beta, we've prepared a predefined
migration that will work out of the box for you to migrate from an
earlier version of the adapter to the most recent version easily.

It makes the schema changes in step with actually moving the data from
the old locations to the new before adding any null constraints and
dropping the old columns and tables.

### How to migrate

The steps to preserve your data while making this update are as follows.
These steps are the same whether you are moving from Payload v2 to v3 or
a previous version of v3 beta to the most recent v3 beta.

**Important: during these steps, don't start the dev server unless you
have `push: false` set on your Postgres adapter.**

#### Step 1 - backup

Always back up your database before performing big changes, especially
in production cases.

#### Step 2 - create a pre-update migration 
Before updating to new Payload and Postgres adapter versions, run
`payload migrate:create` without any other config changes to have a
prior snapshot of the schema from the previous adapter version

#### Step 3 - if you're migrating a dev DB, delete the dev `push` row
from your `payload_migrations` table

If you're migrating a dev database where you have the default setting to
push database changes directly to your DB, and you need to preserve data
in your development database, then you need to delete a `dev` migration
record from your database.

Connect directly to your database in any tool you'd like and delete the
dev push record from the `payload_migrations` table using the following
SQL statement:

```sql
DELETE FROM payload_migrations where batch = -1`
```

#### Step 4 - update Payload and Postgres versions to most recent

Update packages, making sure you have matching versions across all
`@payloadcms/*` and `payload` packages (including
`@payloadcms/db-postgres`)

#### Step 5 - create the predefined migration

Run the following command to create the predefined migration we've
provided:

```
payload migrate:create --file @payloadcms/db-postgres/relationships-v2-v3
```

#### Step 6 - migrate!

Run migrations with the following command: 

```
payload migrate
```

Assuming the migration worked, you can proceed to commit this change and
distribute it to be run on all other environments.

Note that if two servers connect to the same database, only one should
be running migrations to avoid transaction conflicts.

Related discussion:
https://github.com/payloadcms/payload/discussions/4163

---------

Co-authored-by: James <james@trbl.design>
Co-authored-by: PatrikKozak <patrik@payloadcms.com>
2024-05-30 14:09:11 -04:00
Elliot DeNolf
b86d4c647f chore(release): v3.0.0-beta.38 [skip ci] 2024-05-30 11:24:12 -04:00
Elliot DeNolf
4884f0d297 fix(cpa): safer command exists check (#6569)
- Use execa to check if command exists
- Remove third-party dep
2024-05-30 11:11:40 -04:00
Patrik
f1db24e303 fix(ui): adjusts sizing of remove/add buttons to be same size (#6529)
## Description

V2 PR [here](https://github.com/payloadcms/payload/pull/6527)

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-05-30 09:42:25 -04:00
Patrik
7f15147286 fix: ui field validation error with admin.disableListColumn property (#6531)
## Description

V2 PR [here](https://github.com/payloadcms/payload/pull/6530)

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
- [x] This change requires a documentation update

## Checklist:

- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
2024-05-30 09:41:58 -04:00
Patrik
e0a6db7f97 fix(translations): adds new userEmailAlreadyRegistered translations (#6550)
## Description

V2 PR [here](https://github.com/payloadcms/payload/pull/6549)

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-05-30 09:37:02 -04:00
Elliot DeNolf
0d7d3e5c92 fix(cpa): more package manager detection improvements (#6566)
- Adjust package manager detection logic
- Remove pnpm-lock.yaml from blank template
2024-05-30 09:35:53 -04:00
Elliot DeNolf
8b49402e4c chore(templates): consolidate initial vercel-postgres migration (#6568) 2024-05-30 09:33:35 -04:00
Jarrod Flesch
347464250e fix: duplicate options appearing in relationship where builder (#6557)
- enables reactStrictMode by default
- enables reactCompiler by default
- fixes cases where ID's set to 0 broke UI
2024-05-30 00:35:59 -04:00
Paul
aa02801c3d fix(plugin-search): Render error on custom UI component (#6562)
## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
2024-05-30 01:56:06 +00:00
Elliot DeNolf
a3ee07f693 chore: import vercel-postgres one-click template (#6564)
Import vercel one-click template
2024-05-29 18:06:52 -04:00
Jessica Chowdhury
511908a964 docs: adds sentry to plugin docs (#6475) 2024-05-29 15:19:16 -04:00
Jarrod Flesch
425576be25 fix: ensure relationship field pills respect isSortable property (#6561) 2024-05-29 15:12:42 -04:00
Jacob Fletcher
92f458dad2 feat(next,ui): improves loading states (#6434) 2024-05-29 14:01:13 -04:00
Jarrod Flesch
043a91d719 fix: ability to query relationships not equal to ID (#6555) 2024-05-29 13:47:44 -04:00
Jacob Fletcher
54e2d7fb38 docs: renames vercel visual editing to vercel content link (#6559) 2024-05-29 13:39:40 -04:00
Jacob Fletcher
321e97f9fe feat: extracts buildFormState logic from endpoint for reuse (#6501) 2024-05-29 12:51:16 -04:00
Elliot DeNolf
4e0dfd410d chore(release): v3.0.0-beta.37 [skip ci] 2024-05-29 10:54:45 -04:00
Elliot DeNolf
8506385ef9 fix(cpa): improve package manager detection (#6546)
Improves package manager detection.

Closes #6231
2024-05-29 09:30:15 -04:00
Jarrod Flesch
e74952902e fix: multi value draggable/sortable pills (#6500) 2024-05-29 08:22:37 -04:00
Alessio Gravili
4a51f4d2c1 fix(richtext-lexical): various html converter fixes (#6544) 2024-05-29 00:29:25 -04:00
Alessio Gravili
2c283bcc08 fix(richtext-lexical): user-defined html converters not taking precedence, and shared default html converters doubling in size after every field initialization 2024-05-29 00:14:58 -04:00
Alessio Gravili
a2e9bcd333 fix(richtext-lexical): list converters and nodes being added duplicatively 2024-05-28 23:53:35 -04:00
Alessio Gravili
33d53121a2 feat(richtext-lexical): link markdown transformers (#6543)
Closes https://github.com/payloadcms/payload/issues/6507

---------

Co-authored-by: ShawnVogt <41651465+shawnvogt@users.noreply.github.com>
2024-05-29 03:28:26 +00:00
Leo Hilsheimer
e0b201c810 fix(richtext-lexical): link html converter: serialize newTab to target="_blank" (#6350)
Co-authored-by: Leo <leo.hilsheimer@gmail.com>
2024-05-28 23:20:44 -04:00
Alessio Gravili
a8000f644f feat(richtext-lexical): i18n (#6542)
Continuation of https://github.com/payloadcms/payload/pull/6524
2024-05-29 02:40:48 +00:00
Paul
7d0e909a30 feat(plugin-form-builder)!: update form builder plugin field overrides to use a function instead (#6497)
## Description

Changes the `fields` override for form builder plugin to use a function
instead so that we can actually override existing fields which currently
will not work.

```ts
//before
fields: [
  {
    name: 'custom',
    type: 'text',
  }
]

// current
fields: ({ defaultFields }) => {
  return [
    ...defaultFields,
    {
      name: 'custom',
      type: 'text',
    },
  ]
}
```

## Type of change

- [x] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
2024-05-28 17:45:51 -03:00
Elliot DeNolf
b2662eeb1f ci: update app-build-with-packed job (#6541)
Add `--ignore-workspace` and `--no-frozen-lockfile` where necessary
2024-05-28 14:35:18 -04:00
Elliot DeNolf
0b274dd67e chore: adjust email-nodemailer workspace dep pattern (#6539)
Adjust dep pattern for email-nodemailer reference from plugin-cloud
2024-05-28 14:19:21 -04:00
Elliot DeNolf
2ddd50edc4 fix(deps): proper location for scheduler peer dep (#6537)
Properly put `scheduler` dep under `ui` instead of `payload`.
2024-05-28 14:15:56 -04:00
Elliot DeNolf
0287acb8f0 chore(templates): update dockerfile and docker-compose for blank template (#6536)
Update Dockerfile and docker-compose.yml for blank template.
2024-05-28 12:35:05 -04:00
Elliot DeNolf
10c94b3665 feat(cpa): update existing payload installation (#6193)
Updates create-payload-app to update an existing payload installation

- Detects existing Payload installation. Fixes #6517 
- If not latest, will install latest and grab the `(payload)` directory
structure (ripped from `templates/blank-3.0`
2024-05-28 11:38:33 -04:00
Alessio Gravili
ea48ca377e chore: move lexical package from workspace-root to test package (#6533) 2024-05-28 15:01:30 +00:00
zvizvi
6f5d86ed84 fix: Add missing He lang export in payload/i18n (#6484)
## Description
Fixed missing Hebrew language export in payload/i18n module.
The import statement import { he } from 'payload/i18n/he' was not
functioning due to he not being exported correctly.


<!-- Please include a summary of the pull request and any related issues
it fixes. Please also include relevant motivation and context. -->

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

<!-- Please delete options that are not relevant. -->

- [x] Chore (non-breaking change which does not add functionality)
2024-05-28 10:52:20 -03:00
Alessio Gravili
c383f391e3 feat(richtext-lexical): i18n support (#6524) 2024-05-28 09:10:39 -04:00
Paul
8a91a7adbb feat(richtext-lexical): update validation of custom URLs to include relative and anchor links (#6525)
Updates the regex to allow relative and anchor links as well. Manually
tested all common variations of absolute, relative and anchor links with
a combination

## Type of change

- [x] New feature (non-breaking change which adds functionality)
2024-05-28 00:55:00 -03:00
Alessio Gravili
96181d91a6 chore(ui): add ability to compile using react compiler (#6483)
This does not enable the react compiler by default
2024-05-27 22:54:36 -04:00
Alessio Gravili
eff5129a5f fix(next): unable to pass custom view client components (#6513) 2024-05-27 22:48:41 -04:00
Dan Ribbens
38e5adc462 chore: fix seed file path windows (#6512) 2024-05-26 15:39:16 +00:00
Dan Ribbens
ff4ea1eecc chore: getPayloadHMR conditionally call db.connect (#6510) 2024-05-25 22:17:27 +00:00
Dan Ribbens
dbfd1beed5 chore: gitignore static files uploads tests (#6509) 2024-05-25 22:06:43 +00:00
Dan Ribbens
4b6774463e chore: loader tests error on windows (#6508) 2024-05-25 21:57:32 +00:00
Dan Ribbens
cb14b97a6e chore: swcrc syntax fix (#6505) 2024-05-25 15:45:05 +00:00
Jarrod Flesch
18bc4b708c fix: separate collection docs with same ids were excluded in selectable (#6499) 2024-05-24 15:20:07 -04:00
Paul
6d951e6987 chore: add lexical as a direct dependency to the website template (#6496) 2024-05-24 16:33:36 +00:00
Elliot DeNolf
365660764d chore(templates): enable next lint on blank (#6494)
Enables next linting on blank template

Closes #6481
2024-05-24 11:36:50 -04:00
Elliot DeNolf
8b91af8a5b chore(cpa): adjust unit test template (#6490)
Adjust template used in unit tests.
2024-05-24 10:12:19 -04:00
Paul
b4092f59ae chore: fix seed data validation in website template (#6491)
Fixes an issue with data validation in lexical for the seed script
2024-05-24 14:10:29 +00:00
Alessio Gravili
7a768144ea fix(richtext-lexical): localized sub-fields were omitted from the API output (#6489)
Closes #6455. Proper localization support will be worked on later, this
just resolves the issue where having it enabled not only doesn't
localize those fields, it also omits them from the API response. Now,
they are not omitted, and localization is simply skipped.
2024-05-24 10:01:04 -04:00
Elliot DeNolf
3839eb5ab0 chore(templates): remove blank v2 template (#6488)
New v3 is `blank-3.0`. Will rename that one in future PR.
2024-05-24 09:36:57 -04:00
Paul
fd02bee0fe chore: website template updates (#6480)
Just style updates
2024-05-23 20:38:25 +00:00
Patrik
42222cd2f6 fix(ui): where builder issues (#6478)
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2024-05-23 16:01:13 -04:00
Elliot DeNolf
e3222f2ac3 chore(release): v3.0.0-beta.36 [skip ci] 2024-05-23 13:35:19 -04:00
Alessio Gravili
35f961fecb feat!: next.js 15, react 19, react compiler support (#6429)
**BREAKING:**
- bumps minimum required next.js version from `14.3.0-canary.68` to
`15.0.0-rc.0`
- bumps minimum required react and react-dom versions to `19.0.0
`(`19.0.0-rc-f994737d14-20240522` should be used)
- `@types/react` and `@types/react-dom` have to be bumped to
`npm:types-react@19.0.0-beta.2` using overrides and pnpm overrides, if
you want correct types. You can find an example of this here:
https://github.com/payloadcms/payload/pull/6429/files#diff-10cb9e57a77733f174ee2888587281e94c31f79e434aa3f932a8ec72fa7a5121L32

## Issues

- Bunch of todos for our react-select package which is having type
issues. Works fine, just type issues. Their type defs are importing JSX
in a weird way, we likely just have to wait until they fix them in a
future update.
2024-05-23 13:30:12 -04:00
Paul
85bfca79ef feat: add website template (#6470)
Adds the new website template for 3.0
2024-05-23 16:48:41 +00:00
Alessio Gravili
661a4a099d feat(ui): split up Select component into Select and SelectInput (#6471) 2024-05-23 11:36:57 -04:00
Jarrod Flesch
72c0534008 fix: adds host to initPage req creation (#6476) 2024-05-23 11:04:35 -04:00
Alessio Gravili
78579ed2bd feat(richtext-lexical): various UX and performance improvements (#6467) 2024-05-22 14:42:17 -04:00
Alessio Gravili
7bcb4ba1cc chore(email-*): remove excess backtick in readme install commands 2024-05-22 13:59:33 -04:00
Alessio Gravili
6b45cf3197 feat(richtext-lexical): improve block dragging UX 2024-05-22 13:55:44 -04:00
Elliot DeNolf
73d0b209d7 fix: isHotkey webpack error (#6466)
Fixes webpack issue with isHotkey: `TypeError:
is_hotkey__WEBPACK_IMPORTED_MODULE_9__ is not a function`

Changing this from a default import to a named export, and it appears to
resolve the issue.

Fixes #6421
2024-05-22 17:41:15 +00:00
Alessio Gravili
c93752bdbb fix(richtext-lexical): order of add/drag handles was inconsistent between gutter and no-gutter mode 2024-05-22 10:49:11 -04:00
Alessio Gravili
7a4dd5890e fix(ui): field errors aren't red in light mode 2024-05-22 10:29:41 -04:00
Alessio Gravili
60ee55fcaa chore(richtext-lexical): do not show red border & background for erroring field without gutter 2024-05-22 10:22:06 -04:00
Jacob Fletcher
1fe9790d0d feat(next): server-side theme detection (#6452) 2024-05-22 10:19:38 -04:00
zvizvi
3c0853a675 feat(translations): add Hebrew translation (#6428)
Hebrew translation added.
2024-05-22 14:15:10 +00:00
Jacob Fletcher
2b941b7e2c fix(next,ui): fixes global doc permissions and optimizes publish access data loading (#6451) 2024-05-22 10:03:12 -04:00
Elliot DeNolf
db772a058c chore: add label-author.yml 2024-05-22 09:09:52 -04:00
Alessio Gravili
0bfbf9c750 fix(richtext-lexical): link drawer sending too many form state requests for actions unrelated to links 2024-05-21 22:34:41 -04:00
Alessio Gravili
5c7647f45b ci: split up test suites (#6415) 2024-05-21 17:11:55 -04:00
Alessio Gravili
6c952875e8 feat(richtext-lexical): various gutter, error states & add/drag handle improvements (#6448)
## Gutter

Adds gutter by default:

![CleanShot 2024-05-21 at 16 24
13](https://github.com/payloadcms/payload/assets/70709113/09c59b6f-bd4a-4e81-bfdd-731d1cbbe075)


![CleanShot 2024-05-21 at 16 20
23](https://github.com/payloadcms/payload/assets/70709113/94df3e8c-663e-4b08-90cb-a24b2a788ff6)

can be disabled with admin.hideGutter

## Error states
![CleanShot 2024-05-21 at 16 21
18](https://github.com/payloadcms/payload/assets/70709113/06754d8f-c674-4aaa-a4e5-47e284970776)

Finally, proper error states display. Cleaner, and previously fields
were shown as erroring even though they weren't. No more!

## Drag & Block handles
Improved performance, and cleaned up code. Drag handle positions are now
only calculated for one editor rather than all editors on the page. Add
block handle calculation now uses a better algorithm to minimize the
amount of nodes which are iterated.

Additionally, have you noticed how sometimes the add button jumps to the
next node while the drag button is still at the previous node?


https://github.com/payloadcms/payload/assets/70709113/8dff3081-1de0-4902-8229-62f178f23549

No more! Now they behave the same. Feels a lot cleaner now.
2024-05-21 20:55:06 +00:00
Jacob Fletcher
af7e12aa2f chore(ui)!: uses consistent button naming conventions (#6444)
## Description

Renames the `Save` to `SaveButton`, etc. to match the already
established convention of the `PreviewButton`, etc. This matches the
imports with their respective component and type names, and also gives
these components more context to the developer whenever they're
rendered, i.e. its clearly just a button and not an entire block or
complex component.

**BREAKING**:

Import paths for these components have changed, if you were previously
importing these components into your own projects to customize, change
the import paths accordingly:

Old:
```ts
import { PublishButton } from '@payloadcms/ui/elements/Publish'
import { SaveButton } from '@payloadcms/ui/elements/Save'
import { SaveDraftButton } from '@payloadcms/ui/elements/SaveDraft'
```

New:
```ts
import { PublishButton } from '@payloadcms/ui/elements/PublishButton'
import { SaveButton } from '@payloadcms/ui/elements/SaveButton'
import { SaveDraftButton } from '@payloadcms/ui/elements/SaveDraftButton'
```

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
2024-05-21 14:52:53 -04:00
Patrik
bcc506b423 fix(ui): disableListColumn fields not hidden in table columns (#6445)
## Description

Setting `disableListColumn` to `true` on a field would hide the field
from the column selector but not from the table columns.

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-05-21 13:33:28 -04:00
Elliot DeNolf
3d7c8277d7 chore(release): v3.0.0-beta.35 [skip ci] 2024-05-21 10:51:19 -04:00
Paul
a8a2dc2347 chore!: export DefaultListView as ListView (#6432)
Change the exports of DefaultListView and DefaultEditView to be renamed
without "Default" as ListView

```ts
// before
import { DefaultEditView } from '@payloadcms/next/views'
import { DefaultListView } from '@payloadcms/next/views'

// after 
import { EditView } from '@payloadcms/next/views'
import { ListView } from '@payloadcms/next/views'
```
2024-05-21 10:22:44 -04:00
Alessio Gravili
6c74b0326b chore(richtext-lexical): improve node validation messages (#6443) 2024-05-21 10:19:24 -04:00
Paul
f51af92491 chore(translations): ai translation script should use formal language (#6433)
Added additional prompt to make sure the translation we receive is using
formal language where it makes sense.

In the context of latin languages for example:
- Spanish: "tu" should be using "vos"
- French: "tu" should be using "votre"

These differences can affect verb conjugations and in these languages it
comes across as less professional if informal language is used.
2024-05-21 10:15:01 -04:00
Alessio Gravili
77528a1e7d chore(richtext-slate): fix richtext container elements direction 2024-05-21 09:40:17 -04:00
Alessio Gravili
ba8b8e8330 chore(richtext-lexical): improve node validation messages 2024-05-21 09:36:58 -04:00
Jessica Chowdhury
23f9a32a99 fix: user verification email broken (#6442)
## Description

Closes
[#225](https://github.com/payloadcms/payload-3.0-demo/issues/225).

The user verification emails are not being sent and this error is shown:
```ts
APIError: Error sending email: 422 validation_error - Invalid `from` field. The email address needs to follow the `email@example.com` or `Name <email@example.com>` format.
```

The issue is resolved by updating the `from` property on the outgoing
verification email:
```ts
from: `"${email.defaultFromName}" <${email.defaultFromName}>`,
// to
from: `"${email.defaultFromName}" <${email. defaultFromAddress}>`,
```

**NOTE:** This was not broken in 2.0, see correct outgoing email
[here](https://github.com/payloadcms/payload/blob/main/packages/payload/src/auth/sendVerificationEmail.ts#L69).

- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [X] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [X] Existing test suite passes locally with my changes
2024-05-21 13:25:59 +00:00
Jessica Chowdhury
0190eb8b28 fix(ui): blocks browser save dialog from opening when hotkey used with no changes (#6366) 2024-05-21 09:00:34 -04:00
Anders Semb Hermansen
f482fdcfd5 fix: separate sort and search fields when looking up relationship. (#6440)
## Description

Default sort is used as searching field which is causing unexpected
behaviour described in https://github.com/payloadcms/payload/issues/4815
and https://github.com/payloadcms/payload/issues/5222 This bugfix
separates which field is used for sorting and which is used for
searching.

Fixes: https://github.com/payloadcms/payload/issues/4815
https://github.com/payloadcms/payload/issues/5222

@denolfe This fix is a port of the fix in
[#5964](https://github.com/payloadcms/payload/pull/5964) to beta branch.

- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [X] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
2024-05-20 16:57:49 -04:00
Alessio Gravili
ed4766188d fix(ui): tooltip positioning issues (#6439) 2024-05-20 20:37:53 +00:00
Ritsu
e682cb1b04 fix(ui): update relationship cell formatted value when when search changes (#6208)
## Description

Fixes https://github.com/payloadcms/payload-3.0-demo/issues/181
Although issue is about page changing, it happens as well when you
change sort / limit / where filter (and probably locale)
<!-- Please include a summary of the pull request and any related issues
it fixes. Please also include relevant motivation and context. -->

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

<!-- Please delete options that are not relevant. -->


- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes

---------

Co-authored-by: Jessica Chowdhury <jessica@trbl.design>
2024-05-20 16:03:04 -04:00
Elliot DeNolf
36fda30c61 feat: store focal point on uploads (#6436)
Store focal point data on uploads as `focalX` and `focalY`

Addresses https://github.com/payloadcms/payload/discussions/4082

Mirrors #6364 for beta branch.
2024-05-20 15:57:52 -04:00
Alessio Gravili
fa7cc376d1 fix(richtext-lexical): field required validation not working if content was removed manually (#6435) 2024-05-20 17:17:54 +00:00
Paul
3fc2ff1ef9 chore: export DefaultListView for reuse (#6422)
Exports `DefaultListView` so other plugins or custom implementations can
re-use it
2024-05-20 11:53:36 -03:00
Jarrod Flesch
1d81eef805 fix: attributes graphql packages, adds esm import path (#6431) 2024-05-20 10:48:41 -04:00
Paul
8fcfac61b5 fix(plugin-seo): white screen of death on choosing an existing media for meta image (#6424)
Closes https://github.com/payloadcms/payload/issues/6423

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
2024-05-19 04:51:19 +00:00
Elliot DeNolf
0d544dacdb chore(release): v3.0.0-beta.34 [skip ci] 2024-05-17 16:12:46 -04:00
Alessio Gravili
147b50e719 fix: page metadata generation not working in turbopack (#6417)
In turbo, payloadFaviconDark is a string, not an object with src
2024-05-17 15:44:12 -04:00
Elliot DeNolf
eeb689dd55 chore(release): v3.0.0-beta.33 [skip ci] 2024-05-17 14:59:23 -04:00
James Mikrut
c2571cfdb6 fix(db-postgres): uuid custom db name (#6408)
## Description

Fixes an issue with creating versions when using custom DB names,
`uuid`, and drafts.

---------

Co-authored-by: PatrikKozak <patrik@payloadcms.com>
2024-05-17 14:11:14 -04:00
Dan Ribbens
12c812d379 fix(db-postgres): query with like on id columns (#6414)
When typing into the search input on the list view of a collection, the
`like` operator is used for id which causes an error for postgres. To
fix this we are sanitizing the `like` for number or uuid fields to
instead be an `equals` operator. An alternate solution would have been
to cast the ids to text `id::text` but this would have performence
implications on larger data sets.

---------

Co-authored-by: James <james@trbl.design>
2024-05-17 14:09:44 -04:00
Alessio Gravili
bf106db6e2 feat(richtext-lexical): new aboveContainer and belowContainer plugin positioning options, fix incorrect placeholder positioning (#6410) 2024-05-17 17:51:51 +00:00
Jacob Fletcher
18009349c0 fix(ui): properly sets hasSavePermission on nested documents (#6394) 2024-05-17 13:41:38 -04:00
Alessio Gravili
89b6055d61 fix: component is undefined error within isReactServerComponentOrFunction (#6411) 2024-05-17 17:24:51 +00:00
Francis Turmel
d9a8869132 chore(translations): French translation improvements (beta branch) (#6406)
## Description

* The apostrophe character `’` should be used instead of the single
quote `'`
* Gender corrections: "L’adresse e-mail fourni**e**", "Vérification
échoué**e**"
* Lowercase: "Supprimer le **té**léversement"
* Dark and light theme: I think it makes more sense to use "Sombre" and
"Clair" here to identify the theme. Day/Night modes imply a hue/warmth
correction and are different features altogether. Reference:
https://fr.wikipedia.org/wiki/Mode_sombre#Mode_sombre_et_mode_nuit_ou_chaud
* Fix accent: "Mis à jour avec succ**è**s"
* "Bienvenue" I think would be the correct standalone greeting form.
Reference:
https://www.projet-voltaire.fr/question-orthographe/orthographe-bienvenu-bienvenue-chez-moi/
* "Recadrer" is the correct word for "crop". "Récolte" means "crop" in
the sense of "harvest", so this was probably a bad literal Google
Translate that slipped through.
* Correct all "Es-tu sûr ?" to the proper formal "Êtes-vous sûr ?" for
consistency
* Use _article défini_ since we will enumerate the values: "Ce champ
contient **les** sélections invalides suivantes :"
* Space before question marks

---

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.
2024-05-17 13:52:17 -03:00
Alessio Gravili
9d5c0d350c feat!: upgrade minimum next version to 14.3.0-canary.68 & upgrade react packages, react-toastify (#6387)
**BREAKING:**
- The minimum required next version is now 14.3.0-canary.68. This is
because we are migrating away from the deprecated
experimental.serverComponentsExternalPackages next config key to
experimental.serverExternalPackages, which is not available in older
next canaries
- The minimum `react` and `react-dom` versions have been bumped to
^18.2.0 or ^19.0.0. This matches the minimum react version recommended
by next
2024-05-17 12:48:37 -04:00
Alessio Gravili
4dedd6e267 fix: turbopack RSC detection (#6405) 2024-05-17 11:40:10 -04:00
Alessio Gravili
276213193b feat: allow client components and extra rsc props for custom edit and list views (#6395) 2024-05-17 11:25:33 -04:00
Jacob Fletcher
553bb4b530 fix(next)!: removes initPage export from barrel file (#6403) 2024-05-17 09:37:54 -04:00
James Mikrut
5083525189 fix: loader support for server-only (#6383)
## Description

Allows `server-only` to work within the Payload loader.

Fixes https://github.com/payloadcms/payload-3.0-demo/issues/218
2024-05-17 09:07:18 -04:00
Elliot DeNolf
e4185259b4 ci: properly prefix proposed release in release script with 'v' 2024-05-16 22:34:21 -04:00
Alessio Gravili
5323d76a5b fix: react-select menu is hidden behind lexical fixed toolbar (#6396) 2024-05-16 21:09:41 +00:00
Alessio Gravili
608387084c fix(richtext-lexical): upload, relationship and block node insertion fails sometimes 2024-05-16 16:02:53 -04:00
Jacob Fletcher
9556d1bd42 feat!: replaces admin.meta.ogImage with admin.meta.openGraph.images (#6227) 2024-05-16 12:40:15 -04:00
Paul
a6bf05815c chore!: remove unused staticOptions config on uploads (#6378)
Removes the unused `staticOptions` on upload config, it was previously
typed to express configuration and is unused anywhere in the codebase
2024-05-16 15:15:35 +00:00
Patrik
a4deaf07d6 fix(next): incorrect stepnav breadcrumbs after selecting existing upload (#6372)
## Description

Fixes [this](https://github.com/payloadcms/payload-3.0-demo/issues/202)
v3 demo issue

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-05-16 10:23:32 -04:00
Jacob Fletcher
4adf01ab04 fix(next): does not wrap custom views with template by default (#6379) 2024-05-16 09:10:27 -04:00
Alessio Gravili
1abcdf96fa fix(translations): type StripCountVariants not working in TS strict mode (#6374)
This also removes the unnecessary StripCountVariants utility use for
when NestedKeysStripped<T[K]> is an object
2024-05-15 21:30:57 -04:00
Paul
3456b5f6a7 chore: eslint updates to the tailwind example (#6377)
minor updates to eslint rules in tailwind example
2024-05-16 00:56:19 +00:00
Paul
d053778bf2 chore: update form builder example (#6376)
Updates the form builder example
2024-05-15 21:22:54 -03:00
Patrik
fbad39a120 fix: safely access cookie header for uploads (#6373)
## Description

Issue with editing and changing the crop or focal point of an image

`fix`: adds optional chaining to safely access cookie header when
fetching image

v2 PR [here](https://github.com/payloadcms/payload/pull/6367)

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-05-15 21:04:21 +00:00
Patrik
e8d1d369cf fix(db-postgres): filter with ID not_in AND queries (#6359)
## Description

v2 PR [here](https://github.com/payloadcms/payload/pull/6358)

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
2024-05-15 15:10:51 -04:00
Alessio Gravili
fbea52416c feat(richtext-lexical)!: upgrade lexical from 0.14.5 to 0.15.0 (#6371)
**BREAKING:** This upgrades all lexical packages from 0.14.5 to 0.15.0.
If there are any breaking changes within lexical, this could break your
project if you use lexical APIs directly (e.g. in custom features). We
have not noticed any breaking changes within core. Please consult their
changelog: https://github.com/facebook/lexical/releases/tag/v0.15.0
2024-05-15 13:44:51 -04:00
Alessio Gravili
cb9a20fefa fix: loader throwing errors for client files imported using TS paths (#6369)
## Description

Fixes https://github.com/payloadcms/payload-3.0-demo/issues/215

imports like import LogoSVG from '@/components/logo.svg' did not make it
to the TS module resolution, due to the early isClient check.

And the isClient check only uses node module resolution (using
nextResolves) which throws an error here.

This removes module resolution completely, as we "ignore" client files
anyways. Should also help improve performance, and we do not have to
fall back to ts module resolution for client files that way, which would
be unnecessary
2024-05-15 12:53:07 -04:00
Alessio Gravili
8db9664700 fix(richtext-lexical): autoLink node styles not inherited from original text node on creation
Backports https://github.com/facebook/lexical/pull/6069
2024-05-15 12:50:30 -04:00
Alessio Gravili
08add653c7 chore(richtext-lexical): replace deprecated event.keyCode with event.code 2024-05-15 12:42:14 -04:00
Alessio Gravili
eed9676536 chore(richtext-lexical): add @lexical/eslint-plugin eslint plugin and fix all eslint errors & warnings 2024-05-15 12:41:10 -04:00
Alessio Gravili
22480a7648 feat(richtext-lexical)!: upgrade lexical from 0.14.5 to 0.15.0 and ensure peerDependencies force correct lexical version 2024-05-15 12:22:26 -04:00
Jarrod Flesch
aa2073f9e9 chore: adjusts how file uploads are handled, consolidates reading to busboy (#6346) 2024-05-15 11:42:04 -04:00
Alessio Gravili
e0618f81a5 chore: loosen type for ClientTranslationsObject to improve TS performance (#6368) 2024-05-15 15:18:43 +00:00
Alessio Gravili
ea90018979 chore: auto-translation script for v3, and translate all missing translation keys (#6361) 2024-05-15 13:54:25 +00:00
Jessica Chowdhury
5a4074e90a fix: multiselect relationship bug and improve accessibility (#6286)
## Description

Closes [#117](https://github.com/payloadcms/payload-3.0-demo/issues/177)
- hitting the space key while the `ReactSelect` is in focus crashes the
page.

This PR makes the following changes:
- Multivalue select component updated to only use `id`, drag feature
does not work when using `uuid()`
- Ensures relationship field (multi and single value) can be accessed
via the keyboard

- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [X] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [X] Existing test suite passes locally with my changes
2024-05-15 09:33:47 -04:00
Elliot DeNolf
0e9bbecbee chore(release): v3.0.0-beta.32 [skip ci] 2024-05-14 17:33:28 -04:00
Alessio Gravili
c8a1ccaf4b feat(richtext-lexical): add rootFeatures prop to lexicalEditor (#6360) 2024-05-14 17:29:21 -04:00
Alessio Gravili
79f4907cb3 feat!: add missing server-only props to custom RSCs, improve req.user type, clean-up ui imports (#6355) 2024-05-14 17:27:15 -04:00
Jacob Fletcher
6a0fffe002 feat!: consolidates admin.logoutRoute and admin.inactivityRoute into admin.routes (#6354) 2024-05-14 21:18:19 +00:00
Jarrod Flesch
6e116a76fd fix(graphql): threads through correct draft value for upload relations (#6235) 2024-05-14 14:05:58 -04:00
Elliot DeNolf
f52607f3b5 chore(release): v3.0.0-beta.31 [skip ci] 2024-05-14 12:37:38 -04:00
Alessio Gravili
f716122eab feat!: typed i18n (#6343) 2024-05-14 11:10:31 -04:00
Patrik
353c2b0be2 fix(ui): step-nav breadcrumbs ellipsis (#6344) 2024-05-14 11:08:34 -04:00
Jessica Chowdhury
58bbbbd395 fix: collection labels with multiple locales showing incorrectly (#5998) 2024-05-14 15:05:36 +00:00
Jessica Chowdhury
57b072edfc chore: fix indentation in API tab json for empty nested objects/arrays (#6150) 2024-05-14 14:44:10 +00:00
Jacob Fletcher
f6039246c6 feat!: replaces admin.favicon with admin.icons (#6347) 2024-05-14 09:56:07 -04:00
Jessica Chowdhury
fcee13b017 fix: depth field in api view throws error when no value present (#6106) 2024-05-14 13:46:16 +00:00
Jacob Fletcher
ef5197a514 Merge branch 'beta' into feat/next-icons 2024-05-14 09:30:01 -04:00
Jacob Fletcher
7438812db3 feat!: replaces admin.favicon with admin.icons 2024-05-14 08:56:21 -04:00
Elliot DeNolf
48af78278d ci: run protected branch actions to completion 2024-05-13 19:30:02 -04:00
Jarrod Flesch
3abc2e8328 fix: implements graphql schema generation (#6254)
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
2024-05-13 16:46:43 -04:00
Jacob Fletcher
a48043c2aa fix(next): replaces default svg favicons with png 2024-05-13 16:33:50 -04:00
Jacob Fletcher
7e4f50a01c feat(ui): exports png favicons 2024-05-13 16:27:53 -04:00
Elliot DeNolf
40d3078bf2 feat(cpa): initialize git repo on project creation (#6342) 2024-05-13 14:59:39 -04:00
Elliot DeNolf
662334abfb ci: bump playwright actions/cache usage to v4 2024-05-13 14:01:32 -04:00
Paul
aa5ad47177 fix: named tab being required in generated types without any required fields (#6324) 2024-05-13 13:55:48 -04:00
James Mikrut
890c21dda4 fix(payload): loader alias issues (#6341) 2024-05-13 13:49:16 -04:00
Patrik
c7635b2783 fix(ui): properly adds readOnly styles to disabled radio fields (#6240) 2024-05-13 12:18:36 -04:00
James Mikrut
47cd5f4d01 fix(db-postgres): too many clients already, cannot read properties 'transaction' of undefined (#6338) 2024-05-13 12:13:30 -04:00
Alessio Gravili
0d98b4b96f fix!: some custom components were not handled properly if they are RSCs (#6315)
**Breaking:** The following, exported components now need the `payload` object as a prop rather than the `config` object:
- `RenderCustomComponent` (optional)
- `Logo`
- `DefaultTemplate`
- `DefaultNav`
2024-05-13 12:05:13 -04:00
Elliot DeNolf
23c7ab2bc4 ci: add plugin-relationship-object-ids to publish list (#6335) 2024-05-13 10:14:10 -04:00
Fredrik
0680e0c58b chore(plugin-nested-docs): export the getParents utility (#6325) 2024-05-13 13:39:35 +00:00
Jessica Chowdhury
b3df253880 feat: adds translations for API view and select component (#6143) 2024-05-13 13:03:26 +00:00
Elliot DeNolf
a78b44d0c1 docs: add storage adapter docs (#6334) 2024-05-13 08:46:05 -04:00
Elliot DeNolf
d272a1fd22 docs: update email docs for new adapters (#6332) 2024-05-13 08:44:59 -04:00
Elliot DeNolf
095e4402ac test: type fixes (#6331) 2024-05-13 01:37:52 +00:00
Elliot DeNolf
60d2def428 chore: create file that imports all 2.x exports (#6224) 2024-05-12 21:28:19 -04:00
Paul
2f02b3a6e1 fix: wrong translation key being used as upload:Sizes instead of upload:sizes (#6323) 2024-05-11 15:09:38 +00:00
Jarrod Flesch
4f0ddcf632 chore: improves lexical fixed toolbar styles (#6317) 2024-05-10 17:38:24 -04:00
Jarrod Flesch
693621a6e3 chore: improves types for lexical client features (#6318) 2024-05-10 17:38:10 -04:00
Elliot DeNolf
5b201392cc ci: add storage-uploadthing 2024-05-10 17:15:22 -04:00
Elliot DeNolf
a70bcf81c0 chore(release): v3.0.0-beta.30 [skip ci] 2024-05-10 17:10:18 -04:00
Elliot DeNolf
ed880d5018 feat: storage-uploadthing package (#6316)
Co-authored-by: James <james@trbl.design>
2024-05-10 17:05:35 -04:00
Patrik
ea84e82ad5 feat(payload, ui): adds disableListColumn & disableListFilter to fields admin props (#6238) 2024-05-10 15:59:29 -04:00
Patrik
4216d69ccb fix(richtext-slate): list item values returning null (#6291) 2024-05-10 15:42:37 -04:00
Patrik
dcad5003f5 fix(ui): appends editDepth value to radio & checkbox IDs when inside drawer (#6252) 2024-05-10 15:56:24 +00:00
Paul
bd9c06a99d chore: update readme for tailwind example (#6314) 2024-05-10 12:13:10 -03:00
Elliot DeNolf
48932ef54d chore: format plugin object ids (#6310) 2024-05-10 14:20:21 +00:00
Patrik
4aeefc5a1a feat: adds plugin-relationship-object-ids package (#6045) 2024-05-10 09:31:25 -04:00
Ritsu
e96ff90029 fix(next): respect fallback locale null value (#6207) 2024-05-10 14:27:13 +01:00
Elliot DeNolf
f6e77b845b ci: add npm provenance to canary releases 2024-05-10 09:08:16 -04:00
Elliot DeNolf
a0bb02d05a chore: remove unneeded val in redirects publish config 2024-05-09 23:58:58 -04:00
Elliot DeNolf
354ad7092c chore: type gen formatting (#6309) 2024-05-09 23:55:55 -04:00
Elliot DeNolf
f9d862d854 ci(scripts): update getPackageRegistryVersions [skip ci] 2024-05-09 23:35:50 -04:00
Elliot DeNolf
f41576dd65 ci: canary releases (#6308) 2024-05-09 23:12:47 -04:00
Elliot DeNolf
ffa20aa7d0 chore(release): v3.0.0-beta.29 [skip ci] 2024-05-09 17:19:54 -04:00
Alessio Gravili
f7a2cf96b9 chore: properly working generated types within tests (#6288) 2024-05-09 17:12:51 -04:00
Alessio Gravili
cfeac79b99 feat!: fix non-functional custom RSC component handling, separate label and description props, fix non-functional label function handling (#6264)
Breaking Changes:

- Globals config: `admin.description` no longer accepts a custom component. You will have to move it to `admin.components.elements.Description`
- Collections config: `admin.description` no longer accepts a custom component. You will have to move it to `admin.components.edit.Description`
- All Fields: `field.admin.description` no longer accepts a custom component. You will have to move it to `field.admin.components.Description`
- Collapsible Field: `field.label` no longer accepts a custom component. You will have to move it to `field.admin.components.RowLabel`
- Array Field: `field.admin.components.RowLabel` no longer accepts strings or records
- If you are using our exported field components in your own app, their `labelProps` property has been stripped down and no longer contains the `label` and `required` prop. Those can now only be configured at the top-level
2024-05-09 17:12:01 -04:00
Elliot DeNolf
821bed0ea6 ci: all green (#6289) 2024-05-09 16:33:05 -04:00
Jacob Fletcher
9e9111666b chore(examples/live-preview): migrates to 3.0 (#6268) 2024-05-09 15:32:46 -04:00
David Velasco
5065322d31 fix(plugin-form-builder): resolve labelValue from LabelFunction (#5817) 2024-05-09 16:23:44 -03:00
Paul
ad4796cdb2 fix(plugin-form-builder): export types correctly (#6287) 2024-05-09 14:42:14 -03:00
Alessio Gravili
43b7ba82da chore: fix dev:generate-types not working (#6284) 2024-05-09 10:37:11 -04:00
Alessio Gravili
3785c79ac9 fix(templates): yarn install broken for new template installs (#6283) 2024-05-09 10:17:09 -04:00
Jarrod Flesch
4384e9eb0e chore: fixes cannot destructure property 'schema' issue (#6282) 2024-05-09 10:16:30 -04:00
Alessio Gravili
9364f8da2e fix(templates): blank-3.0: pin next version, as it was breaking new installs (#6281) 2024-05-09 10:05:38 -04:00
Elliot DeNolf
a4ef359660 chore: examples linting (#6269) 2024-05-08 14:58:57 -04:00
Elliot DeNolf
848c05f247 chore(deps): sync pnpm-lock.yaml 2024-05-08 14:37:48 -04:00
Elliot DeNolf
ec556360b6 chore(release): v3.0.0-beta.28 [skip ci] 2024-05-08 14:08:09 -04:00
Elliot DeNolf
d99b426e3b fix: live-preview-* dep version 2024-05-08 14:07:06 -04:00
Elliot DeNolf
19a78297b4 ci: add live-preview and live-preview-react to publish list 2024-05-08 13:48:17 -04:00
Elliot DeNolf
e95eea694c chore(release): v3.0.0-beta.27 [skip ci] 2024-05-08 13:33:55 -04:00
Kendell Joseph
4c6aaafe88 feat(ui): toggle sortable arrays and blocks (#6008) 2024-05-08 13:28:26 -04:00
Elliot DeNolf
dc8c099d9e ci: publish script retry on failure, log all version on completion 2024-05-08 12:30:48 -04:00
Elliot DeNolf
259ae674a1 chore(release): v3.0.0-beta.26 [skip ci] 2024-05-08 11:18:39 -04:00
Jacob Fletcher
731f023c6d feat: ssr live preview (#6239) 2024-05-08 11:08:15 -04:00
Elliot DeNolf
86b19d4c74 chore: update codeowners file [skip ci] 2024-05-08 10:05:55 -04:00
Elliot DeNolf
17b8c29799 chore(eslint): no imports from exports dir (#6263) 2024-05-08 10:01:20 -04:00
Elliot DeNolf
29af2849ba ci: yaml formatting [skip ci] 2024-05-08 09:40:57 -04:00
Jarrod Flesch
a7ac5efd70 feat: improves crop rendering in thumbnail (#6260) 2024-05-08 08:27:30 -04:00
Elliot DeNolf
15c7a9dcf8 ci: only lint on prs 2024-05-07 16:40:31 -04:00
Alessio Gravili
8e55a2a866 feat(richtext-lexical)!: strongly typed PluginComponent types, remove LexicalBlocks, improve exports, fix e2e (#6255)
**BREAKING:**
- Narrows the type of the `plugins` prop of lexical features. Client props are now also automatically provided to the plugin components. To migrate, type your plugin as either `PluginComponent` or PluginComponentWithAnchor.
- `BlockQuoteFeature` has been renamed to `BlockquoteFeature`
- `createClientComponent` is now exported only from /components
- The `LexicalBlocks` and `FieldWithRichTextRequiredEditor` types have been removed in favor of just `Blocks` & `Fields`, as well as improved validation.
2024-05-07 16:26:28 -04:00
Alessio Gravili
0f306da63b fix(richtext-lexical): various UX improvements (#6241) 2024-05-07 10:42:26 -04:00
Alessio Gravili
ba9ea5c752 fix(richtext-lexical): fixed toolbar actions not ensuring editor focus, various link editor selection issues 2024-05-07 10:40:56 -04:00
Alessio Gravili
53b7d6f89f fix(richtext-lexical): fixed toolbar not wrapping correctly on small screen sizes 2024-05-07 09:51:45 -04:00
Alessio Gravili
f5fb095df4 feat(richtext-lexical): improve draggable block indicator style and animations 2024-05-07 09:39:11 -04:00
Alessio Gravili
721919fae9 feat(richtext-lexical): add maxDepth property to various lexical features (#6242) 2024-05-07 09:11:34 -04:00
Elliot DeNolf
d3e27e87fe ci: add lint job (#6247) 2024-05-06 23:32:45 -04:00
Jacob Fletcher
e1ff92e8c6 chore(plugin-stripe)!: disables rest proxy by default (#6230) 2024-05-06 17:33:43 -04:00
Jarrod Flesch
ac5d744914 fix: properly extracts fallbackLang (#6237) 2024-05-06 15:56:46 -04:00
Alessio Gravili
b94a265fad fix(richtext-lexical): ensure inline toolbar is positioned between link editor and fixed toolbar 2024-05-06 15:07:16 -04:00
Alessio Gravili
1ba3a92745 fix(richtext-lexical): text within relationship and upload node components was not able to be selected without selection resetting immediately 2024-05-06 14:58:42 -04:00
Alessio Gravili
9814fd705e fix(richtext-lexical): inline editor is overlapping the clickable link editor for the first line 2024-05-06 14:54:35 -04:00
Alessio Gravili
20455f4fc2 fix(richtext-lexical): floating link editor did not properly hide if selection is not a range selection 2024-05-06 14:50:52 -04:00
Elliot DeNolf
9f37bf7397 ci(scripts): improve footer parsing trailing quote 2024-05-06 13:53:34 -04:00
Elliot DeNolf
892b884745 chore(release): v3.0.0-beta.25 [skip ci] 2024-05-06 13:02:08 -04:00
Elliot DeNolf
e8dd0a7daf chore: more type updates (#6234) 2024-05-06 12:57:50 -04:00
Elliot DeNolf
26151e39c9 chore: package type export consistency (#6232) 2024-05-06 11:38:23 -04:00
Elliot DeNolf
453e331014 chore: package.json author consistency 2024-05-06 11:05:44 -04:00
Paul
7f72006020 chore(plugin-stripe)!: add types exports and rename types to be more consistent with other plugins (#6216)
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
2024-05-06 10:37:44 -04:00
Paul
3c13df3c2d chore(plugin-search): add types export and rename internal config (#6220)
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
2024-05-06 09:54:01 -04:00
Paul
d31af813e2 chore(plugin-nested-docs): add types export (#6219)
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
2024-05-06 09:52:24 -04:00
Paul
a85dc66a39 chore(plugin-form-builder): add types export (#6218)
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
2024-05-06 09:48:14 -04:00
Paul
9492f0ae29 chore(plugin-seo): export types (#6217) 2024-05-06 09:46:25 -04:00
Jarrod Flesch
51149c75ff fix: threads draft arg through for child resolvers in GraphQL queries (#6196) 2024-05-04 16:43:17 -04:00
Jarrod Flesch
56bedb821f chore: adjusts forgot pw email template (#6209) 2024-05-04 16:42:22 -04:00
Friggo
d3bca574aa feat(translations): add Slovak translation (#6114) 2024-05-04 17:26:29 -03:00
Alessio Gravili
c462bf229f feat(richtext-lexical)!: add FixedToolbarFeature (#6192)
BREAKING:

- The default inline toolbar has now been extracted into an `InlineToolbarFeature`. While it's part of the defaultFeatures, you might have to add it to your editor features if you are not including the defaultFeatures and still want to keep the inline toolbar (floating toolbar)
- Some types have been renamed, e.g. `InlineToolbarGroup` is now `ToolbarGroup`, and `InlineToolbarGroupItem` is now `ToolbarGroupItem`
- The `displayName` property of SlashMenuGroup and SlashMenuItem has been renamed to `label` to match the `label` prop of the toolbars
- The `inlineToolbarFeatureButtonsGroupWithItem`, `inlineToolbarFormatGroupWithItems` and `inlineToolbarTextDropdownGroupWithItems` exports have been renamed to `toolbarTextDropdownGroupWithItems`,  `toolbarFormatGroupWithItems`, `toolbarFeatureButtonsGroupWithItems`
2024-05-03 19:55:26 -04:00
Paul
8a452c42af fix(plugin-form-builder): custom formSubmission hooks overriding core ones (#6204) 2024-05-03 18:02:17 -03:00
Jacob Fletcher
07f2d74dc3 chore(examples/auth): migrates to 3.0 (#5877) 2024-05-03 16:28:27 -04:00
Elliot DeNolf
2ce65dc1e0 chore(release): v3.0.0-beta.24 [skip ci] 2024-05-03 14:56:47 -04:00
Elliot DeNolf
37be06448c ci: more resilient release script (#6202) 2024-05-03 14:39:26 -04:00
Elliot DeNolf
9c13089a2f chore: add dotenv to test dir [skip ci] 2024-05-03 13:42:01 -04:00
Paul
b642cb2d93 chore!: update plugin exports to be named and consistent (#6195)
BREAKING CHANGE: All plugins have been updated to use named exports and the names have been updated to be consistent.

// before
import { cloudStorage } from '@payloadcms/plugin-cloud-storage'
// current
import { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'

//before
import { payloadCloud } from '@payloadcms/plugin-cloud'
// current
import { payloadCloudPlugin } from '@payloadcms/plugin-cloud'

//before
import formBuilder from '@payloadcms/plugin-form-builder'
// current
import { formBuilderPlugin } from '@payloadcms/plugin-form-builder'

//before
import { nestedDocs } from '@payloadcms/plugin-nested-docs'
// current
import { nestedDocsPlugin } from '@payloadcms/plugin-nested-docs'

//before
import { redirects } from '@payloadcms/plugin-redirects'
// current
import { redirectsPlugin } from '@payloadcms/plugin-redirects'

// before
import search from '@payloadcms/plugin-search'
// current
import { searchPlugin } from '@payloadcms/plugin-search'

//before
import { sentry } from '@payloadcms/plugin-sentry'
// current
import { sentryPlugin } from '@payloadcms/plugin-sentry'

// before
import { seo } from '@payloadcms/plugin-seo'
// current
import { seoPlugin } from '@payloadcms/plugin-seo'
2024-05-03 13:36:36 -03:00
Elliot DeNolf
9e5d521567 ci: allow examples/* scopes 2024-05-03 11:41:16 -04:00
Jessica Chowdhury
6eabc99e01 chore: translate checkbox result in collection list view (#6165) 2024-05-03 10:44:43 -04:00
Jacob Fletcher
ea917dd811 feat(next): supports custom login redirects in initPage (#6186) 2024-05-03 09:48:57 -04:00
Jacob Fletcher
070d8e1731 feat(next): supports custom login redirects in initPage 2024-05-03 09:24:34 -04:00
Jacob Fletcher
664c60d2bc chore(next): restructures initPage 2024-05-03 09:17:17 -04:00
Jarrod Flesch
e25814e1ee fix: cascade graphql locales through relationships (#6166) 2024-05-03 08:33:53 -04:00
Jarrod Flesch
27ea117731 fix: only allow save after form is modified (#6189) 2024-05-03 08:28:37 -04:00
Alessio Gravili
7ab156e117 feat(richtext-lexical)!: finalize ClientFeature interface (#6191)
**BREAKING:**
If you have own, custom lexical features, there will be a bunch of breaking API changes for you. The saved JSON data is not affected.

- `floatingSelectToolbar` has been changed to `toolbarInline`

- `slashMenu.dynamicOptions `and `slashMenu.options` have been changed to `slashMenu.groups` and `slashMenu.dynamicGroups`

- `toolbarFixed.sections` is now `toolbarFixed.groups`

- Slash menu group `options` and toolbar group `entries` have both been renamed to `items`

- Toolbar group item `onClick` has been renamed to `onSelect` to match slash menu properties

- slashMenu item `onSelect` is no longer auto-wrapped inside an `editor.update`. If you perform editor updates in them, you have to wrap it inside an `editor.update` callback yourself. Within our own features this extra control has removed a good amount of unnecessary, nested `editor.update` calls, which is good

- Slash menu items are no longer initialized using the `new` keyword, as they are now types and no longer classes. You can convert them to an object and add the `key` property as an object property instead of an argument to the previous SlashMenuItem constructor

- CSS classnames for slash menu and toolbars, as well as their items, have changed

- `CheckListFeature` is now exported as and has been renamed to `ChecklistFeature`

For guidance on migration, check out how we migrated our own features in this PR's diff: https://github.com/payloadcms/payload/pull/6191/files
2024-05-02 21:38:15 -04:00
Paul
f2d415663f fix: ensures confirm password remains on form state (#6190) 2024-05-02 18:53:35 -03:00
Jarrod Flesch
bdf08a19d1 fix: moves ts-essentials to prod deps (#6187) 2024-05-02 16:37:05 -04:00
Elliot DeNolf
cb90e9f622 chore(release): v3.0.0-beta.23 [skip ci] 2024-05-02 16:05:19 -04:00
Elliot DeNolf
b05a5e1fb6 chore(deps): bump turborepo 2024-05-02 15:57:18 -04:00
Elliot DeNolf
92a5da1006 chore: move stripe postman out of src [skip ci] 2024-05-02 15:50:44 -04:00
Paul
75a95469b2 feat(plugin-stripe): update plugin stripe for v3 (#6019) 2024-05-02 16:19:27 -03:00
Jarrod Flesch
c0ae287d46 fix: reset password validations (#6153)
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
Co-authored-by: James <james@trbl.design>
Co-authored-by: Alessio Gravili <alessio@gravili.de>
2024-05-02 15:08:47 -04:00
Patrik
a2b92aa3ff fix(ui): watch "where" query param inside route and reset WhereBuilder (#6184) 2024-05-02 13:25:51 -04:00
Friggo
544a2285d3 chore(translations): czech translation improvements (#6078) 2024-05-02 12:54:18 -04:00
Elliot DeNolf
8c39950ea3 chore(release): v3.0.0-beta.22 [skip ci] 2024-05-02 12:27:28 -04:00
Yiannis Demetriades
6d642fe9b9 fix(templates): adds back missing CSS import in blank 3.0 template (#6183) 2024-05-02 11:30:58 -04:00
Jacob Fletcher
f175a741bc chore(next): exports initPage utility (#6182) 2024-05-02 11:22:13 -04:00
Jacob Fletcher
3290376f80 fix(next): ensures admin access only blocks admin routes 2024-05-02 11:07:43 -04:00
Jacob Fletcher
2b5c1ba99a chore(next): exports initPage utility 2024-05-02 10:32:17 -04:00
Alessio Gravili
bcb3f08386 chore: hide test flakes, improve playwright CI logs, significantly reduce playwright timeouts, add back test retries, cache playwright browsers in CI, disable CI telemetry, improve test throttle utility (#6155) 2024-05-01 17:35:41 -04:00
Wilson
b729b9bebd docs: add before login comments (#6101) 2024-05-01 15:59:11 -04:00
Tylan Davis
26ee91eb48 docs: adjust line breaks in code blocks (#6001) 2024-05-01 15:57:39 -04:00
Paul
43a17f67a0 chore(richtext-lexical): export types for additional props (#6173) 2024-05-01 15:03:06 -03:00
Elliot DeNolf
c71d2db949 chore(release): v3.0.0-beta.21 [skip ci] 2024-05-01 13:25:14 -04:00
Jacob Fletcher
04f1df8af1 fix(templates): updates payload app files (#6172) 2024-05-01 12:43:47 -04:00
Elliot DeNolf
1c490aee42 fix(deps): move file-type to deps (#6171) 2024-05-01 12:20:45 -04:00
Elliot DeNolf
c6132df866 chore: rename resend package (#6168) 2024-05-01 12:02:40 -04:00
Alessio Gravili
d8f91cc94c feat(richtext-lexical)!: various validation improvement (#6163)
BREAKING: this will now display errors if you're previously had invalid link or upload fields data - for example if you have a required field added to an uploads node and did not provide a value to it every time you've added an upload node
2024-05-01 11:33:02 -04:00
Alessio Gravili
568b074809 fix: various loader issues (#6090) 2024-05-01 10:45:28 -04:00
Alessio Gravili
401c16e485 chore: lexical int tests: do not use relationTo to collection with rich text relationships disabled 2024-05-01 00:47:40 -04:00
Elliot DeNolf
17bee6a145 ci: reworks changelog and release notes generation (#6164) 2024-04-30 23:50:49 -04:00
Alessio Gravili
8829fba6cf feat(richtext-lexical)!: add validation to link and upload nodes
BREAKING: this will now display errors if you're previously had invalid link or upload fields data - for example if you have a required field added to an uploads node and did not provide a value to it every time you've added an upload node
2024-04-30 23:14:27 -04:00
Alessio Gravili
e8983abe65 ci: enable fields RichText e2e test suite 2024-04-30 23:12:47 -04:00
Alessio Gravili
01f38c4e33 feat(richtext-lexical): link node: disable client-side link validation. This allows overriding validation behavior by providing your own url field to the Link feature.
Allows you to, for example, allow anchor nodes to be inputted as URL by overriding validation.
2024-04-30 23:12:07 -04:00
Alessio Gravili
10b99ceb6f fix: add missing error logger to buildFormState error catch 2024-04-30 23:10:52 -04:00
Alessio Gravili
1140426b73 Merge remote-tracking branch 'origin/beta' into feat/improve-lexical-validations-2 2024-04-30 23:02:07 -04:00
Alessio Gravili
5a82f34801 feat(richtext-lexical)!: change link fields handling (#6162)
**BREAKING:**
- Drawer fields are no longer wrapped in a `fields` group. This might be breaking if you depend on them being in a field group in any way - potentially if you use custom link fields. This does not change how the data is saved
- If you pass in an array of custom fields to the link feature, those were previously added to the base fields. Now, they completely replace the base fields for consistency. If you want to ADD fields to the base fields now, you will have to pass in a function and spread `defaultFields` - similar to how adding your own features to lexical works

**Example Migration for ADDING fields to the link base fields:**

**Previous:**
```ts
 LinkFeature({
    fields: [
      {
        name: 'rel',
        label: 'Rel Attribute',
        type: 'select',
        hasMany: true,
        options: ['noopener', 'noreferrer', 'nofollow'],
        admin: {
          description:
            'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
        },
      },
    ],
  }),
```

**Now:**
```ts
 LinkFeature({
    fields: ({ defaultFields }) => [
      ...defaultFields,
      {
        name: 'rel',
        label: 'Rel Attribute',
        type: 'select',
        hasMany: true,
        options: ['noopener', 'noreferrer', 'nofollow'],
        admin: {
          description:
            'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
        },
      },
    ],
  }),
2024-04-30 23:01:08 -04:00
Alessio Gravili
5420d889fe fix(richtext-slate): do not add empty fields group if no custom fields are added 2024-04-30 21:53:47 -04:00
Alessio Gravili
d9bb51fdc7 feat(richtext-lexical)!: initialize lexical during sanitization (#6119)
BREAKING:

- sanitizeFields is now an async function
- the richText adapters now return a function instead of returning the adapter directly
2024-04-30 15:09:32 -04:00
Alessio Gravili
9a636a3cfb fix(richtext-lexical): floating toolbar caret positioned incorrectly for some line heights (#6149) 2024-04-30 12:01:25 -04:00
Alessio Gravili
181f82f33e feat(richtext-lexical): implement relationship node click and delete/backspace handling (#6147) 2024-04-30 11:11:47 -04:00
Alessio Gravili
6a9cde24b0 fix(richtext-lexical): drag and add block handles disappear too quickly for smaller screen sizes. (#6144) 2024-04-30 10:50:18 -04:00
Elliot DeNolf
dc31d9c715 test: parse and update tsconfig in before test hook 2024-04-30 00:24:06 -04:00
Elliot DeNolf
45b3f06e1b chore: implement better tsconfig reset mechanism 2024-04-29 23:23:09 -04:00
Elliot DeNolf
d5f7944ac4 chore(eslint): set prefer-ts-expect-error to error 2024-04-29 22:30:05 -04:00
Elliot DeNolf
3d50caf985 feat: implement resend rest email adapter (#5916) 2024-04-29 22:06:53 -04:00
Jacob Fletcher
4d7ef58e7e fix: blocks non-admin users from admin access (#6127) 2024-04-29 19:53:18 -04:00
Paul
3e117f4e99 chore: add graphql as a dependency to the blank template (#6128) 2024-04-29 20:09:27 -03:00
Elliot DeNolf
888d6f8856 ci(scripts): adjust release publish limit 2024-04-29 17:19:36 -04:00
Elliot DeNolf
9ebf8693d4 chore(release): v3.0.0-beta.20 [skip ci] 2024-04-29 16:51:35 -04:00
James Mikrut
d8c3127b09 fix: local req missing url headers (#6126)
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2024-04-29 16:40:59 -04:00
James Mikrut
b6631f4778 fix: logout-inactivity route was 404ing (#6121) 2024-04-29 15:53:08 -04:00
Elliot DeNolf
2e77bdf11e test: add test email adapter, use for all tests by default (#6120) 2024-04-29 14:38:35 -04:00
Elliot DeNolf
cce75f11ca ci: add better message for PR subject pattern error 2024-04-29 14:34:04 -04:00
Elliot DeNolf
d8b6b39dbb fix: validate user slug is an auth-enabled collection (#6118) 2024-04-29 14:25:23 -04:00
Jacob Fletcher
fa89057aac fix(next,ui): properly sets document operation for globals (#6116) 2024-04-29 13:58:06 -04:00
Jarrod Flesch
15db0a8018 fix: conditions throwing errors break form state (#6113) 2024-04-29 12:54:00 -04:00
Elliot DeNolf
b7a4d9cea4 chore(deps): adjust node engines to account for node register requirement (#6115) 2024-04-29 12:28:31 -04:00
Elliot DeNolf
5b676c36e5 docs(storage-*): readme fixes 2024-04-29 09:40:39 -04:00
Jacob Fletcher
32231762ff chore: version permissions (#6068) 2024-04-29 08:22:56 -04:00
Elliot DeNolf
a7096c1599 chore: telemetry localization (#6075) 2024-04-28 22:17:08 -04:00
Alessio Gravili
bed428c27e feat(richtext-lexical)!: upgrade lexical from 0.13.1 to 0.14.5 and backport other changes (#6095)
BREAKING:

- Lexical may introduce breaking changes in their updates. Please consult their changelog. One breaking change I noticed is that the SerializedParagraphNode now has a new, required textFormat property.

- Now that lexical supports ESM, all CJS-style imports have been changed to ESM-style imports. You may have to do the same in your codebase if you import from lexical core packages
2024-04-28 20:25:27 -04:00
Elliot DeNolf
873e698352 docs: storage-* and plugin-cloud-storage updates (#6096) 2024-04-28 20:07:49 -04:00
Alessio Gravili
ad13577399 chore(richtext-lexical): fix build and backport badNode logic change from lexical core 2024-04-28 19:58:07 -04:00
Alessio Gravili
31a9c77055 fix(richtext-lexical): preserve bullet list item indent on newline
BACKPORTS https://github.com/facebook/lexical/pull/5578
2024-04-28 19:30:52 -04:00
Alessio Gravili
bae0c2df5f fix(richtext-lexical): add missing uuid dependency 2024-04-28 19:26:25 -04:00
Alessio Gravili
0ed31def68 feat(richtext-lexical): implement upload node click and delete/backspace handling 2024-04-28 19:19:34 -04:00
Alessio Gravili
0e7a6ad5ab fix(richtext-lexical): prevent link modal from showing if selection spans further than the link
Backports https://github.com/facebook/lexical/pull/5551
2024-04-28 19:03:02 -04:00
Alessio Gravili
180797540c feat(richtext-lexical)!: change all CJS lexical imports to ESM lexical imports
BREAKING: You might have to do the same if you import from lexical in your application
2024-04-28 18:50:58 -04:00
Alessio Gravili
c00babf9b3 chore(richtext-lexical): upgrade all lexical dependencies from 0.13.1 to 0.14.5 2024-04-28 17:52:11 -04:00
Alessio Gravili
943681ae3c chore: upgrade typescript from 5.4.4 to 5.4.5 (#6093) 2024-04-28 17:46:41 -04:00
Alessio Gravili
f14ce367d2 fix(richtext-lexical): type errors for FeatureProviderServer with typescript strict mode (#6091) 2024-04-28 17:12:47 -04:00
Paul
3eb5766323 fix: issue with dupplicate ':' in email links (#6086) 2024-04-28 17:22:07 -03:00
Alessio Gravili
cd5e8d7b52 fix: importWithoutClientFiles not working due to incorrect import path used 2024-04-28 16:06:01 -04:00
Alessio Gravili
361d12e97c chore: loader test: use importConfig helper instead of manually registering loader to realistically test what a user would experience 2024-04-28 16:04:11 -04:00
Elliot DeNolf
fb4a5a3715 chore(ui): fix bad imports 2024-04-28 14:53:15 -04:00
Elliot DeNolf
9c2585ba86 chore(eslint): no-relative-monorepo-imports on package dir, other cleanup 2024-04-28 14:49:48 -04:00
Paul
feb6296bb4 chore: add tailwind and shadcn/ui example (#6085) 2024-04-28 15:06:43 -03:00
Alessio Gravili
74eb71c304 chore: add failing loader test case 2024-04-27 21:13:38 -04:00
Alessio Gravili
fa2083f764 fix: loader: typescript module resolver not resolving to source path of symlinked module 2024-04-27 20:45:04 -04:00
Jacob Fletcher
7111834a99 fix(ui): conditionally fetches versions based on read access 2024-04-26 17:40:28 -04:00
Jacob Fletcher
a943c7eddb fix(ui): conditionally renders versions tab based on read access 2024-04-26 17:40:14 -04:00
Jacob Fletcher
2d089a7bae chore: threads permissions through document tab conditions 2024-04-26 17:38:59 -04:00
Elliot DeNolf
5bba969f0d chore(release): v3.0.0-beta.19 [skip ci] 2024-04-26 17:08:50 -04:00
Jarrod Flesch
3a43fd34c0 chore: fixes bad auto import (#6070) 2024-04-26 16:45:24 -04:00
Jarrod Flesch
d9005b3f53 chore: file uploads, broken import path (#6069) 2024-04-26 16:26:11 -04:00
Jarrod Flesch
fab9e32175 fix: correct createPayloadRequest routeParams (#6059) 2024-04-26 16:13:21 -04:00
Jarrod Flesch
e71c1c2ec4 fix: formData handling on Vercel (#6067) 2024-04-26 16:10:14 -04:00
Dan Ribbens
81fb0515fb fix: bulk publish from collection list (#6065) 2024-04-26 15:46:02 -04:00
Elliot DeNolf
739dfc1434 chore: convert all errors to named exports (#6061) 2024-04-26 13:24:18 -04:00
Jacob Fletcher
a4e8795666 fix(deps): dedupes react (#6064) 2024-04-26 13:22:12 -04:00
Elliot DeNolf
14134d637d fix: properly handle external file url (#6060) 2024-04-26 12:02:07 -04:00
Elliot DeNolf
2b698a9018 chore: add more valid pr scopes [skip ci] 2024-04-26 11:55:01 -04:00
Elliot DeNolf
91684c8a7d ci: app build with packed (#6051) 2024-04-26 00:19:44 -04:00
Elliot DeNolf
fbdfe1d9dd chore: set -ex on pack and build step 2024-04-25 23:56:16 -04:00
Elliot DeNolf
7221725121 chore: start mongo for build 2024-04-25 23:46:48 -04:00
Elliot DeNolf
640348df3a chore: use --ignore-workspace in template install 2024-04-25 23:37:05 -04:00
Elliot DeNolf
4ed99e017a ci: add app-build-with-packed job 2024-04-25 23:28:28 -04:00
Elliot DeNolf
df6b9dd30b ci(scripts): update pack-all-to-dest 2024-04-25 23:25:31 -04:00
Elliot DeNolf
faf142baff chore: sort package.json files (#6050) 2024-04-25 22:41:55 -04:00
Elliot DeNolf
f80cb9f553 chore: clean up package.json descriptions and keywords 2024-04-25 22:39:03 -04:00
Elliot DeNolf
d3eaa1fceb chore: add sort-package-json to lint-staged 2024-04-25 22:23:58 -04:00
Elliot DeNolf
df77152851 chore: add sort-package-json, sort all package.json files 2024-04-25 22:19:37 -04:00
Elliot DeNolf
937202b27c fix(deps): remove monorepo deps 2024-04-25 22:12:44 -04:00
Paul
3581f39c31 chore: update whitelabel example (#6049) 2024-04-25 17:24:42 -03:00
Paul
c1d9c81b68 chore: update virtual fields example (#6043) 2024-04-25 16:13:10 -03:00
Jarrod Flesch
20355a4dd4 fix: version restoration (#6040) 2024-04-25 14:15:12 -04:00
Elliot DeNolf
cf66d7f09b docs: new packages (#6041) 2024-04-25 13:14:52 -04:00
Elliot DeNolf
30afe81462 docs: add docs for all new storage packages 2024-04-25 13:08:32 -04:00
Elliot DeNolf
18ee6e8867 docs: add docs for email-nodemailer 2024-04-25 13:08:18 -04:00
Paul
9f78a93403 chore: update hierarchy example (#6036) 2024-04-25 12:54:17 -03:00
Dan Ribbens
bd046e2437 fix(db-postgres): use locales suffix (#6032) 2024-04-25 11:07:52 -04:00
Elliot DeNolf
e9004a93a4 ci(scripts): safer package details retrieval 2024-04-25 10:50:41 -04:00
Elliot DeNolf
4816a1638a chore(release): v3.0.0-beta.18 [skip ci] 2024-04-25 10:31:56 -04:00
Jarrod Flesch
22c53392a3 chore: improves types for payloadRequest (#6012) 2024-04-25 10:23:03 -04:00
Paul
bdaa9e831d chore: add e2e tests for creating first user (#6027) 2024-04-25 10:57:50 -03:00
James Mikrut
036bcd6b8f chore: adds uuid to test (#6030) 2024-04-25 09:51:49 -04:00
Dan Ribbens
4d2bc861cf fix: disable api key beta (#6021) 2024-04-25 09:39:30 -04:00
James Mikrut
98722dc0fd fix(db-postgres): postgres version id bug (#6026) 2024-04-25 09:23:13 -04:00
James Mikrut
629d7c3263 fix(db-postgres): fully functional dbNames (#6023) 2024-04-24 22:42:24 -04:00
James Mikrut
5f7af5317a fix(next): ensures create-first user works (#6020) 2024-04-24 22:23:14 -04:00
James
8bb1b60964 chore: removes unused line 2024-04-24 22:22:40 -04:00
Elliot DeNolf
7ef5493414 ci(scripts): misc improvements 2024-04-24 21:04:12 -04:00
James
a3ac838221 chore: cleanup 2024-04-24 19:52:58 -04:00
James
14400d1cb9 chore: functional create-first-user 2024-04-24 19:48:58 -04:00
James
7d531646fd Merge branch 'fix/create-first-user-pt2' of github.com:payloadcms/payload into fix/create-first-user-pt2 2024-04-24 18:05:02 -04:00
Jacob Fletcher
6f6c1435c7 fix(ui): renders stay logged in modal (#6009) 2024-04-24 16:19:11 -04:00
Elliot DeNolf
332b8b6f34 ci(scripts): true publish with pLimit 2024-04-24 15:26:24 -04:00
Elliot DeNolf
d40a734080 wip: create first user fix 2024-04-24 15:17:13 -04:00
Dan Ribbens
94f1dfef52 fix: bulk publish (#6007) 2024-04-24 15:05:02 -04:00
Elliot DeNolf
0857dbe465 Revert "fix: issues creating the first user (#5986)"
This reverts commit 0ede95f375.
2024-04-24 14:36:08 -04:00
Elliot DeNolf
71f19fba58 chore(release): v3.0.0-beta.15 [skip ci] 2024-04-24 13:41:28 -04:00
Paul
24b18fb0fd feat!: removed getDataAndFile and getLocales from createPayloadRequest in favour of new utilities addDataAndFileToRequest and addLocalesToRequest (#5999) 2024-04-24 13:31:54 -03:00
Elliot DeNolf
5731241a5c fix(db-postgres): postgres uuid (#6003)
Co-authored-by: James <james@trbl.design>
2024-04-24 11:59:39 -04:00
Dan Ribbens
47e70abb4e fix: type collection config missing dbName (#5983) 2024-04-24 11:32:59 -04:00
Paul
0ede95f375 fix: issues creating the first user (#5986)
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2024-04-24 11:30:52 -04:00
Jarrod Flesch
b723efdd3b chore: fixing flakey tests (#5984) 2024-04-24 00:44:43 -04:00
Elliot DeNolf
14c513690d ci: lint pr titles (#5988) 2024-04-23 23:40:55 -04:00
Alessio Gravili
88f239e784 feat(richtext-lexical)!: rework population behavior and allow richText adapter field hooks (#5893)
BREAKING:

- Unpopulated lexical relationship, link and upload nodes now save the relationTo document ID under value instead of value.id. This matches the behavior of core relationship fields. This changes the shape of the saved JSON data
- Any custom features which add their own population promises need to be reworked. populationPromises no longer accepts the promises as a return value. Instead, it expects you to mutate the promises array which is passed through, which mimics the way it works in core
2024-04-23 20:43:07 -04:00
Alessio Gravili
a1f6bf8a67 fix(richtext-lexical): Heading feature: enabledHeadingSizes not being applied 2024-04-23 20:37:11 -04:00
Alessio Gravili
912dcd38df fix(richtext-lexical): add missing HorizontalRuleFeature export 2024-04-23 20:25:12 -04:00
Alessio Gravili
da5028cdee feat(richtext-lexical): show loading indicator while block nodes are loading 2024-04-23 20:22:18 -04:00
Elliot DeNolf
899faa62f1 chore: update pnpm-lock 2024-04-23 17:01:57 -04:00
Alessio Gravili
9df6a644c9 chore: update lockfile 2024-04-23 16:37:02 -04:00
Alessio Gravili
1a6d9eaa11 Merge remote-tracking branch 'origin/beta' into fix/lexical-localization 2024-04-23 16:33:54 -04:00
Alessio Gravili
7d447af277 chore: add remaining missing preferences prop to validations 2024-04-23 15:46:01 -04:00
Elliot DeNolf
d8baaab849 chore(release): v3.0.0-beta.14 [skip ci] 2024-04-23 15:29:35 -04:00
Jarrod Flesch
3e1523f007 fix: move graphql-http from devDep to dep in next package (#5982)
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
2024-04-23 15:27:43 -04:00
Alessio Gravili
fa38af025f Merge branch 'beta' into fix/lexical-localization 2024-04-23 15:20:56 -04:00
Elliot DeNolf
6ca9ff847f chore(release): v3.0.0-beta.13 [skip ci] 2024-04-23 15:15:21 -04:00
Alessio Gravili
a8824b2b51 fix: incorrect value for empty preferences passed into buildStateFromSchema 2024-04-23 15:12:55 -04:00
Alessio Gravili
6aa3752b16 feat(richtext-lexical): allow richtext adapters to hook into field hooks 2024-04-23 15:10:35 -04:00
Elliot DeNolf
c483a439bf build: adjust pnpm engines version 2024-04-23 15:01:00 -04:00
Jarrod Flesch
74bdf1c681 chore: reduces graphql dependencies (#5979)
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
2024-04-23 15:00:09 -04:00
James Mikrut
7437d9fe58 Fix/postgres relation names (#5976) 2024-04-23 14:56:43 -04:00
Elliot DeNolf
51f7351962 fix(cpa): install db adapter in package.json (#5921) 2024-04-23 14:03:01 -04:00
Elliot DeNolf
d01fcb921b chore: tsconfig.json back to default 2024-04-23 13:18:45 -04:00
Elliot DeNolf
6179c938bf ci: remove email e2e tests, ethereal calls failing 2024-04-23 13:18:10 -04:00
Elliot DeNolf
dbbcb658a9 fix(deps): proper deps for storage-s3 and storage-vercel-blob (#5975) 2024-04-23 13:17:00 -04:00
James
16f97ad7c3 chore: disables forced pg for tests 2024-04-23 13:13:59 -04:00
James
bc7445ed99 Merge branch 'beta' of github.com:payloadcms/payload into fix/postgres-relation-names 2024-04-23 12:43:32 -04:00
James
e4d024cd0d chore: properly destroys db in postgres 2024-04-23 12:43:25 -04:00
James
1005de8295 fix(db-postgres): shortens relation names 2024-04-23 12:14:01 -04:00
Elliot DeNolf
c79289cedf chore(release): v3.0.0-beta.12 [skip ci] (#5972) 2024-04-23 11:02:08 -04:00
Jarrod Flesch
6a745be036 chore: pass mock req through with validate function to slate richText validation function (#5971) 2024-04-23 10:57:36 -04:00
Elliot DeNolf
cee9cc33ed chore(release): v3.0.0-beta.12 [skip ci] 2024-04-23 10:55:51 -04:00
Elliot DeNolf
9a5e9313cd ci: remove warning for no artifacts found 2024-04-23 10:47:17 -04:00
Elliot DeNolf
5401af5812 chore(storage-*): set disableLocalStorage true for enabled collections (#5970) 2024-04-23 10:46:33 -04:00
Elliot DeNolf
6305a1d1c2 chore: remove NodemailerAdapter type imports 2024-04-23 10:09:35 -04:00
Jarrod Flesch
95b96e3e9e chore: adjust headersWithCors for req without payload (#5963) 2024-04-23 09:50:41 -04:00
Elliot DeNolf
95b3f6d40d chore(scripts): add new packages to getPackageRegistryVersions 2024-04-23 09:10:25 -04:00
Elliot DeNolf
c258a4bef1 chore(scripts): add throttling to release script, optional git commit arg 2024-04-23 09:09:55 -04:00
Elliot DeNolf
647544a0c6 chore: fix build:tests filter [skip ci] 2024-04-23 08:46:15 -04:00
Elliot DeNolf
7e0a2a879c chore: adjust nodemailer type export 2024-04-23 08:39:32 -04:00
Elliot DeNolf
471e1388ae ci: bump pnpm version in gh action, use variable 2024-04-22 22:29:07 -04:00
Elliot DeNolf
1da430b042 ci: bump pnpm version 2024-04-22 22:01:56 -04:00
Elliot DeNolf
56ac06c563 fix: disallow importing from ts extensions 2024-04-22 21:15:13 -04:00
Elliot DeNolf
4dec4bb61c fix: resave media using cloud storage plugin (#5959) 2024-04-22 19:58:57 -04:00
Elliot DeNolf
99a09c49a3 ci: start docker for plugin-cloud-storage e2e 2024-04-22 16:59:57 -04:00
James Mikrut
88fd46bfea fix(db-postgres): row table names were not being built properly (#5960) 2024-04-22 16:55:12 -04:00
Elliot DeNolf
8a6603b3d8 test: add plugin-cloud-storage e2e 2024-04-22 16:43:54 -04:00
PatrikKozak
f6c9f454a5 Merge branch 'beta' of https://github.com/payloadcms/payload into fix/row-table-names 2024-04-22 16:18:24 -04:00
PatrikKozak
d8a5426c37 chore: adds array within row in tabsDoc data 2024-04-22 16:18:14 -04:00
Elliot DeNolf
c9011dcbfd fix(plugin-cloud-storage): resave media 2024-04-22 16:11:19 -04:00
Jarrod Flesch
43089fd13c chore: adds cors headers to routeErrors (#5957) 2024-04-22 15:48:42 -04:00
Elliot DeNolf
bb3bd9c395 chore: adjust email adapter messaging 2024-04-22 15:42:21 -04:00
James
ba423ab424 fix: row table names were not being built properly 2024-04-22 15:10:59 -04:00
Elliot DeNolf
c23984cac3 feat(plugin-cloud-storage): implement storage packages (#5928) 2024-04-22 14:31:20 -04:00
Elliot DeNolf
6685a0fa7e feat!: email adapter (#5901) 2024-04-22 14:26:12 -04:00
Jarrod Flesch
ac4750d016 chore: adds fallbackFileType functionality (#5958) 2024-04-22 14:20:02 -04:00
Elliot DeNolf
951e9fd7f2 test: email e2e updated nodemailer usage 2024-04-22 14:13:38 -04:00
Elliot DeNolf
cbd1554589 chore: adjust email pattern 2024-04-22 13:32:33 -04:00
Jacob Fletcher
80c545933f fix(next): adds CORS headers to API Responses (#5906)
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2024-04-22 12:13:06 -04:00
Paul
594f319fc6 chore!: admin now takes a client side custom property and custom is server only (#5926) 2024-04-22 12:22:32 -03:00
Elliot DeNolf
102feb9576 chore: updates telemetry (#5883) 2024-04-22 09:44:34 -04:00
Simon Vreman
68274d2862 fix(plugin-cloud-storage)!: Pass filename to utility function getting file prefix (#5934) 2024-04-21 07:32:11 -04:00
Dan Ribbens
8945b7a4fa fix(db-postgres): nested groups in nested blocks validation (#5941)
Co-authored-by: Ricardo Domingues <rfdomingues98@gmail.com>
2024-04-21 00:38:55 -04:00
Dan Ribbens
d5ef93b2ba fix(db-postgres): v3 #5938 extra version suffix table names (#5940) 2024-04-20 23:23:06 -04:00
Ritsu
cb0f0dba3a chore: removes comment and unused type import (#5935) 2024-04-20 23:06:43 -04:00
Paul
7b263be01b chore: add missing translations (#5929) 2024-04-20 14:57:22 -04:00
Dan Ribbens
56df60f520 chore: fixes e2e test running on windows (#5927) 2024-04-20 14:54:18 -04:00
Ritsu
d5cbbc472d feat: add count operation to collections (#5930) 2024-04-20 14:45:44 -04:00
Dan Ribbens
d987e5628a feat(live-preview-vue): new live-preview-vue package (#5933)
Co-authored-by: Christian Gil <mrcgam.christian@gmail.com>
2024-04-20 07:52:00 -04:00
Dan Ribbens
1383191f15 fix: v3 update many with drafts (#5900)
Co-authored-by: Elliot DeNolf <denolfe@gmail.com>
2024-04-19 16:32:59 -04:00
Ritsu
27297284cf fix: Passes correct path to import modules on Windows started with file:// (#5919) 2024-04-19 16:28:41 -04:00
Kendell Joseph
3af3a91c87 feat: json field schemas (#5898) 2024-04-19 13:35:59 -04:00
Paul
23c5b71f95 chore(payload,ui)!:update custom config to separate client and server bundles (#5914) 2024-04-19 11:52:55 -03:00
Dan Ribbens
2ee6a8ec3a fix(db-mongodb): ignore end session errors (#5905) 2024-04-19 09:19:55 -04:00
Elliot DeNolf
10819b8693 chore: proper SendMailOptions export 2024-04-18 15:43:10 -04:00
Elliot DeNolf
83c617b452 test: clean up email-nodemailer config 2024-04-18 14:17:36 -04:00
Elliot DeNolf
4acb133655 chore: export SendMailOptions 2024-04-18 14:15:55 -04:00
Elliot DeNolf
6e4135e790 test: add nodemailer adapter to email test config 2024-04-18 13:36:48 -04:00
Elliot DeNolf
f0198b62f3 feat: implement stdout email adapter, use if no adapter configured 2024-04-18 11:59:03 -04:00
Jessica Chowdhury
3ff8063ab8 chore:(i18n): adds translation for document/s key (#5890) 2024-04-18 10:18:06 +01:00
Elliot DeNolf
8d52f1b279 chore: add payload to dev deps 2024-04-18 02:27:43 -04:00
Elliot DeNolf
24072d222c chore: clean up types, remove logMockEmailCredentials 2024-04-18 02:07:54 -04:00
Elliot DeNolf
55c59e71da chore: remove nodemailer from payload completely 2024-04-18 01:44:35 -04:00
Elliot DeNolf
62233788e0 feat(plugin-cloud): use nodemailer adapter 2024-04-18 01:44:20 -04:00
Elliot DeNolf
b297c5499d chore(email): strict true 2024-04-18 00:02:05 -04:00
Elliot DeNolf
fb7925f272 feat: create email-nodemailer package 2024-04-17 21:58:24 -04:00
Patrik
221e873862 chore(translations): adds localsNotSaved_one & localsNotSaved_other translations (#5903) 2024-04-17 16:34:10 -04:00
Patrik
e7143e02e2 fix: adds type error validations for email and password in login operation (#5899) 2024-04-17 16:33:19 -04:00
Elliot DeNolf
a1d68bd951 feat: abstract nodemailer into email adapter interface 2024-04-17 16:10:51 -04:00
Jarrod Flesch
93ee452a2d fix(next): do not require handlers, attempt to read filesystem or throw (#5896) 2024-04-17 15:12:57 -04:00
Jarrod Flesch
1abaa5fc17 chore(next): bump next@^14.3.0-canary.7 (#5894) 2024-04-17 13:07:40 -04:00
Alessio Gravili
999059bc61 fix(richtext-lexical): properly validate block node nested fields, fixes one failing e2e test suite we previously skipped 2024-04-17 11:47:24 -04:00
Alessio Gravili
39ba39c237 feat(richtext-lexical)!: rework how population works and saves data, improve node typing 2024-04-17 11:46:47 -04:00
Jarrod Flesch
009e6c2066 chore(test): fix flakey relationship tests (#5892) 2024-04-17 11:44:07 -04:00
Ritsu
8bf03ae706 fix(next): pass a corrent content-type header in getFile route (#5799)
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2024-04-17 11:40:02 -04:00
Kendell Joseph
a2fe3f66e3 fix: accepts empty cell data in json field (#5876) 2024-04-17 11:07:28 -04:00
Jacob Fletcher
6cd5b253f1 fix(next): admin access control (#5887) 2024-04-17 10:31:39 -04:00
Elliot DeNolf
abf0461d80 ci: add exports pattern to codeowners 2024-04-17 10:24:02 -04:00
Elliot DeNolf
abca45e152 chore: add comments to exports about server vs. front-end 2024-04-17 10:23:21 -04:00
Alessio Gravili
58ea94f6ac feat: pass through doc preferences to field validate function and improve types 2024-04-17 09:27:04 -04:00
Jessica Chowdhury
49cba92fa1 fix(create-payload-app): uses baseUrl for payload config path in tsconfig (#5888) 2024-04-17 13:53:00 +01:00
Elliot DeNolf
42329fc736 ci: cut down on codeowners noise [skip ci] 2024-04-16 22:07:55 -04:00
Elliot DeNolf
68dee49501 feat(cpa): list plugin template after updating for 3.0 2024-04-16 19:46:27 -04:00
Dan Ribbens
234837ee1d fix: postgres query hasMany in (#5884) 2024-04-16 17:09:43 -04:00
Kendell Joseph
0d3554d70a fix: accepts empty cell data 2024-04-16 12:35:03 -04:00
Paul
7f6c6c4787 fix(next): check for matching passwords when creating the first user (#5869) 2024-04-16 12:41:20 -03:00
Jacob Fletcher
b6c975bfdc Revert "fix(plugin-seo): uses correct key for ukrainian translation"
This reverts commit b9a9dad60a.
2024-04-16 11:36:38 -04:00
Elliot DeNolf
eaf5a86121 ci: enforce node version for all jobs 2024-04-16 11:35:00 -04:00
Jacob Fletcher
b9a9dad60a fix(plugin-seo): uses correct key for ukrainian translation 2024-04-16 11:14:44 -04:00
Alessio Gravili
a2afc38894 fix(richtext-lexical): do not allow empty url field in link drawer 2024-04-16 11:03:12 -04:00
Paul
b80c92ba93 fix(next): issue with password and confirm password fields not being type of password (#5870) 2024-04-16 11:52:56 -03:00
Patrik
6669a2cedb fix(next): adds client-side field validations to login and forgot-password views (#5871) 2024-04-16 10:36:37 -04:00
Bohdan Kucheriavyi
7369da3d8d chore(plugin-seo): adds Ukrainian translations (#5836)
Signed-off-by: Bohdan Kucheriavyi <bohdan.kucheriavyi@zapal.tech>
2024-04-16 09:32:08 -04:00
Jarrod Flesch
697a0f1ecf fix: ensure body limit is respected (#5807)
Co-authored-by: James <james@trbl.design>
2024-04-16 09:22:41 -04:00
Jarrod Flesch
3db0557b07 chore: improve cookie helper functions (#5866) 2024-04-15 22:11:17 -04:00
Elliot DeNolf
8178d57ab9 chore(release): v3.0.0-beta.11 [skip ci] 2024-04-15 16:50:51 -04:00
Ritsu
f1b2f767bb fix(db-postgres): validateExistingBlockIsIdentical localized (#5840) 2024-04-15 16:50:28 -04:00
Dan Ribbens
f21b394d21 chore(create-payload-app): db user and password connection URI (#5853) 2024-04-15 16:40:31 -04:00
Dan Ribbens
4e4ccca02a fix(db-mongodb): version fields indexSortableFields (#5864) 2024-04-15 16:26:26 -04:00
Elliot DeNolf
b6578d6447 test(pcs): add prefix test (#5867) 2024-04-15 16:10:51 -04:00
Elliot DeNolf
abeb94a53d docs: add externalFileHeaderFilter 2024-04-15 15:11:35 -04:00
Ritsu
974a74500b fix(ui): passes cellComponentProps through buildColumnState (#5848) 2024-04-15 15:03:16 -04:00
Elliot DeNolf
61dd17ae5e feat: allow configuration for setting headers on external file fetch (#5862) 2024-04-15 15:02:07 -04:00
Jacob Fletcher
2628249a51 fix(next): removes links to hidden entities (#5861) 2024-04-15 14:58:57 -04:00
Elliot DeNolf
5f57782199 fix(db-postgres): properly pass id type for type gen (#5859) 2024-04-15 13:38:46 -04:00
Ritsu
bceb49ee6c fix(ui): ensures titleField is not empty (#5850) 2024-04-15 13:33:49 -03:00
Alessio Gravili
beeb59f263 ci: add weird tune linux network step which seems to reduce flakes (#5855) 2024-04-15 12:23:48 -04:00
Paul
4150c87be0 chore(plugin-nested-docs): update nested docs plugin exports and moved away from default exports (#5856) 2024-04-15 13:22:01 -03:00
Patrik
a394d8211e fix: passes parent id instead of incoming id to saveVersion (#5854) 2024-04-15 12:02:17 -04:00
Paul
6a162776f2 chore: export react toastify from UI (#5828) 2024-04-15 12:53:12 -03:00
James Mikrut
d41bd7b133 chore: exports getFieldsToSign (#5852) 2024-04-15 10:48:03 -04:00
Patrik
f3409fab29 fix(db-mongodb): failing contains query with special chars (#5776) 2024-04-15 10:24:07 -04:00
James Mikrut
dd75fbfee2 feat: image dimensions rework (#5824) 2024-04-15 10:22:40 -04:00
James
ae2c85f947 chore: exports getFieldsToSign 2024-04-15 10:19:55 -04:00
Oladayo Olufemi Fagbemi
c0b454a5de fix(plugin-seo): add default empty endpoints array (#5844) 2024-04-14 17:44:15 -04:00
Alessio Gravili
27754dd0d7 fix(richtext-lexical): ensure schema maps for complex fields / sub-fields are handled correctly (#5842) 2024-04-14 17:29:48 -04:00
Oladayo Olufemi Fagbemi
18ec830882 fix(plugin-seo): incorrect styling of field labels (#5845)
Co-authored-by: Oladayo Fagbemi <oladayo.fagbemi@acelspringer.com>
Co-authored-by: Alessio Gravili <alessio@gravili.de>
2024-04-14 17:28:53 -04:00
Ritsu
1ffc0f552e fix(payload): remove incorrect payload module import within payload (#5847) 2024-04-14 16:49:06 -04:00
Alessio Gravili
07b676ac81 chore(richtext-lexical): adjust field name inside int tests 2024-04-14 16:45:54 -04:00
Alessio Gravili
da79f09544 fix(payload): ensure that the minimum @swc/core peerdep version used is 1.4.13 (#5841) 2024-04-14 16:43:42 -04:00
Alessio Gravili
993b035285 fix(richtext-lexical): ensure schema maps for complex fields / sub-fields are handled correctly for blocks, link and upload features 2024-04-14 02:19:58 -04:00
Alessio Gravili
3f2df643e7 chore(richtext-lexical): add failing e2e test which ensures sub-richtext blocks work as intended 2024-04-14 02:18:16 -04:00
James
42c7649176 chore: removes tempy 2024-04-13 17:06:25 -04:00
Ritsu
2722d2f5ce fix: passes number type limit arg to find on the list view (#5837)
Co-authored-by: Paul <paul@payloadcms.com>
2024-04-13 17:24:46 -03:00
Elliot DeNolf
f71e61d7d9 chore(templates): remove no longer used editor-import comment 2024-04-13 10:27:39 -04:00
Gabriel Novotny
73c76cab77 fix: postgres turbopack static analysis error (#5832) 2024-04-12 18:53:19 -04:00
Elliot DeNolf
43e8a533b7 fix: ensure file persists through form state changes (#5823) 2024-04-12 15:16:47 -04:00
Elliot DeNolf
ea4203bb32 chore(release): v3.0.0-beta.10 [skip ci] 2024-04-12 14:58:12 -04:00
Elliot DeNolf
568b5073c8 chore(deps): sync pnpm-lock.yaml 2024-04-12 14:56:42 -04:00
Elliot DeNolf
471e1f4827 chore: specify next canary peer dep 2024-04-12 14:56:04 -04:00
Elliot DeNolf
b9185a6fcd chore: fix more publish config exports 2024-04-12 14:48:34 -04:00
Elliot DeNolf
80496aa94c fix: remove all exports null (#5830) 2024-04-12 14:35:36 -04:00
Alessio Gravili
5fd6e3c1a8 fix!: upgrade minimum required node version from 18.17.0 to v18.20.2. Some old node versions have issues with our loader (#5829) 2024-04-12 14:01:33 -04:00
Alessio Gravili
54590c1700 fix(richtext-lexical)!: fix output of internal list HTML converter (#5827)
BREAKING: Changes the classnames of the converted HTML
2024-04-12 12:10:44 -04:00
Elliot DeNolf
b1259be8f2 chore(release): v3.0.0-beta.9 [skip ci] 2024-04-12 12:06:41 -04:00
Elliot DeNolf
cd161e4b16 feat!: remove pointer files (#5826) 2024-04-12 11:58:34 -04:00
Alessio Gravili
cb4214fe6e fix(richtext-lexical)!: fix output of internal list HTML converter
BREAKING: Changes the classnames of the converted HTML
2024-04-12 11:58:05 -04:00
Elliot DeNolf
9d42751a42 feat!: remove more pointer files 2024-04-12 11:39:08 -04:00
Elliot DeNolf
c2c637b359 chore: clean up unused files 2024-04-12 11:35:25 -04:00
Paul
2f446e11d6 chore: bump nextjs dependencies to ^14.2 (#5820)
fix(plugin-seo): overriding existing endpoints
2024-04-12 12:32:45 -03:00
Elliot DeNolf
4f566b088c feat!: remove pointer files 2024-04-12 11:31:35 -04:00
Dan Ribbens
0d40d87b31 fix(db-postgres): relationship query pagination (#5803) 2024-04-12 11:18:40 -04:00
Elliot DeNolf
70fcd6bf40 feat: rework image dimensions, use image-size 2024-04-12 11:13:16 -04:00
Jacob Fletcher
94c0095b3b chore(ui): removes all static font assets (#5821) 2024-04-12 09:57:31 -04:00
Elliot DeNolf
4328060637 feat(pcs): vercel blob storage adapter (#5811) 2024-04-12 09:37:35 -04:00
Elliot DeNolf
d98d0fd5bd chore(pcs): use proper getFilePrefix 2024-04-12 09:30:19 -04:00
Elliot DeNolf
5db2863d08 feat(pcs): export utilities 2024-04-12 09:30:10 -04:00
Jacob Fletcher
ff5e438d6d chore(ui): replaces suisse-intl font with system fallbacks 2024-04-12 09:16:43 -04:00
Paul
bfd5f13ee9 chore: add types for local api find/update operations (#5808) 2024-04-12 10:15:57 -03:00
Elliot DeNolf
8043188f36 chore(pcs): update README 2024-04-12 09:12:34 -04:00
Elliot DeNolf
e3e0998772 chore: ignore vercelBlob pointers, adjust peer deps 2024-04-12 00:34:41 -04:00
Elliot DeNolf
86adc6f282 chore: add vercelBlob to exports 2024-04-11 23:05:22 -04:00
Elliot DeNolf
b51b519d30 feat(plugin-cloud-storage): vercel blob storage adapter 2024-04-11 22:58:55 -04:00
Alessio Gravili
c70dcb6a59 feat(richtext-lexical): add HorizontalRuleFeature, improve block handle positioning (#5806) 2024-04-11 16:51:54 -04:00
Alessio Gravili
2486c7dba0 fix(richtext-lexical): incorrect margin for nested unordered lists 2024-04-11 16:42:35 -04:00
Paul
1456fcdcad chore: type locale from localization config on the payload request (#5801) 2024-04-11 17:38:35 -03:00
Alessio Gravili
a216800c72 chore(richtext-lexical): fix build error 2024-04-11 16:31:40 -04:00
Alessio Gravili
c3d8597c13 feat(richtext-lexical): add HorizontalRuleFeature 2024-04-11 16:24:04 -04:00
Alessio Gravili
844663ce1a fix(richtext-lexical): limit unnecessary floating handle positioning updates 2024-04-11 15:55:55 -04:00
Alessio Gravili
055e6af7b7 feat(richtext-lexical): improve floating handle y-positioning by positioning it in the center for smaller elements. 2024-04-11 15:55:43 -04:00
Alessio Gravili
479e6ecddc fix(richtext-lexical): incorrect floating handle y-position calculation next to certain kinds of HTML elements like HR 2024-04-11 15:55:26 -04:00
Jacob Fletcher
b9456e8244 fix(next): safely handles missing json body in post requests (#5797) 2024-04-11 15:53:50 -04:00
Jacob Fletcher
432dfef435 chore(next): installs merriweather as google font and removes static assets 2024-04-11 15:31:19 -04:00
Jacob Fletcher
bcb538aee2 fix(next): awaits logout operation in api route handler 2024-04-11 14:57:35 -04:00
Jacob Fletcher
01e8f8c649 Merge branch 'beta' into fix/post-body-parse 2024-04-11 14:15:56 -04:00
Jacob Fletcher
330e4a7724 fix(next): safely handles missing json body in post requests 2024-04-11 13:42:21 -04:00
2851 changed files with 189163 additions and 138438 deletions

View File

@@ -11,3 +11,4 @@
playwright.config.ts
jest.config.js
test/live-preview/next-app
tsconfig.tsbuildinfo

View File

@@ -8,6 +8,8 @@ module.exports = {
plugins: ['payload'],
rules: {
'payload/no-jsx-import-statements': 'warn',
'payload/no-relative-monorepo-imports': 'error',
'payload/no-imports-from-exports-dir': 'error',
},
},
{
@@ -29,6 +31,12 @@ module.exports = {
'perfectionist/sort-objects': 'off',
},
},
{
files: ['templates/vercel-postgres/**'],
rules: {
'no-restricted-exports': 'off',
},
},
{
files: ['package.json', 'tsconfig.json'],
rules: {

45
.github/CODEOWNERS vendored
View File

@@ -1,41 +1,30 @@
# Order matters. The last matching pattern takes precedence.
### Core ###
/packages/payload/src/uploads/ @denolfe
/packages/payload/src/admin/ @jmikrut @jacobsfletch @JarrodMFlesch
### Package Exports ###
/**/exports/ @denolfe @jmikrut
### Adapters ###
/packages/db-*/ @denolfe @jmikrut @DanRibbens
/packages/richtext-*/ @denolfe @jmikrut @DanRibbens @AlessioGr
### Plugins ###
/packages/plugin-*/ @denolfe @jmikrut @DanRibbens
### Packages ###
/packages/richtext-*/ @AlessioGr
/packages/plugin-cloud*/ @denolfe
/packages/plugin-form-builder/ @jacobsfletch
/packages/plugin-live-preview*/ @jacobsfletch
/packages/plugin-nested-docs/ @jacobsfletch
/packages/plugin-redirects/ @jacobsfletch
/packages/plugin-search/ @jacobsfletch
/packages/plugin-sentry/ @JessChowdhury
/packages/plugin-seo/ @jacobsfletch
/packages/plugin-stripe/ @jacobsfletch
### Examples ###
/examples/ @jacobsfletch
/examples/testing/ @JarrodMFlesch
/examples/email/ @JessChowdhury
/examples/whitelabel/ @JessChowdhury
/packages/email-*/ @denolfe
/packages/storage-*/ @denolfe
/packages/create-payload-app/ @denolfe
/packages/eslint-*/ @denolfe
### Templates ###
/templates/ @jacobsfletch @denolfe
### Misc ###
/packages/create-payload-app/ @denolfe
/packages/eslint-config-payload/ @denolfe
/packages/payload-admin-bar/ @jacobsfletch
### Build Files ###
/**/package.json @denolfe
/tsconfig.json @denolfe
/**/tsconfig*.json @denolfe
/jest.config.js @denolfe
/**/jest.config.js @denolfe
### Root ###
/package.json @denolfe
/scripts/ @denolfe
/.husky/ @denolfe
/.vscode/ @denolfe
/.github/ @denolfe
/.github/CODEOWNERS @denolfe

48
.github/actions/setup/action.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: Setup node and pnpm
description: Configure the Node.js and pnpm versions
inputs:
node-version:
description: 'The Node.js version to use'
required: true
default: 18.20.2
pnpm-version:
description: 'The pnpm version to use'
required: true
default: 8.15.7
runs:
using: composite
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
shell: bash
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ inputs.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: ${{ inputs.pnpm-version }}
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
pnpm-store-
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- shell: bash
run: pnpm install

50
.github/workflows/label-author.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: label-author
on:
pull_request:
types: [opened]
issues:
types: [opened]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
debug-context:
runs-on: ubuntu-latest
steps:
- name: View context attributes
uses: actions/github-script@v7
with:
script: console.log(context)
label-created-by:
name: Label pr/issue on opening
runs-on: ubuntu-latest
steps:
- name: Tag with 'created-by'
uses: actions/github-script@v7
if: github.event.action == 'opened'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const type = context.payload.pull_request ? 'pull_request' : 'issue';
const association = context.payload[type].author_association;
let label = ''
if (association === 'MEMBER' || association === 'OWNER') {
label = 'created-by: Payload team';
} else if (association === 'CONTRIBUTOR') {
label = 'created-by: Contributor';
}
if (!label) return;
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: [label],
});
console.log('Added created-by: Payload team label');

View File

@@ -2,14 +2,26 @@ name: build
on:
pull_request:
types: [opened, reopened, synchronize]
types:
- opened
- reopened
- synchronize
push:
branches: ['main', 'beta']
branches:
- main
- beta
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
# <workflow_name>-<branch_name>-<true || commit_sha if branch is protected>
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.ref_protected && github.sha || ''}}
cancel-in-progress: true
env:
NODE_VERSION: 18.20.2
PNPM_VERSION: 8.15.7
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
jobs:
changes:
runs-on: ubuntu-latest
@@ -19,6 +31,10 @@ jobs:
needs_build: ${{ steps.filter.outputs.needs_build }}
templates: ${{ steps.filter.outputs.templates }}
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- uses: actions/checkout@v4
with:
fetch-depth: 25
@@ -39,6 +55,51 @@ jobs:
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
echo "templates: ${{ steps.filter.outputs.templates }}"
lint:
if: >
github.event_name == 'pull_request' && !contains(github.event.pull_request.title, 'no-lint') && !contains(github.event.pull_request.title, 'skip-lint')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- name: Setup pnpm cache
uses: actions/cache@v4
timeout-minutes: 720
with:
path: ${{ env.STORE_PATH }}
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
pnpm-store-
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- run: pnpm install
- name: Lint staged
run: |
git diff --name-only --diff-filter=d origin/${GITHUB_BASE_REF}...${GITHUB_SHA}
npx lint-staged --diff="origin/${GITHUB_BASE_REF}...${GITHUB_SHA}"
build:
needs: changes
if: ${{ needs.changes.outputs.needs_build == 'true' }}
@@ -49,15 +110,19 @@ jobs:
with:
fetch-depth: 25
- name: Use Node.js 18
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: 18
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Get pnpm store directory
@@ -77,6 +142,8 @@ jobs:
- run: pnpm install
- run: pnpm run build:all
env:
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
- name: Cache build
uses: actions/cache@v4
@@ -90,15 +157,19 @@ jobs:
needs: build
steps:
- name: Use Node.js 18
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: 18
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Restore build
@@ -122,9 +193,9 @@ jobs:
database:
- mongodb
- postgres
# - postgres-custom-schema
# - postgres-uuid
# - supabase
- postgres-custom-schema
- postgres-uuid
- supabase
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
@@ -135,15 +206,19 @@ jobs:
AWS_REGION: us-east-1
steps:
- name: Use Node.js 18
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: 18
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Restore build
@@ -215,34 +290,49 @@ jobs:
suite:
- _community
- access-control
- admin
- admin__e2e__1
- admin__e2e__2
- auth
- email
- field-error-states
- fields-relationship
- fields
- fields__collections__Blocks
- fields__collections__Array
- fields__collections__Relationship
- fields__collections__Lexical
- fields__collections__RichText
- fields__collections__Lexical__e2e__main
- fields__collections__Lexical__e2e__blocks
- fields__collections__Date
- fields__collections__Number
- fields__collections__Point
- fields__collections__Tabs
- fields__collections__Text
- fields__collections__Upload
- live-preview
- localization
- i18n
- plugin-cloud-storage
- plugin-form-builder
- plugin-nested-docs
- plugin-seo
- versions
- uploads
env:
SUITE_NAME: ${{ matrix.suite }}
steps:
- name: Use Node.js 18
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: 18
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Restore build
@@ -252,34 +342,115 @@ jobs:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Install Playwright
run: pnpm exec playwright install --with-deps
- name: Start LocalStack
run: pnpm docker:start
if: ${{ matrix.suite == 'plugin-cloud-storage' }}
- name: Store Playwright's Version
run: |
# Extract the version number using a more targeted regex pattern with awk
PLAYWRIGHT_VERSION=$(pnpm ls @playwright/test --depth=0 | awk '/@playwright\/test/ {print $2}')
echo "Playwright's Version: $PLAYWRIGHT_VERSION"
echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV
- name: Cache Playwright Browsers for Playwright's Version
id: cache-playwright-browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-browsers-${{ env.PLAYWRIGHT_VERSION }}
- name: Setup Playwright - Browsers and Dependencies
if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
run: pnpm exec playwright install --with-deps chromium
- name: Setup Playwright - Dependencies-only
if: steps.cache-playwright-browsers.outputs.cache-hit == 'true'
run: pnpm exec playwright install-deps chromium
- name: E2E Tests
run: pnpm test:e2e ${{ matrix.suite }}
run: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e ${{ matrix.suite }}
env:
PLAYWRIGHT_JSON_OUTPUT_NAME: results_${{ matrix.suite }}.json
NEXT_TELEMETRY_DISABLED: 1
- uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-${{ matrix.suite }}
path: test/test-results/
if-no-files-found: ignore
retention-days: 1
tests-type-generation:
if: false # This should be replaced with gen on a real Payload project
# Disabled until this is fixed: https://github.com/daun/playwright-report-summary/issues/156
# - uses: daun/playwright-report-summary@v3
# with:
# report-file: results_${{ matrix.suite }}.json
# report-tag: ${{ matrix.suite }}
# job-summary: true
app-build-with-packed:
runs-on: ubuntu-latest
needs: build
steps:
- name: Use Node.js 18
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: 18
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: 8
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Restore build
uses: actions/cache@v4
timeout-minutes: 10
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.10.0
with:
mongodb-version: 6.0
- name: Pack and build app
run: |
set -ex
pnpm run script:pack --dest templates/blank-3.0
cd templates/blank-3.0
cp .env.example .env
ls -la
pnpm add ./*.tgz --ignore-workspace
pnpm install --ignore-workspace --no-frozen-lockfile
cat package.json
pnpm run build
tests-type-generation:
runs-on: ubuntu-latest
needs: build
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Restore build
@@ -308,11 +479,14 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 25
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Use Node.js 18
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: 18
node-version: ${{ env.NODE_VERSION }}
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.10.0
@@ -326,3 +500,48 @@ jobs:
yarn install
yarn build
yarn generate:types
generated-templates:
needs: build
runs-on: ubuntu-latest
steps:
# https://github.com/actions/virtual-environments/issues/1187
- name: tune linux network
run: sudo ethtool -K eth0 tx off rx off
- name: Setup Node@${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Restore build
uses: actions/cache@v4
timeout-minutes: 10
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Build all generated templates
run: pnpm tsx ./scripts/build-generated-templates.ts
all-green:
name: All Green
if: always()
runs-on: ubuntu-latest
needs:
- lint
- build
- tests-unit
- tests-int
- tests-e2e
steps:
- if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
run: exit 1

103
.github/workflows/pr-title.yml vendored Normal file
View File

@@ -0,0 +1,103 @@
name: pr-title
on:
pull_request:
types:
- opened
- edited
- synchronize
permissions:
pull-requests: write
jobs:
main:
name: lint-pr-title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
id: lint_pr_title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
build
chore
ci
docs
feat
fix
perf
refactor
revert
style
test
types
scopes: |
cpa
db-\*
db-mongodb
db-postgres
email-nodemailer
eslint
graphql
live-preview
live-preview-react
next
payload
plugin-cloud
plugin-cloud-storage
plugin-form-builder
plugin-nested-docs
plugin-redirects
plugin-relationship-object-ids
plugin-search
plugin-sentry
plugin-seo
plugin-stripe
richtext-\*
richtext-lexical
richtext-slate
storage-\*
storage-azure
storage-gcs
storage-vercel-blob
storage-s3
translations
ui
templates
examples(\/(\w|-)+)?
deps
# Disallow uppercase letters at the beginning of the subject
subjectPattern: ^(?![A-Z]).+$
subjectPatternError: |
The subject "{subject}" found in the pull request title "{title}"
didn't match the configured pattern. Please ensure that the subject
doesn't start with an uppercase character.
- uses: marocchino/sticky-pull-request-comment@v2
# When the previous steps fails, the workflow would stop. By adding this
# condition you can continue the execution with the populated error message.
if: always() && (steps.lint_pr_title.outputs.error_message != null)
with:
header: pr-title-lint-error
message: |
Pull Request titles must follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and have valid scopes.
${{ steps.lint_pr_title.outputs.error_message }}
```
feat(ui): add Button component
^ ^ ^
| | |__ Subject
| |_______ Scope
|____________ Type
```
# Delete a previous comment when the issue has been resolved
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
uses: marocchino/sticky-pull-request-comment@v2
with:
header: pr-title-lint-error
delete: true

36
.github/workflows/release-canary.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: release-canary
on:
workflow_dispatch:
branches:
- beta
env:
NODE_VERSION: 18.20.2
PNPM_VERSION: 8.15.7
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
jobs:
release:
permissions:
id-token: write
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup
uses: ./.github/actions/setup
with:
node-version: ${{ env.NODE_VERSION }}
pnpm-version: ${{ env.PNPM_VERSION }}
- name: Load npm token
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Canary release script
# dry run hard-coded to true for testing and no npm token provided
run: pnpm tsx ./scripts/publish-canary.ts
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true

27
.idea/payload.iml generated
View File

@@ -47,6 +47,33 @@
<excludeFolder url="file://$MODULE_DIR$/templates" />
<excludeFolder url="file://$MODULE_DIR$/test/.swc" />
<excludeFolder url="file://$MODULE_DIR$/versions" />
<excludeFolder url="file://$MODULE_DIR$/packages/richtext-slate/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/richtext-slate/.turbo" />
<excludeFolder url="file://$MODULE_DIR$/packages/email-nodemailer/.turbo" />
<excludeFolder url="file://$MODULE_DIR$/packages/email-nodemailer/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/email-resend/.turbo" />
<excludeFolder url="file://$MODULE_DIR$/packages/email-resend/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview-vue/.turbo" />
<excludeFolder url="file://$MODULE_DIR$/packages/live-preview-vue/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/payload/.swc" />
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-form-builder/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-relationship-object-ids/.turbo" />
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-relationship-object-ids/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/plugin-stripe/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/storage-azure/.turbo" />
<excludeFolder url="file://$MODULE_DIR$/packages/storage-azure/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/storage-gcs/.turbo" />
<excludeFolder url="file://$MODULE_DIR$/packages/storage-gcs/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/storage-s3/.turbo" />
<excludeFolder url="file://$MODULE_DIR$/packages/storage-s3/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/storage-uploadthing/.turbo" />
<excludeFolder url="file://$MODULE_DIR$/packages/storage-uploadthing/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/storage-vercel-blob/.turbo" />
<excludeFolder url="file://$MODULE_DIR$/packages/storage-vercel-blob/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/translations/.turbo" />
<excludeFolder url="file://$MODULE_DIR$/packages/translations/dist" />
<excludeFolder url="file://$MODULE_DIR$/packages/ui/.swc" />
<excludeFolder url="file://$MODULE_DIR$/packages/ui/.turbo" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

View File

@@ -0,0 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="true" type="JavaScriptTestRunnerJest">
<node-interpreter value="project" />
<node-options value="--experimental-vm-modules --no-deprecation" />
<envs />
<scope-kind value="ALL" />
<method v="2" />
</configuration>
</component>

View File

@@ -1 +1 @@
v18.19.1
v18.20.2

2
.nvmrc
View File

@@ -1 +1 @@
v18.19.1
v18.20.2

View File

@@ -12,3 +12,5 @@
tsconfig.json
packages/payload/*.js
packages/payload/*.d.ts
payload-types.ts
tsconfig.tsbuildinfo

9
.swcrc
View File

@@ -7,6 +7,15 @@
"syntax": "typescript",
"tsx": true,
"dts": true
},
"transform": {
"react": {
"runtime": "automatic",
"pragmaFrag": "React.Fragment",
"throwIfNamespace": true,
"development": false,
"useBuiltins": true
}
}
},
"module": {

37
.vscode/launch.json vendored
View File

@@ -16,6 +16,14 @@
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js storage-uploadthing",
"cwd": "${workspaceFolder}",
"name": "Uploadthing",
"request": "launch",
"type": "node-terminal",
"envFile": "${workspaceFolder}/test/storage-uploadthing/.env"
},
{
"command": "node --no-deprecation test/dev.js live-preview",
"cwd": "${workspaceFolder}",
@@ -41,6 +49,13 @@
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js auth",
"cwd": "${workspaceFolder}",
"name": "Run Dev Auth",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev plugin-cloud-storage",
"cwd": "${workspaceFolder}",
@@ -51,6 +66,13 @@
"PAYLOAD_PUBLIC_CLOUD_STORAGE_ADAPTER": "s3"
}
},
{
"command": "node --no-deprecation test/dev.js collections-graphql",
"cwd": "${workspaceFolder}",
"name": "Run Dev GraphQL",
"request": "launch",
"type": "node-terminal"
},
{
"command": "node --no-deprecation test/dev.js fields",
"cwd": "${workspaceFolder}",
@@ -69,35 +91,32 @@
}
},
{
"command": "pnpm run dev versions",
"command": "node --no-deprecation test/dev.js versions",
"cwd": "${workspaceFolder}",
"name": "Run Dev Versions",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev localization",
"command": "node --no-deprecation test/dev.js localization",
"cwd": "${workspaceFolder}",
"name": "Run Dev Localization",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev uploads",
"command": "node --no-deprecation test/dev.js uploads",
"cwd": "${workspaceFolder}",
"name": "Run Dev Uploads",
"request": "launch",
"type": "node-terminal"
},
{
"command": "PAYLOAD_BUNDLER=vite pnpm run dev fields",
"command": "node --no-deprecation test/dev.js field-error-states",
"cwd": "${workspaceFolder}",
"name": "Run Dev Fields (Vite)",
"name": "Run Dev Field Error States",
"request": "launch",
"type": "node-terminal",
"env": {
"NODE_ENV": "production"
}
"type": "node-terminal"
},
{
"command": "pnpm run test:int live-preview",

View File

@@ -40,5 +40,10 @@
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"files.insertFinalNewline": true,
"jestrunner.jestCommand": "pnpm exec cross-env NODE_OPTIONS=\"--experimental-vm-modules --no-deprecation\" node 'node_modules/jest/bin/jest.js'",
"jestrunner.debugOptions": {
"runtimeArgs": ["--experimental-vm-modules", "--no-deprecation"]
}
}

View File

@@ -1,9 +1,10 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
export const GET = REST_GET(config)
export const POST = REST_POST(config)
export const DELETE = REST_DELETE(config)
export const PATCH = REST_PATCH(config)
export const OPTIONS = REST_OPTIONS(config)

View File

@@ -1,39 +0,0 @@
module.exports = {
// gitRawCommitsOpts: {
// from: 'v2.0.9',
// path: 'packages/payload',
// },
// infile: 'CHANGELOG.md',
options: {
preset: {
name: 'conventionalcommits',
types: [
{ section: 'Features', type: 'feat' },
{ section: 'Features', type: 'feature' },
{ section: 'Bug Fixes', type: 'fix' },
{ section: 'Documentation', type: 'docs' },
],
},
},
// outfile: 'NEW.md',
writerOpts: {
commitGroupsSort: (a, b) => {
const groupOrder = ['Features', 'Bug Fixes', 'Documentation']
return groupOrder.indexOf(a.title) - groupOrder.indexOf(b.title)
},
// Scoped commits at the end, alphabetical sort
commitsSort: (a, b) => {
if (a.scope || b.scope) {
if (!a.scope) return -1
if (!b.scope) return 1
return a.scope === b.scope
? a.subject.localeCompare(b.subject)
: a.scope.localeCompare(b.scope)
}
// Alphabetical sort
return a.subject.localeCompare(b.subject)
},
},
}

View File

@@ -8,8 +8,6 @@ keywords: globals, access control, permissions, documentation, Content Managemen
You can define Global-level Access Control within each Global's `access` property. All Access Control functions accept one `args` argument.
\*\*Available argument properties:
## Available Controls
| Function | Allows/Denies Access |

View File

@@ -19,7 +19,7 @@ Access control within Payload is extremely powerful while remaining easy and int
- Restricting a `User` to only be able to see their own `Order`(s), but no others
- Allowing `User`s that belong to a certain `Organization` to access only that `Organization`'s `Resource`s
### Default Settings
## Default Settings
**By default, all Collections and Globals require that a user is logged in to be able to interact in any way.** The default Access Control function evaluates the `user` from the Express `req` and returns `true` if a user is logged in, and `false` if not.
@@ -43,7 +43,7 @@ const defaultPayloadAccess = ({ req: { user } }) => {
to <strong>false</strong>.
</Banner>
### Access Control Types
## Access Control Types
You can manage access within Payload on three different levels:
@@ -51,7 +51,7 @@ You can manage access within Payload on three different levels:
- [Fields](/docs/access-control/fields)
- [Globals](/docs/access-control/globals)
### When Access Control is Executed
## When Access Control is Executed
<Banner type="success">
<strong>Note:</strong>
@@ -60,17 +60,17 @@ You can manage access within Payload on three different levels:
your access control is executed.
</Banner>
#### As you execute operations
### As you execute operations
When you perform Payload operations like `create`, `read`, `update`, and `delete`, your access control functions will be executed before any changes or operations are completed.
#### Within the Admin UI
### Within the Admin UI
The Payload Admin UI responds dynamically to the access control that you define. For example, if you restrict editing a `ExampleCollection` to only users that feature a `role` of `admin`, the Payload Admin UI will **hide** the `ExampleCollection` from the Admin UI entirely. This is super powerful and allows you to control who can do what with your Admin UI.
To accomplish this, Payload ships with an `Access` operation, which is executed when a user logs into the Admin UI. Payload will execute each one of your access control functions, across all collections, globals, and fields, at the top level and return a response that contains a reflection of what the currently authenticated user can do with your application.
### Argument Availability
## Argument Availability
<Banner type="warning">
<strong>Important:</strong>

View File

@@ -7,7 +7,7 @@ desc: Bundlers are used to bundle the code that serves Payload's Admin Panel.
Payload has two official bundlers, the [Webpack Bundler](/docs/admin/webpack) and the [Vite Bundler](/docs/admin/vite). You must install a bundler to use the admin panel.
##### Install a bundler
## Install a bundler
Webpack (recommended):
@@ -21,7 +21,7 @@ Vite (beta):
yarn add @payloadcms/bundler-vite
```
##### Configure the bundler
## Configure the bundler
```ts
// payload.config.ts
@@ -39,7 +39,7 @@ export default buildConfig({
})
```
### What are bundlers?
## What are bundlers?
At their core, a bundler's main goal is to take a bunch of files and turn them into a few optimized files that you ship to the browser. The admin UI has a root `index.html` entry point, and from there the bundler traverses the dependency tree, bundling all of the files that are required from that point on.

View File

@@ -17,12 +17,12 @@ To swap in your own React component, first, consult the list of available compon
normally accepts.
</Banner>
### Base Component Overrides
## Base Component Overrides
You can override a set of admin panel-wide components by providing a component to your base Payload config's `admin.components` property. The following options are available:
| Path | Description |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`Nav`** | Contains the sidebar / mobile menu in its entirety. |
| **`BeforeNavLinks`** | Array of components to inject into the built-in Nav, _before_ the links themselves. |
| **`AfterNavLinks`** | Array of components to inject into the built-in Nav, _after_ the links. |
@@ -73,7 +73,7 @@ export default buildConfig({
})
```
#### Views
### Views
You can easily swap entire views with your own by using the `admin.components.views` property. At the root level, Payload renders the following views by default, all of which can be overridden:
@@ -111,7 +111,7 @@ For more granular control, pass a configuration object instead. Each view corres
_\* An asterisk denotes that a property is required._
#### Adding new views
### Adding new views
To add a _new_ view to the Admin Panel, simply add another key to the `views` object with at least a `path` and `Component` property. For example:
@@ -145,7 +145,7 @@ _For more examples regarding how to customize components, look at the following
For help on how to build your own custom view components, see [building a custom view component](#building-a-custom-view-component).
### Collections
## Collections
You can override components on a collection-by-collection basis via the `admin.components` property.
@@ -221,7 +221,7 @@ export const MyCollection: SanitizedCollectionConfig = {
}
```
#### Collection views
### Collection views
To swap out entire views on collections, you can use the `admin.components.views` property on the collection's config. Payload renders the following views by default, all of which can be overridden:
@@ -297,7 +297,7 @@ Here's an example of how you can customize nested views within the `Edit` view i
You can also add _new_ tabs to the `Edit` view by adding another key to the `components.views.Edit[key]` object with a `path` and `Component` property. See [Custom Tabs](#custom-tabs) for more information.
### Globals
## Globals
As with Collections, you can override components on a global-by-global basis via the `admin.components` property.
@@ -309,7 +309,7 @@ As with Collections, you can override components on a global-by-global basis via
| **`elements.PreviewButton`** | Replace the default `Preview` button with a custom component. |
| **`views`** | Override or create new views within the Payload Admin UI. [More](#global-views) |
#### Global views
### Global views
To swap out views for globals, you can use the `admin.components.views` property on the global's config. Payload renders the following views by default, all of which can be overridden:
@@ -378,7 +378,7 @@ Here's how you can customize nested views within the `Edit` view in Globals, inc
You can also add _new_ tabs to the `Edit` view by adding another key to the `components.views.Edit[key]` object with a `path` and `Component` property. See [Custom Tabs](#custom-tabs) for more information.
### Custom Tabs
## Custom Tabs
You can easily swap individual collection or global edit views. To do this, pass an _object_ to the `admin.components.views.Edit` property of the config. Payload renders the following views by default, all of which can be overridden:
@@ -445,7 +445,7 @@ export const MyCollection: SanitizedCollectionConfig = {
}
```
### Building a custom view component
## Building a custom view component
Your custom view components will be given all the props that a React Router `<Route />` typically would receive, as well as two props from Payload:
@@ -470,7 +470,7 @@ You can find examples of custom views in the [Payload source code `/test/admin/c
To see how to pass in your custom views to create custom views of your own, take a look at the `admin.components.views` property of the [Payload test admin config](https://github.com/payloadcms/payload/blob/main/test/admin/config.ts).
### Fields
## Fields
All Payload fields support the ability to swap in your own React components. So, for example, instead of rendering a default Text input, you might need to render a color picker that provides the editor with a custom color picker interface to restrict the data entered to colors only.
@@ -531,7 +531,7 @@ const CustomCell: React.FC<Props> = (props) => {
When writing your own custom components you can make use of a number of hooks to set data, get reactive changes to other fields, get the id of the document or interact with a context from a custom provider.
### Sending and receiving values from the form
## Sending and receiving values from the form
When swapping out the `Field` component, you'll be responsible for sending and receiving the field's `value` from the form itself. To do so, import the `useField` hook as follows:
@@ -565,10 +565,11 @@ These are the props that will be passed to your custom Label.
#### Example
```tsx
'use client'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { getTranslation } from '@payloadcms/translations'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
import { getTranslation } from 'payload/utilities/getTranslation'
type Props = {
htmlFor?: string
@@ -668,7 +669,7 @@ As your admin customizations gets more complex you may want to share state betwe
component for the admin UI to show
</Banner>
### Styling Custom Components
## Styling Custom Components
Payload exports its SCSS variables and mixins for reuse in your own custom components. This is helpful in cases where you might want to style a custom input similarly to Payload's built-ini styling, so it blends more thoroughly into the existing admin UI.
@@ -678,23 +679,23 @@ To make use of Payload SCSS variables / mixins to use directly in your own compo
@import '~payload/scss';
```
### Getting the current language
## Getting the current language
When developing custom components you can support multiple languages to be consistent with Payload's i18n support. The best way to do this is to add your translation resources to the [i18n configuration](https://payloadcms.com/docs/configuration/i18n) and import `useTranslation` from `react-i18next` in your components.
When developing custom components you can support multiple languages to be consistent with Payload's i18n support. The best way to do this is to add your translation resources to the [i18n configuration](https://payloadcms.com/docs/configuration/i18n) and import `useTranslation` from `@payloadcms/ui/providers/Translation` in your components.
For example:
```tsx
import { useTranslation } from 'react-i18next'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
const CustomComponent: React.FC = () => {
// highlight-start
const { t, i18n } = useTranslation('namespace1')
const { t, i18n } = useTranslation()
// highlight-end
return (
<ul>
<li>{t('key', { variable: 'value' })}</li>
<li>{t('namespace1:key', { variable: 'value' })}</li>
<li>{t('namespace2:key', { variable: 'value' })}</li>
<li>{i18n.language}</li>
</ul>
@@ -702,7 +703,7 @@ const CustomComponent: React.FC = () => {
}
```
### Getting the current locale
## Getting the current locale
In any custom component you can get the selected locale with `useLocale` hook. `useLocale` returns the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:

View File

@@ -6,7 +6,7 @@ desc: Customize your Payload admin panel further by adding your own CSS or SCSS
keywords: admin, css, scss, documentation, Content Management System, cms, headless, javascript, node, react, express
---
### Adding your own CSS / SCSS
## Adding your own CSS / SCSS
You can add your own CSS by providing your base Payload config with a path to your own CSS or SCSS. Customize the styling of any part of the Payload dashboard as necessary.
@@ -25,7 +25,7 @@ const config = buildConfig({
})
```
### Overriding built-in styles
## Overriding built-in styles
To make it as easy as possible for you to override our styles, Payload uses [BEM naming conventions](http://getbem.com/) for all CSS within the Admin UI. If you provide your own CSS, you can override any built-in styles easily.
@@ -41,7 +41,7 @@ You can find the built-in Payload CSS variables within [`./src/admin/scss/app.sc
- Fonts
- Horizontal gutter
#### Dark mode
### Dark mode
<Banner type="warning">
If you're overriding colors or theme elevations, make sure to consider how your changes will

View File

@@ -10,7 +10,7 @@ It's common for your config to rely on server only modules to perform logic in a
Any file that imports a server-only module such as `fs`, `stripe`, `authorizenet`, `nodemailer`, etc. **cannot** be included in the browser bundle.
#### Example Scenario
## Example Scenario
Say we have a collection called `Subscriptions` that has a `beforeChange` hook that creates a Stripe subscription whenever a Subscription document is created in Payload.
@@ -79,7 +79,7 @@ export const createStripeSubscription = async ({ data, operation }) => {
**As-is, this collection will prevent your Admin panel from bundling or loading correctly, because Stripe relies on some Node-only packages.**
#### How to fix this
## How to exclude server-only code
You need to make sure that you use `alias`es to tell your bundler to import "safe" files vs. attempting to import any server-side code that you need to get rid of. Depending on your bundler (Webpack, Vite, etc.) the steps involved may be slightly different.

View File

@@ -8,7 +8,7 @@ keywords: admin, components, custom, documentation, Content Management System, c
Payload provides a variety of powerful hooks that can be used within your own React components. With them, you can interface with Payload itself and build just about any type of complex customization you can think of—directly in familiar React code.
### useField
## useField
The `useField` hook is used internally within every applicable Payload field component, and it manages sending and receiving a field's state from its parent form.
@@ -52,7 +52,7 @@ const {
// The rest of your component goes here
```
### useFormFields
## useFormFields
There are times when a custom field component needs to have access to data from other fields, and you have a few options to do so. The `useFormFields` hook is a powerful and highly performant way to retrieve a form's field state, as well as to retrieve the `dispatchFields` method, which can be helpful for setting other fields' form states from anywhere within a form.
@@ -81,7 +81,7 @@ const MyComponent: React.FC = () => {
}
```
### useAllFormFields
## useAllFormFields
**To retrieve more than one field**, you can use the `useAllFormFields` hook. Your component will re-render when _any_ field changes, so use this hook only if you absolutely need to. Unlike the `useFormFields` hook, this hook does not accept a "selector", and it always returns an array with type of `[fields: Fields, dispatch: React.Dispatch<Action>]]`.
@@ -109,7 +109,7 @@ const ExampleComponent: React.FC = () => {
};
```
##### Updating other fields' values
#### Updating other fields' values
If you are building a custom component, then you should use `setValue` which is returned from the `useField` hook to programmatically set your field's value. But if you're looking to update _another_ field's value, you can use `dispatchFields` returned from `useFormFields`.
@@ -128,7 +128,7 @@ You can send the following actions to the `dispatchFields` function.
To see types for each action supported within the `dispatchFields` hook, check out the Form types [here](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/components/forms/Form/types.ts).
### useForm
## useForm
The `useForm` hook can be used to interact with the form itself, and sends back many methods that can be used to reactively fetch form state without causing rerenders within your components each time a field is changed. This is useful if you have action-based callbacks that your components fire, and need to interact with form state _based on a user action_.
@@ -635,7 +635,7 @@ export const CustomArrayManager = () => {
]}
/>
### useCollapsible
## useCollapsible
The `useCollapsible` hook allows you to control parent collapsibles:
@@ -667,7 +667,7 @@ const CustomComponent: React.FC = () => {
}
```
### useDocumentInfo
## useDocumentInfo
The `useDocumentInfo` hook provides lots of information about the document currently being edited, including the following:
@@ -707,7 +707,7 @@ const LinkFromCategoryToPosts: React.FC = () => {
}
```
### useLocale
## useLocale
In any custom component you can get the selected locale object with the `useLocale` hook. `useLocale`gives you the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:
@@ -728,7 +728,7 @@ const Greeting: React.FC = () => {
}
```
### useAuth
## useAuth
Useful to retrieve info about the currently logged in user as well as methods for interacting with it. It sends back an object with the following properties:
@@ -755,7 +755,7 @@ const Greeting: React.FC = () => {
}
```
### useConfig
## useConfig
Used to easily fetch the full Payload config.
@@ -771,7 +771,7 @@ const MyComponent: React.FC = () => {
}
```
### useEditDepth
## useEditDepth
Sends back how many editing levels "deep" the current component is. Edit depth is relevant while adding new documents / editing documents in modal windows and other cases.
@@ -787,11 +787,11 @@ const MyComponent: React.FC = () => {
}
```
### usePreferences
## usePreferences
Returns methods to set and get user preferences. More info can be found [here](https://payloadcms.com/docs/admin/preferences).
### useTheme
## useTheme
Returns the currently selected theme (`light`, `dark` or `auto`), a set function to update it and a boolean `autoMode`, used to determine if the theme value should be set automatically based on the user's device preferences.
@@ -819,7 +819,7 @@ const MyComponent: React.FC = () => {
}
```
### useTableColumns
## useTableColumns
Returns methods to manipulate table columns
@@ -843,7 +843,7 @@ const MyComponent: React.FC = () => {
}
```
### useDocumentEvents
## useDocumentEvents
The `useDocumentEvents` hook provides a way of subscribing to cross-document events, such as updates made to nested documents within a drawer. This hook will report document events that are outside the scope of the document currently being edited. This hook provides the following:

View File

@@ -33,7 +33,7 @@ All options for the Admin panel are defined in your base Payload config file.
| `bundler` | The bundler that you would like to use to bundle the admin panel. Officially supported bundlers: [Webpack](/docs/admin/webpack) and [Vite](/docs/admin/vite). |
| `user` | The `slug` of a Collection that you want be used to log in to the Admin dashboard. [More](/docs/admin/overview#the-admin-user-collection) |
| `buildPath` | Specify an absolute path for where to store the built Admin panel bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
| `meta` | Base meta data to use for the Admin panel. Included properties are `titleSuffix`, `ogImage`, and `favicon`. |
| `meta` | Base meta data to use for the Admin panel. Included properties are `titleSuffix`, `icons`, and `openGraph`. Can be overridden on a per collection or per global basis. |
| `disable` | If set to `true`, the entire Admin panel will be disabled. |
| `indexHTML` | Optionally replace the entirety of the `index.html` file used by the Admin panel. Reference the [base index.html file](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/index.html) to ensure your replacement has the appropriate HTML elements. |
| `css` | Absolute path to a stylesheet that you can use to override / customize the Admin panel styling. [More](/docs/admin/customizing-css). |
@@ -45,8 +45,7 @@ All options for the Admin panel are defined in your base Payload config file.
| `components` | Component overrides that affect the entirety of the Admin panel. [More](/docs/admin/components) |
| `webpack` | Customize the Webpack config that's used to generate the Admin panel. [More](/docs/admin/webpack) |
| `vite` | Customize the Vite config that's used to generate the Admin panel. [More](/docs/admin/vite) |
| `logoutRoute` | The route for the `logout` page. |
| `inactivityRoute` | The route for the `logout` inactivity page. |
| `routes` | Replace built-in Admin Panel routes with your own custom routes. I.e. `{ logout: '/custom-logout', inactivity: 'custom-inactivity' }` |
### The Admin User Collection

View File

@@ -21,7 +21,7 @@ Out of the box, Payload handles the persistence of your users' preferences in a
that is reading or setting a preference via all provided authentication methods.
</Banner>
### Use cases
## Use cases
This API is used significantly for internal operations of the Admin panel, as mentioned above. But, if you're building your own React components for use in the Admin panel, you can allow users to set their own preferences in correspondence to their usage of your components. For example:
@@ -30,7 +30,7 @@ This API is used significantly for internal operations of the Admin panel, as me
- You might want to store `recentlyAccessed` documents to give admin editors an easy shortcut back to their recently accessed documents on the `Dashboard` or similar
- Many other use cases exist. Invent your own! Give your editors an intelligent and persistent editing experience.
### Database
## Database
Payload automatically creates an internally used `payload-preferences` collection that stores user preferences. Each document in the `payload-preferences` collection contains the following shape:
@@ -44,15 +44,15 @@ Payload automatically creates an internally used `payload-preferences` collectio
| `createdAt` | A timestamp of when the preference was created. |
| `updatedAt` | A timestamp set to the last time the preference was updated. |
### APIs
## APIs
Preferences are available to both [GraphQL](/docs/graphql/overview#preferences) and [REST](/docs/rest-api/overview#) APIs.
### Adding or reading Preferences in your own components
## Adding or reading Preferences in your own components
The Payload admin panel offers a `usePreferences` hook. The hook is only meant for use within the admin panel itself. It provides you with two methods:
##### `getPreference`
#### `getPreference`
This async method provides an easy way to retrieve a user's preferences by `key`. It will return a promise containing the resulting preference value.
@@ -60,7 +60,7 @@ This async method provides an easy way to retrieve a user's preferences by `key`
- `key`: the `key` of your preference to retrieve.
##### `setPreference`
#### `setPreference`
Also async, this method provides you with an easy way to set a user preference. It returns `void`.

View File

@@ -116,7 +116,7 @@ That plugin should create an alias to support Vite as follows:
This will effectively alias the entire plugin and work with Vite. If the plugin requires admin-specific code, then the `./my-admin-plugin.js` alias target file should reflect any changes necessary to the admin UI that the main server-side plugin performs.
### Extending the Vite config
## Extending the Vite config
The Payload config supports a new property for plugins to be able to extend the Vite config specifically. That property exists on the main Payload config under `admin.vite`. You can check out the [Vite docs](https://vitejs.dev/config/shared-options.html) for more information on what you can do with the Vite config.

View File

@@ -10,13 +10,13 @@ Payload has a Webpack (v5) bundler that you can build the Admin panel with. For
Out of the box, the Webpack bundler supports common functionalities such as SCSS and Typescript, but there are many cases where you may need to add support for additional functionalities.
#### Installation
## Installation
```bash
yarn add @payloadcms/bundler-webpack
```
#### Import the bundler
### Import the bundler
```ts
// payload.config.ts
@@ -33,7 +33,7 @@ export default buildConfig({
})
```
### Extending Webpack
## Extending Webpack
If you need to extend the Webpack config, you can do so by passing a function to the `admin.webpack` property on your Payload config.
The function will receive the Webpack config as an argument and should return the modified config.

View File

@@ -13,7 +13,7 @@ To enable Authentication on a collection, define an `auth` property and set it t
## Options
| Option | Description |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`useAPIKey`** | Payload Authentication provides for API keys to be set on each user within an Authentication-enabled Collection. [More](/docs/authentication/config#api-keys) |
| **`tokenExpiration`** | How long (in seconds) to keep the user logged in. JWTs and HTTP-only cookies will both expire at the same time. |
| **`maxLoginAttempts`** | Only allow a user to attempt logging in X amount of times. Automatically locks out a user from authenticating if this limit is passed. Set to `0` to disable. |

View File

@@ -8,7 +8,7 @@ keywords: authentication, config, configuration, documentation, Content Manageme
Enabling Authentication on a Collection automatically exposes additional auth-based operations in the Local, REST, and GraphQL APIs.
### Access
## Access
The Access operation returns what a logged in user can and can't do with the collections and globals that are registered via your config. This data can be immensely helpful if your app needs to show and hide certain features based on access control, as the Payload Admin panel does.
@@ -69,7 +69,7 @@ query {
Document access can also be queried on a collection/global basis. Access on a global can queried like `http://localhost:3000/api/global-slug/access`, Collection document access can be queried like `http://localhost:3000/api/collection-slug/access/:id`.
### Me
## Me
Returns either a logged in user with token or null when there is no logged in user.
@@ -105,7 +105,7 @@ query {
}
```
### Login
## Login
Accepts an `email` and `password`. On success, it will return the logged in user as well as a token that can be used to authenticate. In the GraphQL and REST APIs, this operation also automatically sets an HTTP-only cookie including the user's token. If you pass an Express `res` to the Local API operation, Payload will set a cookie there as well.
@@ -166,7 +166,7 @@ const result = await payload.login({
})
```
### Logout
## Logout
As Payload sets HTTP-only cookies, logging out cannot be done by just removing a cookie in JavaScript, as HTTP-only cookies are inaccessible by JS within the browser. So, Payload exposes a `logout` operation to delete the token in a safe way.
@@ -189,7 +189,7 @@ mutation {
}
```
### Refresh
## Refresh
Allows for "refreshing" JWTs. If your user has a token that is about to expire, but the user is still active and using the app, you might want to use the `refresh` operation to receive a new token by sending the operation the token that is about to expire.
@@ -244,7 +244,7 @@ mutation {
`token` arg.
</Banner>
### Verify by Email
## Verify by Email
If your collection supports email verification, the Verify operation will be exposed which accepts a verification token and sets the user's `_verified` property to `true`, thereby allowing the user to authenticate with the Payload API.
@@ -276,7 +276,7 @@ const result = await payload.verifyEmail({
})
```
### Unlock
## Unlock
If a user locks themselves out and you wish to deliberately unlock them, you can utilize the Unlock operation. The Admin panel features an Unlock control automatically for all collections that feature max login attempts, but you can programmatically unlock users as well by using the Unlock operation.
@@ -309,7 +309,7 @@ const result = await payload.unlock({
})
```
### Forgot Password
## Forgot Password
Payload comes with built-in forgot password functionality. Submitting an email address to the Forgot Password operation will generate an email and send it to the respective email address with a link to reset their password.
@@ -361,7 +361,7 @@ const token = await payload.forgotPassword({
use the token to "reset" their password.
</Banner>
### Reset Password
## Reset Password
After a user has "forgotten" their password and a token is generated, that token can be used to send to the reset password operation along with a new password which will allow the user to reset their password securely.

View File

@@ -30,7 +30,7 @@ _Admin panel screenshot depicting an Admins Collection with Auth enabled_
By default, Payload provides you with a `User` collection that supports Authentication, which is used to access the Admin panel. But, you can add support to one or many Collections of your own. For more information on how to customize, override, or remove the default `User` collection, [click here](/docs/admin/overview#the-admin-user-collection).
### Enabling Auth on a collection
## Enabling Auth on a collection
Every Payload Collection can opt-in to supporting Authentication by specifying the `auth` property on the Collection's config to either `true` or to an object containing `auth` options.
@@ -71,11 +71,11 @@ export const Admins: CollectionConfig = {
Once enabled, each document that is created within the Collection can be thought of as a `user` - who can make use of commonly required authentication functions such as logging in / out, resetting their password, and more.
### Logging in / out, resetting password, etc.
## Logging in / out, resetting password, etc.
[Click here](/docs/authentication/operations) for a list of all automatically-enabled Auth operations, including `login`, `logout`, `refresh`, and others.
### Token-based auth
## Token-based auth
Successfully logging in returns a `JWT` (JSON web token) which is how a user will identify themselves to Payload. By providing this JWT via either an HTTP-only cookie or an `Authorization: JWT` or `Authorization: Bearer` header, Payload will automatically identify the user and add its user JWT data to the Express `req`, which is available throughout Payload including within access control, hooks, and more.
@@ -89,15 +89,15 @@ You can specify what data gets encoded to the JWT token by setting `saveToJWT` t
property.
</Banner>
### HTTP-only cookies
## HTTP-only cookies
Payload `login`, `logout`, and `refresh` operations make use of HTTP-only cookies for authentication purposes. HTTP-only cookies are a highly secure method of storing identifiable data on a user's device so that Payload can automatically recognize a returning user until their cookie expires. They are totally protected from common XSS attacks and cannot be read at all via JavaScript in the browser.
##### Automatic browser inclusion
#### Automatic browser inclusion
Modern browsers automatically include `http-only` cookies when making requests directly to URLs—meaning that if you are running your API on http://example.com, and you have logged in and visit http://example.com/test-page, your browser will automatically include the Payload authentication cookie for you.
##### Using Fetch or other HTTP APIs
#### Using Fetch or other HTTP APIs
However, if you use `fetch` or similar APIs to retrieve Payload resources from its REST or GraphQL API, you need to specify to include credentials (cookies).
@@ -121,7 +121,7 @@ For more about how to automatically include cookies in requests from your app to
will still show HTTP-only cookies, even when JavaScript running on the page can't.
</Banner>
### CSRF Protection
## CSRF Protection
CSRF (cross-site request forgery) attacks are common and dangerous. By using an HTTP-only cookie, Payload removes many XSS vulnerabilities, however, CSRF attacks can still be possible.
@@ -159,7 +159,7 @@ const config = buildConfig({
export default config
```
### Identifying users via the Authorization Header
## Identifying users via the Authorization Header
In addition to authenticating via an HTTP-only cookie, you can also identify users via the `Authorization` header on an HTTP request.

View File

@@ -6,7 +6,7 @@ desc: Quickly configure and deploy your Payload Cloud project in a few simple st
keywords: configuration, config, settings, project, cloud, payload cloud, deploy, deployment
---
### Select your plan
## Select your plan
Once you have created a project, you will need to select your plan. This will determine the resources that are allocated to your project and the features that are available to you.
@@ -17,7 +17,7 @@ Once you have created a project, you will need to select your plan. This will de
anytime.
</Banner>
### Project Details
## Project Details
| Option | Description |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -26,7 +26,7 @@ Once you have created a project, you will need to select your plan. This will de
| **Project Slug** | Choose a unique slug to identify your project. This needs to be unique for your team and you can change it any time. |
| **Team** | Select the team you want to create the project under. If this is your first project, a personal team will be created for you automatically. You can modify your team settings and invite new members at any time from the Team Settings page. |
### Build Settings
## Build Settings
If you are deploying a new project from a template, the following settings will be automatically configured for you. If you are using your own repository, you need to make sure your build settings are accurate for your project to deploy correctly.
@@ -39,7 +39,7 @@ If you are deploying a new project from a template, the following settings will
| **Branch to Deploy** | Select the branch of your repository that you want to deploy from. This is the branch that will be used to build your project when you commit new changes. |
| **Default Domain** | Set a default domain for your project. This must be unique and you will not able to change it. You can always add a custom domain later in your project settings. |
### Environment Variables
## Environment Variables
Any of the features in Payload Cloud that require environment variables will automatically be provided to your application. If your app requires any custom environment variables, you can set them here.
@@ -49,7 +49,7 @@ Any of the features in Payload Cloud that require environment variables will aut
[here](https://payloadcms.com/docs/admin/webpack#admin-environment-vars).
</Banner>
### Payment
## Payment
Payment methods can be set per project and can be updated any time. You can use teams default payment method, or add a new one. Modify your payment methods in your Project settings / Team settings.

View File

@@ -6,7 +6,7 @@ desc: Manage your Payload Cloud projects.
keywords: cloud, payload cloud, projects, project, overview, database, file storage, build settings, environment variables, custom domains, email, developing locally
---
### Overview
## Overview
<Banner>
The overview tab shows your most recent deployment, along with build and deployment logs. From
@@ -18,19 +18,19 @@ keywords: cloud, payload cloud, projects, project, overview, database, file stor
![Payload Cloud Overview Page](https://payloadcms.com/images/docs/cloud/overview-page.jpg)
_A screenshot of the Overview page for a Cloud project._
### Database
## Database
Your Payload Cloud project comes with a MongoDB serverless Atlas DB instance or a Dedicated Atlas cluster, depending on your plan. To interact with your cloud database, you will be provided with a MongoDB connection string. This can be found under the **Database** tab of your project.
`mongodb+srv://your_connection_string`
### File Storage
## File Storage
Payload Cloud gives you S3 file storage backed by Cloudflare as a CDN, and this plugin extends Payload so that all of your media will be stored in S3 rather than locally.
AWS Cognito is used for authentication to your S3 bucket. The [Payload Cloud Plugin](https://github.com/payloadcms/plugin-cloud) will automatically pick up these values. These values are only if you'd like to access your files directly, outside of Payload Cloud.
#### Accessing Files Outside of Payload Cloud
### Accessing Files Outside of Payload Cloud
If you'd like to access your files outside of Payload Cloud, you'll need to retrieve some values from your project's settings and put them into your environment variables. In Payload Cloud, navigate to the File Storage tab and copy the values using the copy button. Put these values in your .env file. Also copy the Cognito Password value separately and put into your .env file as well.
@@ -50,11 +50,11 @@ PAYLOAD_CLOUD_COGNITO_PASSWORD=
The plugin will pick up these values and use them to access your files.
### Build Settings
## Build Settings
You can update settings from your Projects Settings tab. Changes to your build settings will trigger a redeployment of your project.
### Environment Variables
## Environment Variables
From the Environment Variables page of the Settings tab, you can add, update and delete variables for use in your project. Like build settings, these changes will trigger a redeployment of your project.
@@ -64,7 +64,7 @@ From the Environment Variables page of the Settings tab, you can add, update and
[here](https://payloadcms.com/docs/admin/webpack#admin-environment-vars).
</Banner>
### Custom Domains
## Custom Domains
With Payload Cloud, you can add custom domain names to your project. To do so, first go to the Domains page of the Settings tab of your project. Here you can see your default domain. To add a new domain, type in the domain name you wish to use.
@@ -84,19 +84,19 @@ export default buildConfig({
})
```
### Email
## Email
Powered by [Resend](https://resend.com), Payload Cloud comes with integrated email support out of the box. No configuration is needed, and you can use `payload.sendEmail()` to send email right from your Payload app. To learn more about sending email with Payload, checkout the [Email Configuration](https://payloadcms.com/docs/email/overview) overview.
If you are on the Pro or Enterprise plan, you can add your own custom Email domain name. From the Email page of your projects Settings, add the domain you wish to use for email delivery. This will generate a set of DNS records. Add these records to your DNS provider and click verify to check that your records are resolving properly. Once verified, your emails will now be sent from your custom domain name.
### Developing Locally
## Developing Locally
To make changes to your project, you will need to clone the repository defined in your project settings to your local machine. In order to run your project locally, you will need configure your local environment first. Refer to your repositorys `README.md` file to see the steps needed for your specific template.
From there, you are ready to make updates to your project. When you are ready to make your changes live, commit your changes to the branch you specified in your Project settings, and your application will automatically trigger a redeploy and build from your latest commit.
### Cloud Plugin
## Cloud Plugin
Projects generated from a template will come pre-configured with the official Cloud Plugin, but if you are using your own repository you will need to add this into your project. To do so, add the plugin to your Payload config:
@@ -117,7 +117,7 @@ export default buildConfig({
over Payload Cloud's email service.
</Banner>
##### **Optional configuration**
#### **Optional configuration**
If you wish to opt-out of any Payload cloud features, the plugin also accepts options to do so.

View File

@@ -14,22 +14,22 @@ keywords: team, teams, billing, subscription, payment, plan, plans, cloud, paylo
![Payload Cloud Team Settings](https://payloadcms.com/images/docs/cloud/team-settings.jpg)
_A screenshot of the Team Settings page._
### Members
## Members
Each team has members that can interact with your projects. You can invite multiple people to your team and each individual can belong to more than one team. You can assign them either `owner` or `user` permissions. Owners are able to make admin-only changes, such as deleting projects, and editing billing information.
### Adding Members
## Adding Members
To add a new member to your team, visit your Teams Settings page, and click “Invite Teammate”. You can then add their email address, and assign their role. Press “Save” to send the invitations, which will send an email to the invited team member where they can create a new account.
### Billing
## Billing
Users can update billing settings and subscriptions for any teams where they are designated as an `owner`. To make updates to the teams payment methods, visit the Billing page under the Team Settings tab. You can add new cards, delete cards, and set a payment method as a default. The default payment method will be used in the event that another payment method fails.
### Subscriptions
## Subscriptions
From the Subscriptions page, a team owner can see all current plans for their team. From here, you can see the price of each plan, if there is an active trial, and when you will be billed next.
### Invoices
## Invoices
The Invoices page will you show you the invoices for your account, as well as the status on their payment.

View File

@@ -35,7 +35,7 @@ It's often best practice to write your Collections in separate files and then im
_\* An asterisk denotes that a property is required._
#### Simple collection example
### Simple collection example
```ts
import { CollectionConfig } from 'payload/types'
@@ -58,13 +58,13 @@ export const Orders: CollectionConfig = {
}
```
#### More collection config examples
### More collection config examples
You can find an assortment
of [example collection configs](https://github.com/payloadcms/public-demo/tree/master/src/payload/collections) in the Public
Demo source code on GitHub.
### Admin options
## Admin options
You can customize the way that the Admin panel behaves on a collection-by-collection basis by defining the `admin`
property on a collection's config.
@@ -80,13 +80,14 @@ property on a collection's config.
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this collection. |
| `enableRichTextLink` | The [Rich Text](/docs/fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
| `enableRichTextRelationship` | The [Rich Text](/docs/fields/rich-text) field features a `Relationship` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
| `meta` | Metadata overrides to apply to the [Admin panel](../admin/overview). Included properties are `description` and `openGraph`. |
| `preview` | Function to generate preview URLS within the Admin panel that can point to your app. [More](#preview). |
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More](/docs/live-preview/overview). |
| `components` | Swap in your own React components to be used within this collection. [More](/docs/admin/components#collections) |
| `listSearchableFields` | Specify which fields should be searched in the List search view. [More](#list-searchable-fields) |
| **`pagination`** | Set pagination-specific options for this collection. [More](#pagination) |
### Preview
## Preview
Collection `admin` options can accept a `preview` function that will be used to generate a link pointing to the frontend
of your app to preview data.
@@ -125,7 +126,7 @@ export const Posts: CollectionConfig = {
}
```
### Pagination
## Pagination
Here are a few options that you can specify options for pagination on a collection-by-collection basis:
@@ -134,23 +135,23 @@ Here are a few options that you can specify options for pagination on a collecti
| `defaultLimit` | Integer that specifies the default per-page limit that should be used. Defaults to 10. |
| `limits` | Provide an array of integers to use as per-page options for admins to choose from in the List view. |
### Access control
## Access control
You can specify extremely granular access control (what users can do with documents in a collection) on a collection by
collection basis. To learn more, go to the [Access Control](/docs/access-control/overview) docs.
### Hooks
## Hooks
Hooks are a powerful way to extend collection functionality and execute your own logic, and can be defined on a
collection by collection basis. To learn more, go to the [Hooks](/docs/hooks/overview) documentation.
### Field types
## Field types
Collections support all field types that Payload has to offer—including simple fields like text and checkboxes all the
way to more complicated layout-building field groups like Blocks. [Click here](/docs/fields/overview) to learn more
about field types.
### List Searchable Fields
## List Searchable Fields
In the List view, there is a "search" box that allows you to quickly find a document with a search. By default, it
searches on the ID field. If you have `admin.useAsTitle` defined, the list search will use that field. However, you can
@@ -168,7 +169,7 @@ those three fields plus the ID field.
so your admin queries can remain performant.
</Banner>
### TypeScript
## TypeScript
You can import collection types as follows:

View File

@@ -8,7 +8,7 @@ keywords: config, configuration, documentation, Content Management System, cms,
Payload utilizes a few Express-specific middleware packages within its own routers. You can customize how they work by passing in configuration options to the main Payload config's `express` property.
### Custom Middleware
## Custom Middleware
Payload allows you to pass in custom Express middleware to be used on all of the routes it opens. This is useful for adding logging or any other custom functionality to your endpoints.
@@ -42,7 +42,7 @@ const requestLoggerMiddleware = (req, res, next) => {
}
```
### JSON
## JSON
`express.json()` is used to parse JSON body content into JavaScript objects accessible on the Express `req`. Payload allows you to customize all of the `json` method's options. Common examples of customization use-cases are increasing the max allowed JSON body size which defaults to `2MB`.
@@ -60,7 +60,7 @@ const requestLoggerMiddleware = (req, res, next) => {
You can find a list of all available options that are able to be passed to `express.json()` [here](https://expressjs.com/en/api.html).
### Compression
## Compression
Payload uses the `compression` package to optimize transfer size for all of the routes it opens, and you can pass customization options through the Payload config.

View File

@@ -30,7 +30,7 @@ As with Collection configs, it's often best practice to write your Globals in se
_\* An asterisk denotes that a property is required._
#### Simple Global example
### Simple Global example
```ts
import { GlobalConfig } from 'payload/types'
@@ -58,11 +58,11 @@ const Nav: GlobalConfig = {
export default Nav
```
#### Global config example
### Global config example
You can find a few [example Global configs](https://github.com/payloadcms/public-demo/tree/master/src/payload/globals) in the Public Demo source code on GitHub.
### Admin options
## Admin options
You can customize the way that the Admin panel behaves on a Global-by-Global basis by defining the `admin` property on a Global's config.
@@ -74,8 +74,9 @@ You can customize the way that the Admin panel behaves on a Global-by-Global bas
| `preview` | Function to generate a preview URL within the Admin panel for this global that can point to your app. [More](#preview). |
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More](/docs/live-preview/overview). |
| `hideAPIURL` | Hides the "API URL" meta field while editing documents within this collection. |
| `meta` | Metadata overrides to apply to the [Admin panel](../admin/overview). Included properties are `description` and `openGraph`. |
### Preview
## Preview
Global `admin` options can accept a `preview` function that will be used to generate a link pointing to the frontend of your app to preview data.
@@ -112,19 +113,19 @@ export const MyGlobal: GlobalConfig = {
}
```
### Access control
## Access control
As with Collections, you can specify extremely granular access control (what users can do with this Global) on a Global-by-Global basis. However, Globals only have `update` and `read` access control due to their nature of only having one document. To learn more, go to the [Access Control](/docs/access-control/overview) docs.
### Hooks
## Hooks
Globals also fully support a smaller subset of Hooks. To learn more, go to the [Hooks](/docs/hooks/overview) documentation.
### Field types
## Field types
Globals support all field types that Payload has to offer—including simple fields like text and checkboxes all the way to more complicated layout-building field groups like Blocks. [Click here](/docs/fields/overview) to learn more about field types.
### TypeScript
## TypeScript
You can import global types as follows:

View File

@@ -6,7 +6,7 @@ desc: Manage and customize internationalization support in your CMS editor exper
keywords: internationalization, i18n, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
---
Not only does Payload support managing localized content, it also has internationalization support so that admin users can work in their preferred language. Payload's i18n support is built on top of [i18next](https://www.i18next.com). It comes included by default and can be extended in your config.
Not only does Payload support managing localized content, it also has internationalization support so that admin users can work in their preferred language. It comes included by default and can be extended in your config.
While Payload's built-in features come translated, you may want to also translate parts of your project's configuration too. This is possible in places like collections and globals labels and groups, field labels, descriptions and input placeholder text. The admin UI will display all the correct translations you provide based on the user's language.
@@ -56,7 +56,7 @@ export const Articles: CollectionConfig = {
}
```
### Admin UI
## Admin UI
The Payload admin panel reads the language settings of a user's browser and display all text in that language, or will fall back to English if the user's language is not yet supported.
After a user logs in, they can change their language selection in the `/account` view.
@@ -68,15 +68,13 @@ After a user logs in, they can change their language selection in the `/account`
[contributions](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md).
</Banner>
### Node Express
## Node Express
Payload's backend uses express middleware to set the language on incoming requests before they are handled. This allows backend validation to return error messages in the user's own language or system generated emails to be sent using the correct translation. You can make HTTP requests with the `accept-language` header and Payload will use that language.
Anywhere in your Payload app that you have access to the `req` object, you can access i18next's extensive internationalization features assigned to `req.i18n`. To access text translations you can use `req.t('namespace:key')`.
Anywhere in your Payload app that you have access to the `req` object, you can access payload's extensive internationalization features assigned to `req.i18n`. To access text translations you can use `req.t('namespace:key')`.
Read the i18next [API documentation](https://www.i18next.com/overview/api) to learn more.
### Configuration Options
## Configuration Options
In your Payload config, you can add translations and customize the settings in `i18n`. Payload will use your custom options and merge it with the default, allowing you to override the settings Payload provides.
@@ -88,9 +86,8 @@ import { buildConfig } from 'payload/config'
export default buildConfig({
//...
i18n: {
fallbackLng: 'en', // default
debug: false, // default
resources: {
fallbackLanguage: 'en', // default
translations: {
en: {
custom: {
// namespace can be anything you want
@@ -107,4 +104,63 @@ export default buildConfig({
})
```
See the i18next [configuration options](https://www.i18next.com/overview/configuration-options) to learn more.
## Types for custom translations
In order to use custom translations in your project, you need to provide the types for the translations. Here is an example of how you can define the types for the custom translations in a custom react component:
```ts
'use client'
import type { NestedKeysStripped } from '@payloadcms/translations'
import type React from 'react'
import { useTranslation } from '@payloadcms/ui/providers/Translation'
const customTranslations = {
en: {
general: {
test: 'Custom Translation',
},
},
}
type CustomTranslationObject = typeof customTranslations.en
type CustomTranslationKeys = NestedKeysStripped<CustomTranslationObject>
export const MyComponent: React.FC = () => {
const { i18n, t } = useTranslation<CustomTranslationObject, CustomTranslationKeys>() // These generics merge your custom translations with the default client translations
return t('general:test')
}
```
Additionally, payload exposes the `t` function in various places, for example in labels. Here is how you would type those:
```ts
import type {
DefaultTranslationKeys,
NestedKeysStripped,
TFunction,
} from '@payloadcms/translations'
import type { Field } from 'payload/types'
const customTranslations = {
en: {
general: {
test: 'Custom Translation',
},
},
}
type CustomTranslationObject = typeof customTranslations.en
type CustomTranslationKeys = NestedKeysStripped<CustomTranslationObject>
const field: Field = {
name: 'myField',
type: 'text',
label: (
{ t }: { t: TFunction<CustomTranslationKeys | DefaultTranslationKeys> }, // The generic passed to TFunction does not automatically merge the custom translations with the default translations. We need to merge them ourselves here
) => t('fields:addLabel'),
}
```

View File

@@ -9,7 +9,7 @@ keywords: localization, internationalization, i18n, config, configuration, docum
Payload features deep field-based localization support. Maintaining as many locales as you need is easy. All
localization support is opt-in by default. To do so, follow the two steps below.
### Enabling in the Payload config
## Enabling in the Payload config
Add the `localization` property to your Payload config to enable localization project-wide. You'll need to provide a
list of all locales that you'd like to support as well as set a few other options.
@@ -49,7 +49,8 @@ export default buildConfig({
{
label: 'Arabic',
code: 'ar',
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left) when current locale is rtl
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left)
// when current locale is rtl
rtl: true,
},
],
@@ -102,7 +103,7 @@ right-to-left), and `fallbackLocale` property. The locale codes do not need to b
to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter
language and country codes (ISO 31661) such as `en-US`, `en-UK`, `es-MX`, etc.
### Locale Properties:
## Locale Object Properties
| Option | Description |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
@@ -124,7 +125,7 @@ Boolean enabling "fallback" locale functionality. If a document is requested in
localized value corresponding to the requested locale, then if this property is enabled, the document will automatically
fall back to the fallback locale value. If this property is not enabled, the value will not be populated.
### Field by field localization
## Field by field localization
Payload localization works on a **field** level—not a document level. In addition to configuring the base Payload config
to support localization, you need to specify each field that you would like to localize.
@@ -134,13 +135,9 @@ to support localization, you need to specify each field that you would like to l
```js
{
name: 'title',
type
:
'text',
// highlight-start
localized
:
true,
type: 'text',
// highlight-start
localized: true,
// highlight-end
}
```
@@ -169,12 +166,12 @@ and `block`s.
strategy.
</Banner>
### Retrieving localized docs
## Retrieving localized docs
When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be
used.
##### REST API
#### REST API
REST API locale functionality relies on URL query parameters.
@@ -193,7 +190,7 @@ valid locale as provided to your base Payload config, or `'null'`, `'false'`, or
fetch('https://localhost:3000/api/pages?locale=es&fallback-locale=none');
```
##### GraphQL API
#### GraphQL API
In the GraphQL API, you can specify `locale` and `fallbackLocale` args to all relevant queries and mutations.
@@ -221,7 +218,7 @@ query {
arguments in nested related document queries.
</Banner>
##### Local API
#### Local API
You can specify `locale` as well as `fallbackLocale` within the Local API as well as properties on the `options`
argument. The `locale` property will accept any valid locale, and the `fallbackLocale` property will accept any valid

View File

@@ -49,7 +49,7 @@ Payload is a _config-based_, code-first CMS and application framework. The Paylo
_\* An asterisk denotes that a property is required._
#### Simple example
### Simple example
```ts
import { buildConfig } from 'payload/config'
@@ -106,11 +106,11 @@ export default buildConfig({
})
```
#### Full example config
### Full example config
You can see a full [example config](https://github.com/payloadcms/public-demo/blob/master/src/payload/payload.config.ts) in the Public Demo source code on GitHub.
### Using environment variables in your config
## Using environment variables in your config
We suggest using the `dotenv` package to handle environment variables alongside of Payload. All that's necessary to do is to require the package as high up in your application as possible (for example, at the top of your `server.js` file), and ensure that it can find an `.env` file that you create.
@@ -142,7 +142,7 @@ project-name
can access it. [Click here](/docs/admin/webpack#admin-environment-vars) for more info.
</Banner>
### Customizing & Automating Config Location Detection
## Customizing & Automating Config Location Detection
Payload is designed to automatically locate your configuration file. By default, it will first look in the root of your current working directory for a file named `payload.config.js` or `payload.config.ts` if you're using TypeScript.
@@ -152,7 +152,7 @@ In production mode, Payload will first attempt to find the config file in the ou
Please ensure your `tsconfig.json` is properly configured if you want Payload to accurately auto-detect your configuration file location. If `tsconfig.json` does not exist or doesn't specify `rootDir` or `outDir`, Payload will default to the current working directory.
#### Overriding the Config Location
### Overriding the Config Location
In addition to the above automated detection, you can specify your own location for the Payload config file. This is done by using the environment variable `PAYLOAD_CONFIG_PATH`. The path you provide via this environment variable can either be absolute or relative to your current working directory. This can be useful in situations where your Payload config is not in a standard location, or you wish to switch between multiple configurations.
@@ -168,11 +168,11 @@ In addition to the above automated detection, you can specify your own location
When `PAYLOAD_CONFIG_PATH` is set, Payload will use this path to load the configuration, bypassing all automated detection.
### Developing within the Config
## Developing within the Config
Payload comes with `isomorphic-fetch` installed which means that even in Node, you can use the `fetch` API just as you would within the browser. No need to import `axios` or similar, unless you want to!
### TypeScript
## TypeScript
You can import config types as follows:
@@ -190,7 +190,7 @@ import { SanitizedConfig } from 'payload/config'
// Generally, this is only used internally by Payload.
```
### Telemetry
## Telemetry
Payload collects **completely anonymous** telemetry data about general usage. This data is super important to us and helps us accurately understand how we're growing and what we can do to build the software into everything that it can possibly be. The telemetry that we collect also help us demonstrate our growth in an accurate manner, which helps us as we seek investment to build and scale our team. If we can accurately demonstrate our growth, we can more effectively continue to support Payload as free and open-source software. To opt out of telemetry, you can pass `telemetry: false` within your Payload config.

View File

@@ -24,7 +24,7 @@ Ensure you have an npm script called "payload" in your `package.json` file.
because Payload should not be globally installed on your system.
</Banner>
### Migration file contents
## Migration file contents
Payload stores all created migrations in a folder that you can specify. By default, migrations are stored
in `./src/migrations`.
@@ -53,7 +53,7 @@ export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
}
```
### Migrations Directory
## Migrations Directory
Each DB adapter has an optional property `migrationDir` where you can override where you want your migrations to be
stored/read. If this is not specified, Payload will check the default and possibly make a best effort to find your

View File

@@ -28,7 +28,7 @@ export default buildConfig({
})
```
### Options
## Options
| Option | Description |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- |
@@ -38,7 +38,7 @@ export default buildConfig({
| `migrationDir` | Customize the directory that migrations are stored. |
| `transactionOptions` | An object with configuration properties used in [transactions](https://www.mongodb.com/docs/manual/core/transactions/) or `false` which will disable the use of transactions. | |
### Access to Mongoose models
## Access to Mongoose models
After Payload is initialized, this adapter exposes all of your Mongoose models and they are available for you to work
with directly.

View File

@@ -19,7 +19,7 @@ To use a specific database adapter, you need to install it and configure it acco
There are several factors to consider when choosing which database technology and hosting option is right for your project and workload. Payload can theoretically support any database, but it's up to you to decide which database to use.
#### When to use MongoDB
### When to use MongoDB
If your project has a lot of dynamic fields, and you are comfortable with allowing Payload to enforce data integrity across your documents, MongoDB is a great choice. With it, your Payload documents are stored as _one_ document in your database—no matter if you have localization enabled, how many block or array fields you have, etc. This means that the shape of your data in your database will very closely reflect your field schema, and there is minimal complexity involved in storing or retrieving your data.
@@ -30,7 +30,7 @@ You should prefer MongoDB if:
- Most (or everything) in your project is localized
- You leverage a lot of array fields, block fields, or `hasMany` select fields and similar
#### When to use a relational DB
### When to use a relational DB
Many projects might call for more rigid database architecture where the shape of your data is strongly enforced at the database level. For example, if you know the shape of your data and it's relatively "flat", and you don't anticipate it to change often, your workload might suit relational databases like Postgres very well.
@@ -40,7 +40,7 @@ You should prefer a relational DB like Postgres if:
- You require enforced data consistency at the database level
- You have a lot of relationships between collections and require relationships to be enforced
#### Differences in Payload features
### Differences in Payload features
It's important to note that almost everything Payload does is available in all of our officially supported database adapters, including localization, arrays, blocks, etc.

View File

@@ -36,7 +36,7 @@ export default buildConfig({
})
```
### Options
## Options
| Option | Description |
|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
@@ -51,7 +51,7 @@ export default buildConfig({
### Access to Drizzle
## Access to Drizzle
After Payload is initialized, this adapter will expose the full power of Drizzle to you for use if you need it.
@@ -61,7 +61,7 @@ You can access Drizzle as follows:
payload.db.drizzle
```
### Tables, relations, and enums
## Tables, relations, and enums
In addition to exposing Drizzle directly, all of the tables, Drizzle relations, and enum configs are exposed for you via the `payload.db` property as well.
@@ -69,7 +69,7 @@ In addition to exposing Drizzle directly, all of the tables, Drizzle relations,
- Enums - `payload.db.enums`
- Relations - `payload.db.relations`
### Prototyping in development mode
## Prototyping in development mode
Drizzle exposes two ways to work locally in development mode.
@@ -79,7 +79,7 @@ You will be warned if any changes that you make will entail data loss while in d
Alternatively, you can disable `push` and rely solely on migrations to keep your local database in sync with your Payload config.
### Migration workflows
## Migration workflows
Migrations are extremely powerful thanks to the seamless way that Payload and Drizzle work together. Let's take the following scenario:

View File

@@ -20,7 +20,8 @@ The initial request made to Payload will begin a new transaction and attach it t
```ts
const afterChange: CollectionAfterChangeHook = async ({ req }) => {
// because req.transactionID is assigned from Payload and passed through, my-slug will only persist if the entire request is successful
// because req.transactionID is assigned from Payload and passed through,
// my-slug will only persist if the entire request is successful
await req.payload.create({
req,
collection: 'my-slug',
@@ -31,7 +32,7 @@ const afterChange: CollectionAfterChangeHook = async ({ req }) => {
}
```
### Async Hooks with Transactions
## Async Hooks with Transactions
Since Payload hooks can be async and be written to not await the result, it is possible to have an incorrect success response returned on a request that is rolled back. If you have a hook where you do not `await` the result, then you should **not** pass the `req.transactionID`.
@@ -58,7 +59,7 @@ const afterChange: CollectionAfterChangeHook = async ({ req }) => {
}
```
### Direct Transaction Access
## Direct Transaction Access
When writing your own scripts or custom endpoints, you may wish to have direct control over transactions. This is useful for interacting with your database outside of Payload's local API.

View File

@@ -2,166 +2,165 @@
title: Email Functionality
label: Overview
order: 10
desc: Payload uses NodeMailer to allow you to send emails smoothly from your app. Set up email functions such as password resets, order confirmations and more.
desc: Payload uses an adapter pattern to enable email functionality. Set up email functions such as password resets, order confirmations and more.
keywords: email, overview, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
---
### Introduction
## Introduction
Payload comes ready to send your application's email. Whether you simply need built-in password reset
email to work or you want customers to get an order confirmation email, you're almost there. Payload makes use of
[NodeMailer](https://nodemailer.com) for email and won't get in your way for those already familiar.
Payload has a few email adapters that can be imported to enable email functionality. The [@payloadcms/email-nodemailer](https://www.npmjs.com/package/@payloadcms/email-nodemailer) package will be the package most will want to install. This package provides an easy way to use [Nodemailer](https://nodemailer.com) for email and won't get in your way for those already familiar.
For email to send from your Payload server, some configuration is required. The settings you provide will be set
in the `email` property object of your payload init call. Payload will make use of the transport that you have configured for it for things like reset password or verifying new user accounts and email send methods are available to you as well on your payload instance.
The email adapter should be passed into the `email` property of the Payload config. This will allow Payload to send emails for things like password resets, new user verification, and any other email sending needs you may have.
### Configuration
## Configuration
**Three ways to set it up**
### Default Configuration
1. **Default**: When email is not needed, a mock email handler will be created and used when nothing is provided. This is ideal for development environments and can be changed later when ready to [go to production](/docs/production/deployment).
1. **Recommended**: Set the `transportOptions` and Payload will do the set up for you.
1. **Advanced**: The `transport` object can be assigned a nodemailer transport object set up in your server scripts and given for Payload to use.
When email is not needed or desired, Payload will log a warning on startup notifying that email is not configured. A warning message will also be logged on any attempt to send an email.
The following options are configurable in the `email` property object as part of the options object when calling payload.init().
### Email Adapter
| Option | Description |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`fromName`** \* | The name part of the From field that will be seen on the delivered email |
| **`fromAddress`** \* | The email address part of the From field that will be used when delivering email |
| **`transport`** | The NodeMailer transport object for when you want to do it yourself, not needed when transportOptions is set |
| **`transportOptions`** | An object that configures the transporter that Payload will create. For all the available options see the [NodeMailer documentation](https://nodemailer.com) or see the examples below |
| **`logMockCredentials`** | If set to true and no transport/transportOptions, ethereal credentials will be logged to console on startup |
An email adapter will require at least the following fields:
_\* An asterisk denotes that a property is required._
| Option | Description |
| --------------------------- | -------------------------------------------------------------------------------- |
| **`defaultFromName`** \* | The name part of the From field that will be seen on the delivered email |
| **`defaultFromAddress`** \* | The email address part of the From field that will be used when delivering email |
### Use SMTP
Simple Mail Transfer Protocol (SMTP) options can be passed in using the `transportOptions` object on the `email` options. See the [NodeMailer SMTP documentation](https://nodemailer.com/smtp/) for more information, including details on when `secure` should and should not be set to `true`.
### Official Email Adapters
| Name | Package | Description |
| ---------- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Nodemailer | [@payloadcms/email-nodemailer](https://www.npmjs.com/package/@payloadcms/email-nodemailer) | Use any [Nodemailer transport](https://nodemailer.com/transports), including SMTP, Resend, SendGrid, and more. This was provided by default in Payload 2.x. This is the easiest migration path. |
| Resend | [@payloadcms/email-resend](https://www.npmjs.com/package/@payloadcms/email-resend) | Resend email via their REST API. This is preferred for serverless platforms such as Vercel because it is much more lightweight than the nodemailer adapter. |
## Nodemailer Configuration
| Option | Description |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`transport`** | The Nodemailer transport object for when you want to do it yourself, not needed when transportOptions is set |
| **`transportOptions`** | An object that configures the transporter that Payload will create. For all the available options see the [Nodemailer documentation](https://nodemailer.com) or see the examples below |
## Use SMTP
Simple Mail Transfer Protocol (SMTP) options can be passed in using the `transportOptions` object on the `email` options. See the [Nodemailer SMTP documentation](https://nodemailer.com/smtp/) for more information, including details on when `secure` should and should not be set to `true`.
**Example email options using SMTP:**
```ts
payload.init({
email: {
import { buildConfig } from 'payload/config'
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
export default buildConfig({
email: nodemailerAdapter({
defaultFromAddress: 'info@payloadcms.com',
defaultFromName: 'Payload',
// Nodemailer transportOptions
transportOptions: {
host: process.env.SMTP_HOST,
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
port: Number(process.env.SMTP_HOST),
secure: Number(process.env.SMTP_PORT) === 465, // true for port 465, false (the default) for 587 and others
requireTLS: true,
},
fromName: 'hello',
fromAddress: 'hello@example.com',
},
// ...
}),
})
```
<Banner type="warning">
It is best practice to avoid saving credentials or API keys directly in your code, use
[environment variables](/docs/configuration/overview#using-environment-variables-in-your-config).
</Banner>
### Use an email service
Many third party mail providers are available and offer benefits beyond basic SMTP. As an example, your payload init could look like this if you wanted to use SendGrid.com, though the same approach would work for any other [NodeMailer transports](https://nodemailer.com/transports/) shown here or provided by another third party.
**Example email options using nodemailer.createTransport:**
```ts
import payload from 'payload'
import nodemailerSendgrid from 'nodemailer-sendgrid'
const sendGridAPIKey = process.env.SENDGRID_API_KEY
payload.init({
...(sendGridAPIKey
? {
email: {
transportOptions: nodemailerSendgrid({
apiKey: sendGridAPIKey,
}),
fromName: 'Admin',
fromAddress: 'admin@example.com',
},
}
: {}),
})
```
### Use a custom NodeMailer transport
To take full control of the mail transport you may wish to use `nodemailer.createTransport()` on your server and provide it to Payload init.
```ts
import payload from 'payload'
import { buildConfig } from 'payload/config'
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
import nodemailer from 'nodemailer'
const payload = require('payload')
const nodemailer = require('nodemailer')
const transport = await nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
})
payload.init({
email: {
fromName: 'Admin',
fromAddress: 'admin@example.com',
transport,
},
// ...
export default buildConfig({
email: nodemailerAdapter({
defaultFromAddress: 'info@payloadcms.com',
defaultFromName: 'Payload',
// Any Nodemailer transport can be used
transport: nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
}),
}),
})
```
### Sending Mail
**Custom Transport:**
With a working transport you can call it anywhere you have access to payload by calling `payload.sendEmail(message)`. The `message` will contain the `to`, `subject` and `email` or `text` for the email being sent. To see all available message configuration options see [NodeMailer](https://nodemailer.com/message).
You also have the ability to bring your own nodemailer transport. This is an example of using the SendGrid nodemailer transport.
### Mock transport
By default, Payload uses a mock implementation that only sends mail to the [ethereal](https://ethereal.email) capture service that will never reach a user's inbox. While in development you may wish to make use of the captured messages which is why the payload output during server output helpfully logs this out on the server console.
To see ethereal credentials, add `logMockCredentials: true` to the email options. This will cause them to be logged to console on startup.
```ts
payload.init({
email: {
fromName: 'Admin',
fromAddress: 'admin@example.com',
logMockCredentials: true, // Optional
},
// ...
import { buildConfig } from 'payload/config'
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
import nodemailerSendgrid from 'nodemailer-sendgrid'
export default buildConfig({
email: nodemailerAdapter({
defaultFromAddress: 'info@payloadcms.com',
defaultFromName: 'Payload',
transportOptions: nodemailerSendgrid({
apiKey: process.env.SENDGRID_API_KEY,
}),
}),
})
```
**Console output when starting payload with a mock email instance and logMockCredentials: true**
During development, if you pass nothing to `nodemailerAdapter`, it will use the [ethereal.email](https://ethereal.email) service.
```
[06:37:21] INFO (payload): Starting Payload...
[06:37:22] INFO (payload): Payload Demo Initialized
[06:37:22] INFO (payload): listening on 3000...
[06:37:22] INFO (payload): Connected to MongoDB server successfully!
[06:37:23] INFO (payload): E-mail configured with mock configuration
[06:37:23] INFO (payload): Log into mock email provider at https://ethereal.email
[06:37:23] INFO (payload): Mock email account username: hhav5jw7doo4euev@ethereal.email
[06:37:23] INFO (payload): Mock email account password: VNdGcvDZeyEhtuPBqf
This will log the ethereal.email details to console on startup.
```ts
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
export default buildConfig({
email: nodemailerAdapter(),
})
```
The mock email handler is used when payload is started with neither `transport` or `transportOptions` to know how to deliver email.
## Resend Configuration
<Banner type="warning">
The randomly generated email account username and password will be different each time the Payload
server starts.
</Banner>
The Resend adapter requires an API key to be passed in the options. This can be found in the Resend dashboard. This is the preferred package if you are deploying on Vercel because this is much more lightweight than the Nodemailer adapter.
### Using multiple mail providers
| Option | Description |
| ------ | ----------------------------------- |
| apiKey | The API key for the Resend service. |
```ts
import { buildConfig } from 'payload/config'
import { resendAdapter } from '@payloadcms/email-resend'
export default buildConfig({
email: resendAdapter({
defaultFromAddress: 'dev@payloadcms.com',
defaultFromName: 'Payload CMS',
apiKey: process.env.RESEND_API_KEY || '',
}),
})
```
## Sending Mail
With a working transport you can call it anywhere you have access to payload by calling `payload.sendEmail(message)`. The `message` will contain the `to`, `subject` and `html` or `text` for the email being sent. Other options are also available and can be seen in the sendEmail args. Support for these will depend on the adapter being used.
```ts
// Example of sending an email
const email = await payload.sendEmail({
to: 'test@example.com',
subject: 'This is a test email',
text: 'This is my message body',
})
```
## Using multiple mail providers
Payload supports the use of a single transporter of email, but there is nothing stopping you from having more. Consider a use case where sending bulk email is handled differently than transactional email and could be done using a [hook](/docs/hooks/overview).

View File

@@ -24,7 +24,7 @@ keywords: array, fields, config, configuration, documentation, Content Managemen
- Navigational structures where editors can specify nav items containing pages ([relationship field](/docs/fields/relationship)), an "open in new tab" [checkbox field](/docs/fields/checkbox)
- Event agenda "timeslots" where you need to specify start & end time ([date field](/docs/fields/date)), label ([text field](/docs/fields/text)), and Learn More page [relationship](/docs/fields/relationship)
### Config
## Config
| Option | Description |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -49,7 +49,7 @@ keywords: array, fields, config, configuration, documentation, Content Managemen
_\* An asterisk denotes that a property is required._
### Admin Config
## Admin Config
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
@@ -57,8 +57,9 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
| ------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **`initCollapsed`** | Set the initial collapsed state |
| **`components.RowLabel`** | Function or React component to be rendered as the label on the array row. Receives `({ data, index, path })` as args |
| **`isSortable`** | Disable order sorting by setting this value to `false` |
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -26,7 +26,7 @@ keywords: blocks, fields, config, configuration, documentation, Content Manageme
- A form builder tool where available block configs might be `Text`, `Select`, or `Checkbox`.
- Virtual event agenda "timeslots" where a timeslot could either be a `Break`, a `Presentation`, or a `BreakoutSession`.
### Field config
## Field config
| Option | Description |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -49,15 +49,16 @@ keywords: blocks, fields, config, configuration, documentation, Content Manageme
_\* An asterisk denotes that a property is required._
### Admin Config
## Admin Config
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
| Option | Description |
| ------------------- | ------------------------------- |
| **`initCollapsed`** | Set the initial collapsed state |
| Option | Description |
| ------------------- | ---------------------------------- |
| **`initCollapsed`** | Set the initial collapsed state |
| **`isSortable`** | Disable order sorting by setting this value to `false` |
### Block configs
## Block configs
Blocks are defined as separate configs of their own.
@@ -83,7 +84,7 @@ Blocks are defined as separate configs of their own.
| **`dbName`** | Custom table name for this block type when using SQL database adapter ([Postgres](/docs/database/postgres)). Auto-generated from slug if not defined.
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
#### Auto-generated data per block
### Auto-generated data per block
In addition to the field data that you define on each block, Payload will store two additional properties on each block:
@@ -95,7 +96,7 @@ The `blockType` is saved as the slug of the block that has been selected.
The Admin panel provides each block with a `blockName` field which optionally allows editors to label their blocks for better editability and readability.
### Example
## Example
`collections/ExampleCollection.js`
@@ -138,7 +139,7 @@ export const ExampleCollection: CollectionConfig = {
}
```
### TypeScript
## TypeScript
As you build your own Block configs, you might want to store them in separate files but retain typing accordingly. To do so, you can import and use Payload's `Block` type:

View File

@@ -15,7 +15,7 @@ keywords: checkbox, fields, config, configuration, documentation, Content Manage
caption="Admin panel screenshot of Checkbox field with Text field below"
/>
### Config
## Config
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -35,7 +35,7 @@ keywords: checkbox, fields, config, configuration, documentation, Content Manage
_\* An asterisk denotes that a property is required._
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -21,7 +21,7 @@ keywords: code, fields, config, configuration, documentation, Content Management
This field uses the `monaco-react` editor syntax highlighting.
### Config
## Config
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -44,7 +44,7 @@ This field uses the `monaco-react` editor syntax highlighting.
_\* An asterisk denotes that a property is required._
### Admin Config
## Admin Config
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
@@ -53,7 +53,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
| **`language`** | This property can be set to any language listed [here](https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages). |
| **`editorOptions`** | Options that can be passed to the monaco editor, [view the full list](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IDiffEditorConstructionOptions.html). |
### Example
## Example
`collections/ExampleCollection.ts

View File

@@ -18,7 +18,7 @@ keywords: row, fields, config, configuration, documentation, Content Management
caption="Admin panel screenshot of a Collapsible field"
/>
### Config
## Config
| Option | Description |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -29,7 +29,7 @@ keywords: row, fields, config, configuration, documentation, Content Management
_\* An asterisk denotes that a property is required._
### Admin Config
## Admin Config
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
@@ -37,7 +37,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
| ------------------- | ------------------------------- |
| **`initCollapsed`** | Set the initial collapsed state |
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -20,7 +20,7 @@ keywords: date, fields, config, configuration, documentation, Content Management
This field uses [`react-datepicker`](https://www.npmjs.com/package/react-datepicker) for the Admin panel component.
### Config
## Config
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -40,7 +40,7 @@ This field uses [`react-datepicker`](https://www.npmjs.com/package/react-datepic
_\* An asterisk denotes that a property is required._
### Admin Config
## Admin Config
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can customize the following fields that will adjust how the component displays in the admin panel via the `date` property.
@@ -61,7 +61,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
_\* This property is passed directly to [react-datepicker](https://github.com/Hacker0x01/react-datepicker/blob/master/docs/datepicker.md). ._
#### Display Format and Picker Appearance
### Display Format and Picker Appearance
These properties only affect how the date is displayed in the UI. The full date is always stored in the format `YYYY-MM-DDTHH:mm:ss.SSSZ` (e.g. `1999-01-01T8:00:00.000+05:00`).
@@ -71,7 +71,7 @@ These properties only affect how the date is displayed in the UI. The full date
When only `pickerAppearance` is set, an equivalent format will be rendered in the date field cell. To overwrite this format, set `displayFormat`.
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -15,7 +15,7 @@ keywords: email, fields, config, configuration, documentation, Content Managemen
caption="Admin panel screenshot of an Email field"
/>
### Config
## Config
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -36,7 +36,7 @@ keywords: email, fields, config, configuration, documentation, Content Managemen
_\* An asterisk denotes that a property is required._
### Admin config
## Admin config
In addition to the default [field admin config](/docs/fields/overview#admin-config), this field type allows for the following `admin` properties:
@@ -48,7 +48,7 @@ Set this property to define a placeholder string for the field.
Set this property to a string that will be used for browser autocomplete.
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -18,7 +18,7 @@ keywords: group, fields, config, configuration, documentation, Content Managemen
caption="Admin panel screenshot of a Group field"
/>
### Config
## Config
| Option | Description |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -38,7 +38,7 @@ keywords: group, fields, config, configuration, documentation, Content Managemen
_\* An asterisk denotes that a property is required._
### Admin config
## Admin config
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Group allows for the following admin property:
@@ -46,7 +46,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
Set this property to `true` to hide this field's gutter within the admin panel. The field gutter is rendered as a vertical line and padding, but often if this field is nested within a Group, Block, or Array, you may want to hide the gutter.
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -4,7 +4,7 @@ label: JSON
order: 50
desc: The JSON field type will store any string in the Database. Learn how to use JSON fields, see examples and options.
keywords: json, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
keywords: json, jsonSchema, schema, validation, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express
---
<Banner>
@@ -21,7 +21,7 @@ keywords: json, fields, config, configuration, documentation, Content Management
This field uses the `monaco-react` editor syntax highlighting.
### Config
## Config
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -30,6 +30,7 @@ This field uses the `monaco-react` editor syntax highlighting.
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin panel and the backend. [More](/docs/fields/overview#validation) |
| **`jsonSchema`** | Provide a JSON schema that will be used for validation. [JSON schemas](https://json-schema.org/learn/getting-started-step-by-step)
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/config), include its data in the user JWT. |
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
@@ -42,7 +43,7 @@ This field uses the `monaco-react` editor syntax highlighting.
_\* An asterisk denotes that a property is required._
### Admin Config
## Admin Config
In addition to the default [field admin config](/docs/fields/overview#admin-config), you can adjust the following properties:
@@ -50,9 +51,9 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`editorOptions`** | Options that can be passed to the monaco editor, [view the full list](https://microsoft.github.io/monaco-editor/typedoc/variables/editor.EditorOptions.html). |
### Example
## Example
`collections/ExampleCollection.ts
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload/types'
@@ -68,3 +69,67 @@ export const ExampleCollection: CollectionConfig = {
],
}
```
## JSON Schema Validation
Payload JSON fields fully support the [JSON schema](https://json-schema.org/) standard. By providing a schema in your field config, the editor will be guided in the admin UI, getting typeahead for properties and their formats automatically. When the document is saved, the default validation will prevent saving any invalid data in the field according to the schema in your config.
If you only provide a URL to a schema, Payload will fetch the desired schema if it is publicly available. If not, it is recommended to add the schema directly to your config or import it from another file so that it can be implemented consistently in your project.
### Local JSON Schema
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload/types'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'customerJSON', // required
type: 'json', // required
jsonSchema: {
uri: 'a://b/foo.json', // required
fileMatch: ['a://b/foo.json'], // required
schema: {
type: 'object',
properties: {
foo: {
enum: ['bar', 'foobar'],
}
},
},
},
},
],
}
// {"foo": "bar"} or {"foo": "foobar"} - ok
// Attempting to create {"foo": "not-bar"} will throw an error
```
### Remote JSON Schema
`collections/ExampleCollection.ts`
```ts
import { CollectionConfig } from 'payload/types'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'customerJSON', // required
type: 'json', // required
jsonSchema: {
uri: 'https://example.com/customer.schema.json', // required
fileMatch: ['https://example.com/customer.schema.json'], // required
},
},
],
}
// If 'https://example.com/customer.schema.json' has a JSON schema
// {"foo": "bar"} or {"foo": "foobar"} - ok
// Attempting to create {"foo": "not-bar"} will throw an error
```

View File

@@ -18,7 +18,7 @@ keywords: number, fields, config, configuration, documentation, Content Manageme
caption="Admin panel screenshot of a Number field"
/>
### Config
## Config
| Option | Description |
|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
@@ -44,7 +44,7 @@ keywords: number, fields, config, configuration, documentation, Content Manageme
_\* An asterisk denotes that a property is required._
### Admin config
## Admin config
In addition to the default [field admin config](/docs/fields/overview#admin-config), this field type allows for the following `admin` properties:
@@ -60,7 +60,7 @@ Set this property to define a placeholder string for the field.
Set this property to a string that will be used for browser autocomplete.
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -36,7 +36,7 @@ export const Page: CollectionConfig = {
}
```
### Field types
## Field types
- [Array](/docs/fields/array) - for repeating content, supports nested fields
- [Blocks](/docs/fields/blocks) - block-based fields, allowing powerful layout creation
@@ -60,19 +60,19 @@ export const Page: CollectionConfig = {
- [Upload](/docs/fields/upload) - allows local file and image upload
- [UI](/docs/fields/ui) - inject your own custom components and do whatever you need
### Field-level hooks
## Field-level hooks
One of the most powerful parts about Payload is its ability for you to define field-level hooks that can control the logic of your fields to a fine-grained level. for more information about how to define field hooks, [click here](/docs/hooks/overview#field-hooks).
### Field-level access control
## Field-level access control
In addition to being able to define access control on a document-level, you can define extremely granular permissions on a field by field level. For more information about field-level access control, [click here](/docs/access-control/overview#fields).
### Field names
## Field names
Some fields use their `name` property as a unique identifier to store and retrieve from the database. `__v`, `salt`, and `hash` are all reserved field names which are sanitized from Payload's config and cannot be used.
### Validation
## Validation
Field validation is enforced automatically based on the field type and other properties such as `required` or `min` and `max` value constraints on certain field types. This default behavior can be replaced by providing your own validate function for any field. It will be used on both the frontend and the backend, so it should not rely on any Node-specific packages. The validation function can be either synchronous or asynchronous and expects to return either `true` or a string error message to display in both API responses and within the Admin panel.
@@ -91,7 +91,7 @@ There are two arguments available to custom validation functions.
| `user` | An object containing the currently authenticated user |
| `payload` | If the `validate` function is being executed on the server, Payload will be exposed for easily running local operations. |
### Example
## Example
```ts
import { CollectionConfig } from 'payload/types'
@@ -140,7 +140,7 @@ const field: Field = {
}
```
### Customizable ID
## Customizable ID
Collections ID fields are generated automatically by default. An explicit `id` field can be declared in the `fields` array to override this behavior.
Users are then required to provide a custom ID value when creating a record through the Admin UI or API.
@@ -159,29 +159,31 @@ Example:
}
```
### Admin config
## Admin config
In addition to each field's base configuration, you can define specific traits and properties for fields that only have effect on how they are rendered in the Admin panel. The following properties are available for all fields within the `admin` property:
| Option | Description |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `condition` | You can programmatically show / hide fields based on what other fields are doing. [Click here](#conditional-logic) for more info. |
| `components` | All field components can be completely and easily swapped out for custom components that you define. [Click here](#custom-components) for more info. |
| `description` | Helper text to display with the field to provide more information for the editor user. [Click here](#description) for more info. |
| `position` | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. |
| `width` | Restrict the width of a field. you can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. |
| `style` | Attach raw CSS style properties to the root DOM element of a field. |
| `className` | Attach a CSS class name to the root DOM element of a field. |
| `readOnly` | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. |
| `disabled` | If a field is `disabled`, it is completely omitted from the Admin panel. |
| `disableBulkEdit` | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. |
| `hidden` | Setting a field's `hidden` property on its `admin` config will transform it into a `hidden` input type. Its value will still submit with the Admin panel's requests, but the field itself will not be visible to editors. |
| Option | Description |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `condition` | You can programmatically show / hide fields based on what other fields are doing. [Click here](#conditional-logic) for more info. |
| `components` | All field components can be completely and easily swapped out for custom components that you define. [Click here](#custom-components) for more info. |
| `description` | Helper text to display with the field to provide more information for the editor user. [Click here](#description) for more info. |
| `position` | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. |
| `width` | Restrict the width of a field. you can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. |
| `style` | Attach raw CSS style properties to the root DOM element of a field. |
| `className` | Attach a CSS class name to the root DOM element of a field. |
| `readOnly` | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. |
| `disabled` | If a field is `disabled`, it is completely omitted from the Admin panel. |
| `disableBulkEdit` | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. |
| `disableListColumn` | Set `disableListColumn` to `true` to prevent fields from appearing in the list view column selector. |
| `disableListFilter` | Set `disableListFilter` to `true` to prevent fields from appearing in the list view filter options. |
| `hidden` | Setting a field's `hidden` property on its `admin` config will transform it into a `hidden` input type. Its value will still submit with the Admin panel's requests, but the field itself will not be visible to editors. |
### Custom components
## Custom components
All Payload fields support the ability to swap in your own React components with ease. For more information, including examples, [click here](/docs/admin/components#fields).
### Conditional logic
## Conditional logic
You can show and hide fields based on what other fields are doing by utilizing conditional logic on a field by field basis. The `condition` property on a field's admin config accepts a function which takes three arguments:
@@ -220,7 +222,7 @@ The `condition` function should return a boolean that will control if the field
}
```
### Default values
## Default values
Fields can be prefilled with starting values using the `defaultValue` property. This is used in the admin UI and also on the backend as API requests will be populated with missing or undefined field values. You can assign the defaultValue directly in the field configuration or supply a function for dynamic behavior. Values assigned during a create request on the server are added before validation occurs.
@@ -250,7 +252,7 @@ const field = {
You can use async defaultValue functions to fill fields with data from API requests.
</Banner>
### Description
## Description
A description can be configured in three ways.
@@ -312,7 +314,7 @@ This example will display the number of characters allowed as the user types.
This component will count the number of characters entered, as well as display the path of the field.
### TypeScript
## TypeScript
You can import the internal Payload `Field` type as well as other common field types as follows:

View File

@@ -25,7 +25,7 @@ keywords: point, geolocation, geospatial, geojson, 2dsphere, config, configurati
The data structure in the database matches the GeoJSON structure to represent point. The Payload APIs simplifies the object data to only the [longitude, latitude] location.
### Config
## Config
| Option | Description |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -50,7 +50,7 @@ _\* An asterisk denotes that a property is required._
<strong>Note:</strong> The Point field type is currently only supported in MongoDB.
</Banner>
### Example
## Example
`collections/ExampleCollection.ts`
@@ -69,6 +69,6 @@ export const ExampleCollection: CollectionConfig = {
}
```
### Querying
## Querying
In order to do query based on the distance to another point, you can use the `near` operator. When querying using the near operator, the returned documents will be sorted by nearest first.

View File

@@ -18,7 +18,7 @@ keywords: radio, fields, config, configuration, documentation, Content Managemen
caption="Admin panel screenshot of a Radio field"
/>
### Config
## Config
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -49,7 +49,7 @@ _\* An asterisk denotes that a property is required._
being used as a GraphQL enum.
</Banner>
### Admin config
## Admin config
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Radio Group field type allows for the specification of the following `admin` properties:
@@ -57,7 +57,7 @@ In addition to the default [field admin config](/docs/fields/overview#admin-conf
The `layout` property allows for the radio group to be styled as a horizonally or vertically distributed list. The default value is `horizontal`.
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -24,30 +24,30 @@ keywords: relationship, fields, config, configuration, documentation, Content Ma
- To allow for an `Order` to feature a `placedBy` relationship to either an `Organization` or `User` collection
- To assign `Category` documents to `Post` documents
### Config
## Config
| Option | Description |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
| **`relationTo`** \* | Provide one or many collection `slug`s to be able to assign relationships to. |
| **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-relationship-options). |
| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many relations instead of only one. |
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with `hasMany`. |
| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with `hasMany`. |
| **`maxDepth`** | Sets a number limit on iterations of related documents to populate when queried. [Depth](/docs/getting-started/concepts#depth) |
| **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. |
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin panel and the backend. [More](/docs/fields/overview#validation) |
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/config), include its data in the user JWT. |
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-config) for more details. |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
| Option | Description |
|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
| **`relationTo`** \* | Provide one or many collection `slug`s to be able to assign relationships to. |
| **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-relationship-options). |
| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many relations instead of only one. |
| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with `hasMany`. |
| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with `hasMany`. |
| **`maxDepth`** | Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](/docs/getting-started/concepts#field-level-max-depth) |
| **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. |
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
| **`validate`** | Provide a custom validation function that will be executed on both the Admin panel and the backend. [More](/docs/fields/overview#validation) |
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/config), include its data in the user JWT. |
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
| **`required`** | Require this field to have a value. |
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-config) for more details. |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
_\* An asterisk denotes that a property is required._
@@ -58,7 +58,7 @@ _\* An asterisk denotes that a property is required._
related documents that are returned by the API.
</Banner>
### Admin config
## Admin config
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Relationship field type also
allows for the following admin-specific properties:
@@ -122,7 +122,7 @@ In this configuration:
Note: If `sortOptions` is not defined, the default sorting behavior of the Relationship field dropdown will be used.
### Filtering relationship options
## Filtering relationship options
Options can be dynamically limited by supplying a [query constraint](/docs/queries/overview), which will be used both
for validating input and filtering available relationships in the UI.
@@ -139,7 +139,7 @@ called with an argument object with the following properties:
| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation |
| `user` | An object containing the currently authenticated user |
### Example
## Example
```ts
import { CollectionConfig } from 'payload/types'
@@ -181,13 +181,13 @@ You can learn more about writing queries [here](/docs/queries/overview).
<strong>payload/fields/validations</strong> in your validate function.
</Banner>
### How the data is saved
## How the data is saved
Given the variety of options possible within the `relationship` field type, the shape of the data needed for creating
and updating these fields can vary. The following sections will describe the variety of data shapes that can arise from
this field.
#### Has One
### Has One
The most simple pattern of a relationship is to use `hasMany: false` with a `relationTo` that allows for only one type
of collection.
@@ -221,7 +221,7 @@ When querying documents in this collection via REST API, you could query as foll
`?where[owner][equals]=6031ac9e1289176380734024`.
#### Has One - Polymorphic
### Has One - Polymorphic
Also known as **dynamic references**, in this configuration, the `relationTo` field is an array of Collection slugs that
tells Payload which Collections are valid to reference.
@@ -263,7 +263,7 @@ You can also query for documents where a field has a relationship to a specific
This query would return only documents that have an owner relationship to organizations.
#### Has Many
### Has Many
The `hasMany` tells Payload that there may be more than one collection saved to the field.
@@ -295,7 +295,7 @@ When querying documents, the format does not change for arrays:
`?where[owners][equals]=6031ac9e1289176380734024`.
#### Has Many - Polymorphic
### Has Many - Polymorphic
```ts
{
@@ -336,7 +336,7 @@ Querying is done in the same way as the earlier Polymorphic example:
`?where[owners.value][equals]=6031ac9e1289176380734024`.
#### Querying and Filtering Polymorphic Relationships
### Querying and Filtering Polymorphic Relationships
Polymorphic and non-polymorphic relationships must be queried differently because of how the related data is stored and
may be inconsistent across different collections. Because of this, filtering polymorphic relationship fields from the

View File

@@ -30,12 +30,13 @@ Right now, Payload is officially supporting two rich text editors:
Consistent with Payload's goal of making you learn as little of Payload as possible, customizing
and using the Rich Text Editor does not involve learning how to develop for a <em>Payload</em>{' '}
rich text editor.
</strong>{' '}
</strong>
Instead, you can invest your time and effort into learning the underlying open-source tools that
will allow you to apply your learnings elsewhere as well.
</Banner>
### Config
## Config
| Option | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -55,7 +56,7 @@ Right now, Payload is officially supporting two rich text editors:
_\* An asterisk denotes that a property is required._
### Admin config
## Admin config
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Rich Text editor allows for the following admin properties:
@@ -71,6 +72,6 @@ Set this property to `true` to hide this field's gutter within the admin panel.
Override the default text direction of the Admin panel for this field. Set to `true` to force right-to-left text direction.
### Editor-specific options
## Editor-specific options
For a ton more editor-specific options, including how to build custom rich text elements directly into your editor, take a look at either the [Slate docs](/docs/rich-text/slate) or the [Lexical docs](/docs/rich-text/lexical) depending on which editor you're using.

View File

@@ -18,7 +18,7 @@ keywords: row, fields, config, configuration, documentation, Content Management
caption="Admin panel screenshot of a Row field"
/>
### Config
## Config
| Option | Description |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -28,7 +28,7 @@ keywords: row, fields, config, configuration, documentation, Content Management
_\* An asterisk denotes that a property is required._
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -18,7 +18,7 @@ keywords: select, multi-select, fields, config, configuration, documentation, Co
caption="Admin panel screenshot of a Select field"
/>
### Config
## Config
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -52,7 +52,7 @@ _\* An asterisk denotes that a property is required._
being used as a GraphQL enum.
</Banner>
### Admin config
## Admin config
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Select field type also allows for the following admin-specific properties:
@@ -64,7 +64,7 @@ Set to `true` if you'd like this field to be clearable within the Admin UI.
Set to `true` if you'd like this field to be sortable within the Admin UI using drag and drop. (Only works when `hasMany` is set to `true`)
### Example
## Example
`collections/ExampleCollection.ts`
@@ -101,7 +101,7 @@ export const ExampleCollection: CollectionConfig = {
}
```
### Customization
## Customization
The Select field UI component can be customized by providing a custom React component to the `components` object in the Base config.

View File

@@ -19,7 +19,7 @@ keywords: tabs, fields, config, configuration, documentation, Content Management
caption="Tabs field type used to separate Hero fields from Page Layout"
/>
### Config
## Config
| Option | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------ |
@@ -27,7 +27,7 @@ keywords: tabs, fields, config, configuration, documentation, Content Management
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-config) for more details. |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
#### Tab-specific Config
### Tab-specific Config
Each tab must have either a `name` or `label` and the required `fields` array. You can also optionally pass a `description` to render within each individual tab.
@@ -41,7 +41,7 @@ Each tab must have either a `name` or `label` and the required `fields` array. Y
_\* An asterisk denotes that a property is required._
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -18,7 +18,7 @@ keywords: text, fields, config, configuration, documentation, Content Management
caption="Admin panel screenshot of a Text field and read-only Text field"
/>
### Config
## Config
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -44,7 +44,7 @@ keywords: text, fields, config, configuration, documentation, Content Management
_\* An asterisk denotes that a property is required._
### Admin config
## Admin config
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Text field type allows for the following `admin` properties:
@@ -60,7 +60,7 @@ Set this property to a string that will be used for browser autocomplete.
Override the default text direction of the Admin panel for this field. Set to `true` to force right-to-left text direction.
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -18,7 +18,7 @@ keywords: textarea, fields, config, configuration, documentation, Content Manage
caption="Admin panel screenshot of a Textarea field and read-only Textarea field"
/>
### Config
## Config
| Option | Description |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -41,7 +41,7 @@ keywords: textarea, fields, config, configuration, documentation, Content Manage
_\* An asterisk denotes that a property is required._
### Admin config
## Admin config
In addition to the default [field admin config](/docs/fields/overview#admin-config), the Textarea field type allows for the following `admin` properties:
@@ -57,7 +57,7 @@ Set this property to a string that will be used for browser autocomplete.
Override the default text direction of the Admin panel for this field. Set to `true` to force right-to-left text direction.
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -23,7 +23,7 @@ With this field, you can also inject custom `Cell` components that appear as add
- Add a "view page" button into a Pages List view to give editors a shortcut to view a page on the frontend of the site
- Build a "clear cache" button or similar mechanism to manually clear caches of specific documents
### Config
## Config
| Option | Description |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
@@ -31,11 +31,12 @@ With this field, you can also inject custom `Cell` components that appear as add
| **`label`** | Human-readable label for this UI field. |
| **`admin.components.Field`** \* | React component to be rendered for this field within the Edit view. [More](/docs/admin/components/#field-component) |
| **`admin.components.Cell`** | React component to be rendered as a Cell within collection List views. [More](/docs/admin/components/#field-component) |
| **`admin.disableListColumn`** | Set `disableListColumn` to `true` to prevent the UI field from appearing in the list view column selector. |
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
_\* An asterisk denotes that a property is required._
### Example
## Example
`collections/ExampleCollection.ts`

View File

@@ -32,7 +32,7 @@ keywords: upload, images media, fields, config, configuration, documentation, Co
- To allow for a `Product` to deliver a downloadable asset like PDF or MP3
- To give a layout building block the ability to feature a background image
### Config
## Config
| Option | Description |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -56,7 +56,7 @@ keywords: upload, images media, fields, config, configuration, documentation, Co
_\* An asterisk denotes that a property is required._
### Example
## Example
`collections/ExampleCollection.ts`
@@ -76,7 +76,7 @@ export const ExampleCollection: CollectionConfig = {
}
```
### Filtering upload options
## Filtering upload options
Options can be dynamically limited by supplying a [query constraint](/docs/queries/overview), which will be used both
for validating input and filtering available uploads in the UI.
@@ -93,7 +93,7 @@ called with an argument object with the following properties:
| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation |
| `user` | An object containing the currently authenticated user |
### Example
## Example
```ts
const uploadField = {

View File

@@ -8,13 +8,13 @@ keywords: documentation, getting started, guide, Content Management System, cms,
Payload is based around a small and intuitive set of concepts. Before starting to work with Payload, it's a good idea to familiarize yourself with the following:
### Config
## Config
<Banner type="info">The Payload config is where you configure everything that Payload does.</Banner>
By default, the Payload config lives in the root folder of your code and is named `payload.config.js` (`payload.config.ts` if you're using TypeScript), but you can customize its name and where you store it. You can write full functions and even full React components right into your config.
### Collections
## Collections
<Banner type="info">
A Collection represents a type of content that Payload will store and can contain many documents.
@@ -24,7 +24,7 @@ Collections define the shape of your data as well as all functionalities attache
They can represent anything you can store in a database - for example - pages, posts, users, people, orders, categories, events, customers, transactions, and anything else your app needs.
### Globals
## Globals
<Banner type="info">
A Global is a "one-off" piece of content that is perfect for storing navigational structures,
@@ -33,7 +33,7 @@ They can represent anything you can store in a database - for example - pages, p
Globals are in many ways similar to Collections, but there is only ever **one** instance of a Global, whereas Collections can contain many documents.
### Fields
## Fields
<Banner type="info">
Fields are the building blocks of Payload. Collections and Globals both use Fields to define the
@@ -42,7 +42,7 @@ Globals are in many ways similar to Collections, but there is only ever **one**
Payload comes with [many different field types](../fields/overview) that give you a ton of flexibility while designing your API. Each Field type has its own potential properties that allow you to customize how they work.
### Hooks
## Hooks
<Banner type="info">
Hooks are where you can "tie in" to existing Payload actions to perform your own additional logic
@@ -53,7 +53,7 @@ Hooks are an extremely powerful concept and are central to extending and customi
There are many more potential reasons to use Hooks. For more, visit the [Hooks documentation](/docs/hooks/overview).
### Access Control
## Access Control
<Banner type="info">
Access Control refers to Payload's system of defining who can do what to your API.
@@ -63,7 +63,7 @@ Access Control is extremely powerful but easy and intuitive to manage. You can e
For more, visit the [Access Control documentation](/docs/access-control/overview).
### Depth
## Depth
<Banner type="info">
"Depth" gives you control over how many levels down related documents should be automatically
@@ -156,6 +156,28 @@ To populate `user.author.department` in it's entirety you could specify `?depth=
}
```
### Field-level max depth
Fields like relationships or uploads can have a `maxDepth` property that limits the depth of the population for that field. Here are some examples:
Depth: 10
Current depth when field is accessed: 1
`maxDepth`: undefined
In this case, the field would be populated to 9 levels of population.
Depth: 10
Current depth when field is accessed: 0
`maxDepth`: 2
In this case, the field would be populated to 2 levels of population, despite there being a remaining depth of 8.
Depth: 10
Current depth when field is accessed: 2
`maxDepth`: 1
In this case, the field would not be populated, as the current depth (2) has exceeded the `maxDepth` for this field (1).
<Banner type="warning">
<strong>Note:</strong>
<br />

View File

@@ -6,7 +6,7 @@ desc: To quickly get started with Payload, simply run npx create-payload-app or
keywords: documentation, getting started, guide, Content Management System, cms, headless, javascript, node, react, express
---
#### Software Requirements
## Software Requirements
Payload requires the following software:
@@ -48,7 +48,7 @@ Write the above code into your newly created config file. This baseline config w
Although this is just the bare minimum config, there are _many_ more options that you can control here. To reference the full config and all of its options, [click here](/docs/configuration/overview).
### Server
## Server
Now that you've got a baseline Payload config, it's time to initialize Payload. It requires an Express server that you provide, so if you're not familiar with how to set up a baseline Express server, please read up on exactly what Express is and why to use it. Express' own [Documentation](https://expressjs.com/en/starter/hello-world.html) is a good place to start. Otherwise, follow along below for how to build your own Express server to use with Payload.
@@ -100,54 +100,54 @@ PAYLOAD_SECRET=your-payload-secret
Here is a list of all properties available to pass through `payload.init`:
##### secret
#### secret
**Required**. This is a secure string that will be used to authenticate with Payload. It can be random but should be at least 14 characters and be very difficult to guess.
Payload uses this secret key to generate secure user tokens (JWT). Behind the scenes, we do not use your secret key to encrypt directly - instead, we first take the secret key and create an encrypted string using the SHA-256 hash function. Then, we reduce the encrypted string to its first 32 characters. This final value is what Payload uses for encryption.
##### config
#### config
Allows you to pass your config directly to the onInit function. The config passed here should match the payload.config file.
##### disableOnInit
#### disableOnInit
A boolean that disables running your `onInit` function when Payload starts up.
##### disableDBConnect
#### disableDBConnect
A boolean that disables the database connection when Payload starts up.
##### email
#### email
An object used to configure SMTP. [Read more](/docs/email/overview).
##### express
#### express
This is your Express app as shown above. Payload will tie into your existing `app` and scope all of its functionalities to sub-routers. By default, Payload will add an `/admin` router and an `/api` router, but you can customize these paths.
##### local
#### local
A boolean that when set to `true` tells Payload to start in local-only mode which will bypass setting up API routes. When set to `true`, `express` is not required. This is useful when running scripts that need to use Payload's [local-api](/docs/local-api/overview).
##### loggerDestination
#### loggerDestination
Specify destination stream for the built-in Pino logger that Payload uses for internal logging. See [Pino Docs](https://getpino.io/#/docs/api?id=pino-destination) for more info on what is available.
##### loggerOptions
#### loggerOptions
Specify options for the built-in Pino logger that Payload uses for internal logging. See [Pino Docs](https://getpino.io/#/docs/api?id=options) for more info on what is available.
##### onInit
#### onInit
A function that is called immediately following startup that receives the Payload instance as it's only argument.
### Test it out
## Test it out
After you've gotten this far, it's time to boot up Payload. Start your project in your application's folder to get going.
After it starts, you can go to `http://localhost:3000/admin` to create your first Payload user!
### Docker
## Docker
Looking to deploy Payload with Docker? New projects with `create-payload-app` come with a Dockerfile and docker-compose.yml file ready to go. Examples of these files can be seen in our [Deployment docs](/docs/production/deployment#docker).

View File

@@ -38,7 +38,7 @@ In this way, the CMS can ensure that its content editing experience is highly po
At this point this concept is [widely](https://en.wikipedia.org/wiki/Headless_content_management_system) [discussed](https://css-tricks.com/what-is-a-headless-cms/) online, and for good reason. The web has become more complicated and with complexity comes the demand for developers to better structure their code. The rise of interface libraries like React and Vue are now the de-facto standard for building modern applications and traditional content management systems are often not designed to make use of them.
### Why Payload?
## Why Payload?
The team behind Payload has been building websites and apps with existing content management systems and application frameworks for over a decade. We know what works and what doesn't about each of the existing solutions, and to this day have found no silver bullet solution.

View File

@@ -25,11 +25,11 @@ This is Payload's GraphQL dependency. You should not install your own copy of Gr
This is a copy of the currently running Payload instance, which provides you with existing GraphQL types for all of your Collections and Globals - among other things.
##### Return value
## Return value
Both `graphQL.queries` and `graphQL.mutations` functions should return an object with properties equal to your newly written GraphQL queries and mutations.
### Example
## Example
`payload.config.js`:
@@ -68,7 +68,7 @@ export default buildConfig({
})
```
### Resolver function
## Resolver function
In your resolver, make sure you set `depth: 0` if you're returning data directly from the local API so that GraphQL can correctly resolve queries to nested values such as relationship data.
@@ -96,7 +96,7 @@ An object containing the `req` and `res` objects that will provide you with the
Contextual information about the currently running GraphQL operation. You can get schema information from this as well as contextual information about where this resolver function is being run.
### Types
## Types
We've exposed a few types and utilities to help you extend the API further. Payload uses the GraphQL.js package for which you can view the full list of available types in the [official documentation](https://graphql.org/graphql-js/type/).
@@ -138,7 +138,7 @@ graphQL?: {
}
```
### Best practices
## Best practices
There are a few ways to structure your code, we recommend using a dedicated `graphql` directory so you can keep all of your logic in one place. You have total freedom of how you want to structure this but a common pattern is to group functions by type and with their resolver.

View File

@@ -8,7 +8,7 @@ keywords: headless cms, typescript, documentation, Content Management System, cm
When working with GraphQL it is useful to have the schema for development of other projects that need to call on your GraphQL endpoint. In Payload the schema is controlled by your collections and globals and is made available to the developer or third parties, it is not necessary for developers using Payload to write schema types manually.
### Schema generation script
## Schema generation script
Run the following command in a Payload project to generate your project's GraphQL schema from Payload:
@@ -16,7 +16,7 @@ Run the following command in a Payload project to generate your project's GraphQ
payload generate:graphQLSchema
```
### Custom Field Schemas
## Custom Field Schemas
For `array`, `block`, `group` and named `tab` fields, you can generate top level reusable interfaces. The following group field config:
@@ -56,7 +56,7 @@ type Collection1 {
The above example outputs all your definitions to a file relative from your payload config as `./graphql/schema.graphql`. By default, the file will be output to your current working directory as `schema.graphql`.
#### Adding an NPM script
### Adding an NPM script
<Banner type="warning">
<strong>Important</strong>

View File

@@ -42,11 +42,12 @@ export const PublicUser: CollectionConfig = {
**Payload will automatically open up the following queries:**
| Query Name | Operation |
| ------------------ | ------------------- |
| **`PublicUser`** | `findByID` |
| **`PublicUsers`** | `find` |
| **`mePublicUser`** | `me` auth operation |
| Query Name | Operation |
| ------------------ | ------------------- |
| **`PublicUser`** | `findByID` |
| **`PublicUsers`** | `find` |
| **`countPublicUsers`** | `count` |
| **`mePublicUser`** | `me` auth operation |
**And the following mutations:**

View File

@@ -28,13 +28,13 @@ Example uses:
There are many more use cases for Hooks and the sky is the limit.
#### Async vs. synchronous
## Async vs. synchronous
All hooks can be written as either synchronous or asynchronous functions. If the Hook should modify data before a document is updated or created, and it relies on asynchronous actions such as fetching data from a third party, it might make sense to define your Hook as an asynchronous function, so you can be sure that your Hook completes before the operation's lifecycle continues. Async hooks are run in series - so if you have two async hooks defined, the second hook will wait for the first to complete before it starts.
If your Hook simply performs a side-effect, such as updating a CRM, it might be okay to define it synchronously, so the Payload operation does not have to wait for your hook to complete.
#### Server-only execution
## Server-only execution
Payload Hooks are only triggered on the server. You can safely [remove your hooks](/docs/admin/webpack#aliasing-server-only-modules) from your Admin panel's client-side code by customizing the Webpack config, which not only keeps your Admin bundles' filesize small but also ensures that any server-side only code does not cause problems within browser environments.

View File

@@ -1,30 +1,30 @@
---
title: Vercel Visual Editing
label: Vercel Visual Editing
title: Vercel Content Link
label: Vercel Content Link
order: 10
desc: Payload + Vercel Visual Editing allows yours editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it.
keywords: vercel, vercel visual editing, visual editing, content source maps, Content Management System, cms, headless, javascript, node, react, express
desc: Payload + Vercel Content Link allows yours editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it.
keywords: vercel, vercel content link, visual editing, content source maps, Content Management System, cms, headless, javascript, node, react, express
---
[Vercel Visual Editing](https://vercel.com/docs/workflow-collaboration/visual-editing) will allow your editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it. This requires no changes to your front-end code and very few changes to your Payload config.
[Vercel Content Link](https://vercel.com/docs/workflow-collaboration/edit-mode#content-link) will allow your editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it. This requires no changes to your front-end code and very few changes to your Payload config.
![Versions](/images/docs/vercel-visual-editing.jpg)
<Banner type="warning">
Vercel Visual Editing is an enterprise-only feature and only available for deployments hosted on
Vercel Content Link is an enterprise-only feature and only available for deployments hosted on
Vercel. If you are an existing enterprise customer, [contact our sales
team](https://payloadcms.com/for-enterprise) for help with your integration.
</Banner>
### How it works
## How it works
To power Vercel Visual Editing, Payload embeds Content Source Maps into its API responses. Content Source Maps are invisible, encoded JSON values that include a link back to the field in the CMS that generated the content. When rendered on the page, Vercel detects and decodes these values to display the Visual Editing interface.
To power Vercel Content Link, Payload embeds Content Source Maps into its API responses. Content Source Maps are invisible, encoded JSON values that include a link back to the field in the CMS that generated the content. When rendered on the page, Vercel detects and decodes these values to display the Content Link interface.
For full details on how the encoding and decoding algorithm works, check out [`@vercel/stega`](https://www.npmjs.com/package/@vercel/stega).
### Getting Started
## Getting Started
Setting up Payload with Vercel Visual Editing is easy. First, install the `@payloadcms/plugin-csm` plugin into your project. This plugin requires an API key to install, [contact our sales team](https://payloadcms.com/for-enterprise) if you don't already have one.
Setting up Payload with Vercel Content Link is easy. First, install the `@payloadcms/plugin-csm` plugin into your project. This plugin requires an API key to install, [contact our sales team](https://payloadcms.com/for-enterprise) if you don't already have one.
```bash
npm i @payloadcms/plugin-csm
@@ -74,15 +74,15 @@ if (isDraftMode || process.env.VERCEL_ENV === 'preview') {
And that's it! You are now ready to enter Edit Mode and begin visually editing your content.
##### Edit Mode
#### Edit Mode
To see Visual Editing on your site, you first need to visit any preview deployment on Vercel and login using the Vercel Toolbar. When Content Source Maps are detected on the page, a pencil icon will appear in the toolbar. Clicking this icon will enable Edit Mode, highlighting all editable fields on the page in blue.
To see Content Link on your site, you first need to visit any preview deployment on Vercel and login using the Vercel Toolbar. When Content Source Maps are detected on the page, a pencil icon will appear in the toolbar. Clicking this icon will enable Edit Mode, highlighting all editable fields on the page in blue.
![Versions](/images/docs/vercel-toolbar.jpg)
### Troubleshooting
## Troubleshooting
##### Dates
### Date Fields
The plugin does not encode `date` fields by default, but for some cases like text that uses negative CSS letter-spacing, it may be necessary to split the encoded data out from the rendered text. This way you can safely use the cleaned data as expected.
@@ -91,9 +91,9 @@ import { vercelStegaSplit } from '@vercel/stega'
const { cleaned, encoded } = vercelStegaSplit(text)
```
##### Blocks
### Block fields
All `blocks` fields by definition do not have plain text strings to encode. For this reason, blocks are given an additional `encodedSourceMap` key, which you can use to enable Visual Editing on entire sections of your site. You can then specify the editing container by adding the `data-vercel-edit-target` HTML attribute to any top-level element of your block.
All `blocks` fields by definition do not have plain text strings to encode. For this reason, blocks are given an additional `encodedSourceMap` key, which you can use to enable Content Link on entire sections of your site. You can then specify the editing container by adding the `data-vercel-edit-target` HTML attribute to any top-level element of your block.
```ts
<div data-vercel-edit-target>

378
docs/lexical/converters.mdx Normal file
View File

@@ -0,0 +1,378 @@
---
title: Lexical Converters
label: Converters
order: 20
desc: Conversion between lexical, markdown and html
keywords: lexical, rich text, editor, headless cms, convert, html, mdx, markdown, md, conversion, export
---
## Lexical => HTML
Lexical saves data in JSON, but can also generate its HTML representation via two main methods:
1. **Outputting HTML from the Collection:** Create a new field in your collection to convert saved JSON content to HTML. Payload generates and outputs the HTML for use in your frontend.
2. **Generating HTML on any server** Convert JSON to HTML on-demand on the server.
The editor comes with built-in HTML serializers, simplifying the process of converting JSON to HTML.
### Outputting HTML from the Collection
To add HTML generation directly within the collection, follow the example below:
```ts
import type { CollectionConfig } from 'payload/types'
import { HTMLConverterFeature, lexicalEditor, lexicalHTML } from '@payloadcms/richtext-lexical'
const Pages: CollectionConfig = {
slug: 'pages',
fields: [
{
name: 'nameOfYourRichTextField',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
// The HTMLConverter Feature is the feature which manages the HTML serializers.
// If you do not pass any arguments to it, it will use the default serializers.
HTMLConverterFeature({}),
],
}),
},
lexicalHTML('nameOfYourRichTextField', { name: 'nameOfYourRichTextField_html' }),
],
}
```
The `lexicalHTML()` function creates a new field that automatically converts the referenced lexical richText field into HTML through an afterRead hook.
### Generating HTML anywhere on the server:
If you wish to convert JSON to HTML ad-hoc, use this code snippet:
```ts
import type { SerializedEditorState } from 'lexical'
import {
type SanitizedEditorConfig,
convertLexicalToHTML,
consolidateHTMLConverters,
} from '@payloadcms/richtext-lexical'
async function lexicalToHTML(
editorData: SerializedEditorState,
editorConfig: SanitizedEditorConfig,
) {
return await convertLexicalToHTML({
converters: consolidateHTMLConverters({ editorConfig }),
data: editorData,
})
}
```
This method employs `convertLexicalToHTML` from `@payloadcms/richtext-lexical`, which converts the serialized editor state into HTML.
Because every `Feature` is able to provide html converters, and because the `htmlFeature` can modify those or provide their own, we need to consolidate them with the default html Converters using the `consolidateHTMLConverters` function.
### CSS
Payload's lexical HTML converter does not generate CSS for you, but it does add classes to the generated HTML. You can use these classes to style the HTML in your frontend.
Here is some "base" CSS you can use to ensure that nested lists render correctly:
```css
/* Base CSS for Lexical HTML */
.nestedListItem, .list-check {
list-style-type: none;
}
```
### Creating your own HTML Converter
HTML Converters are typed as `HTMLConverter`, which contains the node type it should handle, and a function that accepts the serialized node from the lexical editor, and outputs the HTML string. Here's the HTML Converter of the Upload node as an example:
```ts
import type { HTMLConverter } from '@payloadcms/richtext-lexical'
const UploadHTMLConverter: HTMLConverter<SerializedUploadNode> = {
converter: async ({ node, payload }) => {
const uploadDocument = await payload.findByID({
id: node.value.id,
collection: node.relationTo,
})
const url = (payload?.config?.serverURL || '') + uploadDocument?.url
if (!(uploadDocument?.mimeType as string)?.startsWith('image')) {
// Only images can be serialized as HTML
return ``
}
return `<img src="${url}" alt="${uploadDocument?.filename}" width="${uploadDocument?.width}" height="${uploadDocument?.height}"/>`
},
nodeTypes: [UploadNode.getType()], // This is the type of the lexical node that this converter can handle. Instead of hardcoding 'upload' we can get the node type directly from the UploadNode, since it's static.
}
```
As you can see, we have access to all the information saved in the node (for the Upload node, this is `value`and `relationTo`) and we can use that to generate the HTML.
The `convertLexicalToHTML` is part of `@payloadcms/richtext-lexical` automatically handles traversing the editor state and calling the correct converter for each node.
### Embedding the HTML Converter in your Feature
You can embed your HTML Converter directly within your custom `ServerFeature`, allowing it to be handled automatically by the `consolidateHTMLConverters` function. Here is an example:
```ts
import { createNode } from '@payloadcms/richtext-lexical'
import type { FeatureProviderProviderServer } from '@payloadcms/richtext-lexical'
export const UploadFeature: FeatureProviderProviderServer<
UploadFeatureProps,
UploadFeaturePropsClient
> = (props) => {
/*...*/
return {
feature: () => {
return {
nodes: [
createNode({
converters: {
html: yourHTMLConverter, // <= This is where you define your HTML Converter
},
node: UploadNode,
//...
}),
],
ClientComponent: UploadFeatureClientComponent,
clientFeatureProps: clientProps,
serverFeatureProps: props,
/*...*/
}
},
key: 'upload',
serverFeatureProps: props,
}
}
```
## Headless Editor
Lexical provides a seamless way to perform conversions between various other formats:
- HTML to Lexical (or, importing HTML into the lexical editor)
- Markdown to Lexical (or, importing Markdown into the lexical editor)
- Lexical to Markdown
A headless editor can perform such conversions outside of the main editor instance. Follow this method to initiate a headless editor:
```ts
import { createHeadlessEditor } from '@lexical/headless' // <= make sure this package is installed
import { getEnabledNodes, sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'
const yourEditorConfig // <= your editor config here
const headlessEditor = createHeadlessEditor({
nodes: getEnabledNodes({
editorConfig: sanitizeServerEditorConfig(yourEditorConfig),
}),
})
```
### Getting the editor config
As you can see, you need to provide an editor config in order to create a headless editor. This is because the editor config is used to determine which nodes & features are enabled, and which converters are used.
To get the editor config, simply import the default editor config and adjust it - just like you did inside of the `editor: lexicalEditor({})` property:
```ts
import { defaultEditorConfig, defaultEditorFeatures } from '@payloadcms/richtext-lexical' // <= make sure this package is installed
const yourEditorConfig = defaultEditorConfig
// If you made changes to the features of the field's editor config, you should also make those changes here:
yourEditorConfig.features = [
...defaultEditorFeatures,
// Add your custom features here
]
```
### Getting the editor config from an existing field
If you have access to the sanitized collection config, you can get access to the lexical sanitized editor config & features, as every lexical richText field returns it. Here is an example how you can get it from another field's afterRead hook:
```ts
import type { CollectionConfig, RichTextField } from 'payload/types'
import { createHeadlessEditor } from '@lexical/headless'
import type { LexicalRichTextAdapter, SanitizedServerEditorConfig } from '@payloadcms/richtext-lexical'
import {
getEnabledNodes,
lexicalEditor
} from '@payloadcms/richtext-lexical'
export const MyCollection: CollectionConfig = {
slug: 'slug',
fields: [
{
name: 'text',
type: 'text',
hooks: {
afterRead: [
({ value, collection }) => {
const otherRichTextField: RichTextField = collection.fields.find(
(field) => 'name' in field && field.name === 'richText',
) as RichTextField
const lexicalAdapter: LexicalRichTextAdapter =
otherRichTextField.editor as LexicalRichTextAdapter
const sanitizedServerEditorConfig: SanitizedServerEditorConfig =
lexicalAdapter.editorConfig
const headlessEditor = createHeadlessEditor({
nodes: getEnabledNodes({
editorConfig: sanitizedServerEditorConfig,
}),
})
// Do whatever you want with the headless editor
return value
},
],
},
},
{
name: 'richText',
type: 'richText',
editor: lexicalEditor({
features,
}),
}
]
}
```
## HTML => Lexical
Once you have your headless editor instance, you can use it to convert HTML to Lexical:
```ts
import { $generateNodesFromDOM } from '@lexical/html'
import { $getRoot, $getSelection } from 'lexical'
import { JSDOM } from 'jsdom'
headlessEditor.update(
() => {
// In a headless environment you can use a package such as JSDom to parse the HTML string.
const dom = new JSDOM(htmlString)
// Once you have the DOM instance it's easy to generate LexicalNodes.
const nodes = $generateNodesFromDOM(headlessEditor, dom.window.document)
// Select the root
$getRoot().select()
// Insert them at a selection.
const selection = $getSelection()
selection.insertNodes(nodes)
},
{ discrete: true },
)
// Do this if you then want to get the editor JSON
const editorJSON = headlessEditor.getEditorState().toJSON()
```
Functions prefixed with a `$` can only be run inside an `editor.update()` or `editorState.read()` callback.
This has been taken from the [lexical serialization & deserialization docs](https://lexical.dev/docs/concepts/serialization#html---lexical).
<Banner type="success">
<strong>Note:</strong>
<br />
Using the <code>discrete: true</code> flag ensures instant updates to the editor state. If
immediate reading of the updated state isn't necessary, you can omit the flag.
</Banner>
## Markdown => Lexical
Convert markdown content to the Lexical editor format with the following:
```ts
import { $convertFromMarkdownString } from '@lexical/markdown'
import { sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'
const yourSanitizedEditorConfig = sanitizeServerEditorConfig(yourEditorConfig) // <= your editor config here
const markdown = `# Hello World`
headlessEditor.update(
() => {
$convertFromMarkdownString(markdown, yourSanitizedEditorConfig.features.markdownTransformers)
},
{ discrete: true },
)
// Do this if you then want to get the editor JSON
const editorJSON = headlessEditor.getEditorState().toJSON()
```
## Lexical => Markdown
Export content from the Lexical editor into Markdown format using these steps:
1. Import your current editor state into the headless editor.
2. Convert and fetch the resulting markdown string.
Here's the code for it:
```ts
import { $convertToMarkdownString } from '@lexical/markdown'
import { sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'
import type { SerializedEditorState } from 'lexical'
const yourSanitizedEditorConfig = sanitizeServerEditorConfig(yourEditorConfig) // <= your editor config here
const yourEditorState: SerializedEditorState // <= your current editor state here
// Import editor state into your headless editor
try {
headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState)) // This should commit the editor state immediately
} catch (e) {
logger.error({ err: e }, 'ERROR parsing editor state')
}
// Export to markdown
let markdown: string
headlessEditor.getEditorState().read(() => {
markdown = $convertToMarkdownString(yourSanitizedEditorConfig?.features?.markdownTransformers)
})
```
The `.setEditorState()` function immediately updates your editor state. Thus, there's no need for the `discrete: true` flag when reading the state afterward.
## Lexical => Plain Text
Export content from the Lexical editor into plain text using these steps:
1. Import your current editor state into the headless editor.
2. Convert and fetch the resulting plain text string.
Here's the code for it:
```ts
import type { SerializedEditorState } from 'lexical'
import { $getRoot } from 'lexical'
const yourEditorState: SerializedEditorState // <= your current editor state here
// Import editor state into your headless editor
try {
headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState)) // This should commit the editor state immediately
} catch (e) {
logger.error({ err: e }, 'ERROR parsing editor state')
}
// Export to plain text
const plainTextContent =
headlessEditor.getEditorState().read(() => {
return $getRoot().getTextContent()
}) || ''
```

182
docs/lexical/migration.mdx Normal file
View File

@@ -0,0 +1,182 @@
---
title: Lexical Migration
label: Migration
order: 30
desc: Migration from slate and payload-plugin-lexical to lexical
keywords: lexical, rich text, editor, headless cms, migrate, migration
---
## Migrating from Slate
While both Slate and Lexical save the editor state in JSON, the structure of the JSON is different.
### Migration via SlateToLexicalFeature
One way to handle this is to just give your lexical editor the ability to read the slate JSON.
Simply add the `SlateToLexicalFeature` to your editor:
```ts
import type { CollectionConfig } from 'payload/types'
import { SlateToLexicalFeature, lexicalEditor } from '@payloadcms/richtext-lexical'
const Pages: CollectionConfig = {
slug: 'pages',
fields: [
{
name: 'nameOfYourRichTextField',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [...defaultFeatures, SlateToLexicalFeature({})],
}),
},
],
}
```
and done! Now, everytime this lexical editor is initialized, it converts the slate date to lexical on-the-fly. If the data is already in lexical format, it will just pass it through.
This is by far the easiest way to migrate from Slate to Lexical, although it does come with a few caveats:
- There is a performance hit when initializing the lexical editor
- The editor will still output the Slate data in the output JSON, as the on-the-fly converter only runs for the admin panel
The easy way to solve this: Just save the document! This overrides the slate data with the lexical data, and the next time the document is loaded, the lexical data will be used. This solves both the performance and the output issue for that specific document.
### Migration via migration script
The method described above does not solve the issue for all documents, though. If you want to convert all your documents to lexical, you can use a migration script. Here's a simple example:
```ts
import type { Payload } from 'payload'
import type { YourDocumentType } from 'payload/generated-types'
import {
cloneDeep,
convertSlateToLexical,
defaultSlateConverters,
} from '@payloadcms/richtext-lexical'
import { AnotherCustomConverter } from './lexicalFeatures/converters/AnotherCustomConverter'
export async function convertAll(payload: Payload, collectionName: string, fieldName: string) {
const docs: YourDocumentType[] = await payload.db.collections[collectionName].find({}).exec() // Use MongoDB models directly to query all documents at once
console.log(`Found ${docs.length} ${collectionName} docs`)
const converters = cloneDeep([...defaultSlateConverters, AnotherCustomConverter])
// Split docs into batches of 20.
const batchSize = 20
const batches = []
for (let i = 0; i < docs.length; i += batchSize) {
batches.push(docs.slice(i, i + batchSize))
}
let processed = 0 // Number of processed docs
for (const batch of batches) {
// Process each batch asynchronously
const promises = batch.map(async (doc: YourDocumentType) => {
const richText = doc[fieldName]
if (richText && Array.isArray(richText) && !('root' in richText)) {
// It's Slate data - skip already-converted data
const converted = convertSlateToLexical({
converters: converters,
slateData: richText,
})
await payload.update({
id: doc.id,
collection: collectionName as any,
depth: 0, // performance optimization. No need to run population.
data: {
[fieldName]: converted,
},
})
}
})
// Wait for all promises in the batch to complete. Resolving batches of 20 asynchronously is faster than waiting for each doc to update individually
await Promise.all(promises)
// Update the count of processed docs
processed += batch.length
console.log(`Converted ${processed} of ${docs.length}`)
}
}
```
The `convertSlateToLexical` is the same method used in the `SlateToLexicalFeature` - it handles traversing the Slate JSON for you.
Do note that this script might require adjustment depending on your document structure, especially if you have nested richText fields or localization enabled.
### Converting custom Slate nodes
If you have custom Slate nodes, create a custom converter for them. Here's the Upload converter as an example:
```ts
import type { SerializedUploadNode } from '../uploadNode.'
import type { SlateNodeConverter } from '@payloadcms/richtext-lexical'
export const SlateUploadConverter: SlateNodeConverter = {
converter({ slateNode }) {
return {
fields: {
...slateNode.fields,
},
format: '',
relationTo: slateNode.relationTo,
type: 'upload',
value: {
id: slateNode.value?.id || '',
},
version: 1,
} as const as SerializedUploadNode
},
nodeTypes: ['upload'],
}
```
It's pretty simple: You get a Slate node as input, and you return the lexical node. The `nodeTypes` array is used to determine which Slate nodes this converter can handle.
When using a migration script, you can add your custom converters to the `converters` property of the `convertSlateToLexical` props, as seen in the example above
When using the `SlateToLexicalFeature`, you can add your custom converters to the `converters` property of the `SlateToLexicalFeature` props:
```ts
import type { CollectionConfig } from 'payload/types'
import {
SlateToLexicalFeature,
lexicalEditor,
defaultSlateConverters,
} from '@payloadcms/richtext-lexical'
import { YourCustomConverter } from '../converters/YourCustomConverter'
const Pages: CollectionConfig = {
slug: 'pages',
fields: [
{
name: 'nameOfYourRichTextField',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
SlateToLexicalFeature({
converters: [...defaultSlateConverters, YourCustomConverter],
}),
],
}),
},
],
}
```
## Migrating from payload-plugin-lexical
Migrating from [payload-plugin-lexical](https://github.com/AlessioGr/payload-plugin-lexical) works similar to migrating from Slate.
Instead of a `SlateToLexicalFeature` there is a `LexicalPluginToLexicalFeature` you can use. And instead of `convertSlateToLexical` you can use `convertLexicalPluginToLexical`.

179
docs/lexical/overview.mdx Normal file
View File

@@ -0,0 +1,179 @@
---
title: Lexical Overview
label: Overview
order: 10
desc: Built by Meta, Lexical is an incredibly powerful rich text editor, and it works beautifully within Payload.
keywords: lexical, rich text, editor, headless cms
---
One of Payload's goals is to build the best rich text editor experience that we possibly can. We want to combine the beauty and polish of the Medium editing experience with the strength and features of the Notion editor - all in one place.
Classically, we've used SlateJS to work toward this goal, but building custom elements into Slate has proven to be more difficult than we'd like, and we've been keeping our options open.
<Banner type="warning">
Payload's Lexical rich text editor is currently in beta. It's stable enough to use as you build on
Payload, so if you're up for helping us fine-tune it, you should use it. But if you're looking for
stability, use Slate instead.
</Banner>
Lexical is extremely impressive and trivializes a lot of the hard parts of building new elements into a rich text editor. It has a few distinct advantages over Slate, including the following:
1. A "/" menu, which allows editors to easily add new elements while never leaving their keyboard
1. A "hover" toolbar that pops up if you select text
1. It supports Payload blocks natively, directly within your rich text editor
1. Custom elements, called "features", are much easier to build in Lexical vs. Slate
To use the Lexical editor, first you need to install it:
```
npm install @payloadcms/richtext-lexical
```
Once you have it installed, you can pass it to your top-level Payload config as follows:
```ts
import { buildConfig } from 'payload/config'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
export default buildConfig({
collections: [
// your collections here
],
// Pass the Lexical editor to the root config
editor: lexicalEditor({}),
})
```
You can also override Lexical settings on a field-by-field basis as follows:
```ts
import type { CollectionConfig } from 'payload/types'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
export const Pages: CollectionConfig = {
slug: 'pages',
fields: [
{
name: 'content',
type: 'richText',
// Pass the Lexical editor here and override base settings as necessary
editor: lexicalEditor({}),
},
],
}
```
## Extending the lexical editor with Features
Lexical has been designed with extensibility in mind. Whether you're aiming to introduce new functionalities or tweak the existing ones, Lexical makes it seamless for you to bring those changes to life.
### Features: The Building Blocks
At the heart of Lexical's customization potential are "features". While Lexical ships with a set of default features we believe are essential for most use cases, the true power lies in your ability to redefine, expand, or prune these as needed.
If you remove all the default features, you're left with a blank editor. You can then add in only the features you need, or you can build your own custom features from scratch.
### Integrating New Features
To weave in your custom features, utilize the `features` prop when initializing the Lexical Editor. Here's a basic example of how this is done:
```ts
import {
BlocksFeature,
LinkFeature,
UploadFeature,
lexicalEditor,
} from '@payloadcms/richtext-lexical'
import { Banner } from '../blocks/Banner'
import { CallToAction } from '../blocks/CallToAction'
{
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
LinkFeature({
// Example showing how to customize the built-in fields
// of the Link feature
fields: ({ defaultFields }) => [
...defaultFields,
{
name: 'rel',
label: 'Rel Attribute',
type: 'select',
hasMany: true,
options: ['noopener', 'noreferrer', 'nofollow'],
admin: {
description:
'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
},
},
],
}),
UploadFeature({
collections: {
uploads: {
// Example showing how to customize the built-in fields
// of the Upload feature
fields: [
{
name: 'caption',
type: 'richText',
editor: lexicalEditor(),
},
],
},
},
}),
// This is incredibly powerful. You can re-use your Payload blocks
// directly in the Lexical editor as follows:
BlocksFeature({
blocks: [Banner, CallToAction],
}),
],
})
}
```
## Features overview
Here's an overview of all the included features:
| Feature Name | Included by default | Description |
|--------------------------------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`BoldTextFeature`** | Yes | Handles the bold text format |
| **`ItalicTextFeature`** | Yes | Handles the italic text format |
| **`UnderlineTextFeature`** | Yes | Handles the underline text format |
| **`StrikethroughTextFeature`** | Yes | Handles the strikethrough text format |
| **`SubscriptTextFeature`** | Yes | Handles the subscript text format |
| **`SuperscriptTextFeature`** | Yes | Handles the superscript text format |
| **`InlineCodeTextFeature`** | Yes | Handles the inline-code text format |
| **`ParagraphFeature`** | Yes | Handles paragraphs. Since they are already a key feature of lexical itself, this Feature mainly handles the Slash and Add-Block menu entries for paragraphs |
| **`HeadingFeature`** | Yes | Adds Heading Nodes (by default, H1 - H6, but that can be customized) |
| **`AlignFeature`** | Yes | Allows you to align text left, centered and right |
| **`IndentFeature`** | Yes | Allows you to indent text with the tab key |
| **`UnorderedListFeature`** | Yes | Adds unordered lists (ul) |
| **`OrderedListFeature`** | Yes | Adds ordered lists (ol) |
| **`CheckListFeature`** | Yes | Adds checklists |
| **`LinkFeature`** | Yes | Allows you to create internal and external links |
| **`RelationshipFeature`** | Yes | Allows you to create block-level (not inline) relationships to other documents |
| **`BlockQuoteFeature`** | Yes | Allows you to create block-level quotes |
| **`UploadFeature`** | Yes | Allows you to create block-level upload nodes - this supports all kinds of uploads, not just images |
| **`HorizontalRuleFeature`** | Yes | Horizontal rules / separators. Basically displays an `<hr>` element |
| **`InlineToolbarFeature`** | Yes | The inline toolbar is the floating toolbar which appears when you select text. This toolbar only contains actions relevant for selected text |
| **`FixedToolbarFeature`** | No | This classic toolbar is pinned to the top and always visible. Both inline and fixed toolbars can be enabled at the same time. |
| **`BlocksFeature`** | No | Allows you to use Payload's [Blocks Field](/docs/fields/blocks) directly inside your editor. In the feature props, you can specify the allowed blocks - just like in the Blocks field. |
| **`TreeViewFeature`** | No | Adds a debug box under the editor, which allows you to see the current editor state live, the dom, as well as time travel. Very useful for debugging |
Notice how even the toolbars are features? That's how extensible our lexical editor is - you could theoretically create your own toolbar if you wanted to!
## Creating your own, custom Feature
Creating your own custom feature requires deep knowledge of the Lexical editor. We recommend you take a look at the [Lexical documentation](https://lexical.dev/docs/intro) first - especially the "concepts" section.
Next, take a look at the [features we've already built](https://github.com/payloadcms/payload/tree/main/packages/richtext-lexical/src/field/features) - understanding how they work will help you understand how to create your own. There is no difference between the features included by default and the ones you create yourself - since those features are all isolated from the "core", you have access to the same APIs, whether the feature is part of payload or not!
## Coming Soon
Lots more documentation will be coming soon, which will show in detail how to create your own custom features within Lexical.
For now, take a look at the TypeScript interfaces and let us know if you need a hand. Much more will be coming from the Payload team on this topic soon.

View File

@@ -0,0 +1,292 @@
---
title: Client-side Live Preview
label: Client-side
order: 40
desc: Learn how to implement Live Preview in your client-side front-end application.
keywords: live preview, frontend, react, next.js, vue, nuxt.js, svelte, hook, useLivePreview
---
<Banner type="info">
If your front-end application supports Server Components like the [Next.js App Router](https://nextjs.org/docs/app), etc., we suggest setting up [server-side Live Preview](./server).
</Banner>
While using Live Preview, the Admin panel emits a new `window.postMessage` event every time your document has changed. Your front-end application can listen for these events and re-render accordingly.
If your front-end application is built with [React](#react) or [Vue](#vue), use the `useLivePreview` hooks that Payload provides. In the future, all other major frameworks like Svelte will be officially supported. If you are using any of these frameworks today, you can still integrate with Live Preview yourself using the underlying tooling that Payload provides. See [building your own hook](#building-your-own-hook) for more information.
By default, all hooks accept the following args:
| Path | Description |
| ------------------ | -------------------------------------------------------------------------------------- |
| **`serverURL`** \* | The URL of your Payload server. |
| **`initialData`** | The initial data of the document. The live data will be merged in as changes are made. |
| **`depth`** | The depth of the relationships to fetch. Defaults to `0`. |
| **`apiRoute`** | The path of your API route as defined in `routes.api`. Defaults to `/api`. |
_\* An asterisk denotes that a property is required._
And return the following values:
| Path | Description |
| --------------- | ---------------------------------------------------------------- |
| **`data`** | The live data of the document, merged with the initial data. |
| **`isLoading`** | A boolean that indicates whether or not the document is loading. |
<Banner type="info">
If your front-end is tightly coupled to required fields, you should ensure that your UI does not
break when these fields are removed. For example, if you are rendering something like
`data.relatedPosts[0].title`, your page will break once you remove the first related post. To get
around this, use conditional logic, optional chaining, or default values in your UI where needed.
For example, `data?.relatedPosts?.[0]?.title`.
</Banner>
<Banner type="info">
It is important that the `depth` argument matches exactly with the depth of your initial page request. The depth property is used to populated relationships and uploads beyond their IDs. See [Depth](../getting-started/concepts#depth) for more information.
</Banner>
## React
If your front-end application is built with client-side [React](https://react.dev) like [Next.js Pages Router](https://nextjs.org/docs/pages), you can use the `useLivePreview` hook that Payload provides.
First, install the `@payloadcms/live-preview-react` package:
```bash
npm install @payloadcms/live-preview-react
```
Then, use the `useLivePreview` hook in your React component:
```tsx
'use client'
import { useLivePreview } from '@payloadcms/live-preview-react'
import { Page as PageType } from '@/payload-types'
// Fetch the page in a server component, pass it to the client component, then thread it through the hook
// The hook will take over from there and keep the preview in sync with the changes you make
// The `data` property will contain the live data of the document
export const PageClient: React.FC<{
page: {
title: string
}
}> = ({ page: initialPage }) => {
const { data } = useLivePreview<PageType>({
initialData: initialPage,
serverURL: PAYLOAD_SERVER_URL,
depth: 2,
})
return <h1>{data.title}</h1>
}
```
## Vue
If your front-end application is built with [Vue 3](https://vuejs.org) or [Nuxt 3](https://nuxt.js), you can use the `useLivePreview` composable that Payload provides.
First, install the `@payloadcms/live-preview-vue` package:
```bash
npm install @payloadcms/live-preview-vue
```
Then, use the `useLivePreview` hook in your Vue component:
```vue
<script setup lang="ts">
import type { PageData } from '~/types';
import { defineProps } from 'vue';
import { useLivePreview } from '@payloadcms/live-preview-vue';
// Fetch the initial data on the parent component or using async state
const props = defineProps<{ initialData: PageData }>();
// The hook will take over from here and keep the preview in sync with the changes you make.
// The `data` property will contain the live data of the document only when viewed from the Preview view of the Admin UI.
const { data } = useLivePreview<PageData>({
initialData: props.initialData,
serverURL: "<PAYLOAD_SERVER_URL>",
depth: 2,
});
</script>
<template>
<h1>{{ data.title }}</h1>
</template>
```
## Building your own hook
No matter what front-end framework you are using, you can build your own hook using the same underlying tooling that Payload provides.
First, install the base `@payloadcms/live-preview` package:
```bash
npm install @payloadcms/live-preview
```
This package provides the following functions:
| Path | Description |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| **`subscribe`** | Subscribes to the Admin panel's `window.postMessage` events and calls the provided callback function. |
| **`unsubscribe`** | Unsubscribes from the Admin panel's `window.postMessage` events. |
| **`ready`** | Sends a `window.postMessage` event to the Admin panel to indicate that the front-end is ready to receive messages. |
| **`isLivePreviewEvent`** | Checks if a `MessageEvent` originates from the Admin panel and is a Live Preview event, i.e. debounced form state. |
The `subscribe` function takes the following args:
| Path | Description |
| ------------------ | ------------------------------------------------------------------------------------------- |
| **`callback`** \* | A callback function that is called with `data` every time a change is made to the document. |
| **`serverURL`** \* | The URL of your Payload server. |
| **`initialData`** | The initial data of the document. The live data will be merged in as changes are made. |
| **`depth`** | The depth of the relationships to fetch. Defaults to `0`. |
With these functions, you can build your own hook using your front-end framework of choice:
```tsx
import { subscribe, unsubscribe } from '@payloadcms/live-preview'
// To build your own hook, subscribe to Live Preview events using the `subscribe` function
// It handles everything from:
// 1. Listening to `window.postMessage` events
// 2. Merging initial data with active form state
// 3. Populating relationships and uploads
// 4. Calling the `onChange` callback with the result
// Your hook should also:
// 1. Tell the Admin panel when it is ready to receive messages
// 2. Handle the results of the `onChange` callback to update the UI
// 3. Unsubscribe from the `window.postMessage` events when it unmounts
```
Here is an example of what the same `useLivePreview` React hook from above looks like under the hood:
```tsx
import { subscribe, unsubscribe, ready } from '@payloadcms/live-preview'
import { useCallback, useEffect, useState, useRef } from 'react'
export const useLivePreview = <T extends any>(props: {
depth?: number
initialData: T
serverURL: string
}): {
data: T
isLoading: boolean
} => {
const { depth = 0, initialData, serverURL } = props
const [data, setData] = useState<T>(initialData)
const [isLoading, setIsLoading] = useState<boolean>(true)
const hasSentReadyMessage = useRef<boolean>(false)
const onChange = useCallback((mergedData) => {
// When a change is made, the `onChange` callback will be called with the merged data
// Set this merged data into state so that React will re-render the UI
setData(mergedData)
setIsLoading(false)
}, [])
useEffect(() => {
// Listen for `window.postMessage` events from the Admin panel
// When a change is made, the `onChange` callback will be called with the merged data
const subscription = subscribe({
callback: onChange,
depth,
initialData,
serverURL,
})
// Once subscribed, send a `ready` message back up to the Admin panel
// This will indicate that the front-end is ready to receive messages
if (!hasSentReadyMessage.current) {
hasSentReadyMessage.current = true
ready({
serverURL,
})
}
// When the component unmounts, unsubscribe from the `window.postMessage` events
return () => {
unsubscribe(subscription)
}
}, [serverURL, onChange, depth, initialData])
return {
data,
isLoading,
}
}
```
<Banner type="info">
When building your own hook, ensure that the args and return values are consistent with the ones
listed at the top of this document. This will ensure that all hooks follow the same API.
</Banner>
## Example
For a working demonstration of this, check out the official [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload). There you will find examples of various front-end frameworks and how to integrate each one of them, including:
- [Next.js App Router](https://github.com/payloadcms/payload/tree/main/examples/live-preview/next-app)
- [Next.js Pages Router](https://github.com/payloadcms/payload/tree/main/examples/live-preview/next-pages)
## Troubleshooting
### Relationships and/or uploads are not populating
If you are using relationships or uploads in your front-end application, and your front-end application runs on a different domain than your Payload server, you may need to configure [CORS](../configuration/overview) to allow requests to be made between the two domains. This includes sites that are running on a different port or subdomain. Similarly, if you are protecting resources behind user authentication, you may also need to configure [CSRF](../authentication/overview#csrf-protection) to allow cookies to be sent between the two domains. For example:
```ts
// payload.config.ts
{
// ...
// If your site is running on a different domain than your Payload server,
// This will allows requests to be made between the two domains
cors: {
[
'http://localhost:3001' // Your front-end application
],
},
// If you are protecting resources behind user authentication,
// This will allow cookies to be sent between the two domains
csrf: {
[
'http://localhost:3001' // Your front-end application
],
},
}
```
### Relationships and/or uploads disappear after editing a document
It is possible that either you are setting an improper [`depth`](../getting-started/concepts#depth) in your initial request and/or your `useLivePreview` hook, or they're mismatched. Ensure that the `depth` parameter is set to the correct value, and that it matches exactly in both places. For example:
```tsx
// Your initial request
const { docs } = await payload.find({
collection: 'pages',
depth: 1, // Ensure this is set to the proper depth for your application
where: {
slug: {
equals: 'home',
},
},
})
```
```tsx
// Your hook
const { data } = useLivePreview<PageType>({
initialData: initialPage,
serverURL: PAYLOAD_SERVER_URL,
depth: 1, // Ensure this matches the depth of your initial request
})
```
### Iframe refuses to connect
If your front-end application has set a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) (CSP) that blocks the Admin Panel from loading your front-end application, the iframe will not be able to load your site. To resolve this, you can whitelist the Admin Panel's domain in your CSP by setting the `frame-ancestors` directive:
```plaintext
frame-ancestors: "self" localhost:* https://your-site.com;
```

View File

@@ -1,246 +1,16 @@
---
title: Implementing Live Preview in your app
label: Frontend Implementation
title: Implementing Live Preview in your frontend
label: Frontend
order: 20
desc: Learn how to implement Live Preview in your front-end application.
keywords: live preview, frontend, react, next.js, vue, nuxt.js, svelte, hook, useLivePreview
---
While using Live Preview, the Admin panel emits a new `window.postMessage` event every time a change is made to the document. Your front-end application can listen for these events and re-render accordingly.
There are two ways to use Live Preview in your own application depending on whether your front-end framework supports server components:
Wiring your front-end into Live Preview is easy. If your front-end application is built with React or Next.js, use the [`useLivePreview`](#react) React hook that Payload provides. In the future, all other major frameworks like Vue, Svelte, etc will be officially supported. If you are using any of these frameworks today, you can still integrate with Live Preview yourself using the underlying tooling that Payload provides. See [building your own hook](#building-your-own-hook) for more information.
By default, all hooks accept the following args:
| Path | Description |
| ------------------ | -------------------------------------------------------------------------------------- |
| **`serverURL`** \* | The URL of your Payload server. |
| **`initialData`** | The initial data of the document. The live data will be merged in as changes are made. |
| **`depth`** | The depth of the relationships to fetch. Defaults to `0`. |
| **`apiRoute`** | The path of your API route as defined in `routes.api`. Defaults to `/api`. |
_\* An asterisk denotes that a property is required._
And return the following values:
| Path | Description |
| --------------- | ---------------------------------------------------------------- |
| **`data`** | The live data of the document, merged with the initial data. |
| **`isLoading`** | A boolean that indicates whether or not the document is loading. |
- [Server-side Live Preview (suggested)](./server)
- [Client-side Live Preview](./client)
<Banner type="info">
If your front-end is tightly coupled to required fields, you should ensure that your UI does not
break when these fields are removed. For example, if you are rendering something like
`data.relatedPosts[0].title`, your page will break once you remove the first related post. To get
around this, use conditional logic, optional chaining, or default values in your UI where needed.
For example, `data?.relatedPosts?.[0]?.title`.
We suggest using server-side Live Preview if your framework supports it, it is both simpler to setup and more performant to run than the client-side alternative.
</Banner>
### React
If your front-end application is built with React or Next.js, you can use the `useLivePreview` hook that Payload provides.
First, install the `@payloadcms/live-preview-react` package:
```bash
npm install @payloadcms/live-preview-react
```
Then, use the `useLivePreview` hook in your React component:
```tsx
'use client'
import { useLivePreview } from '@payloadcms/live-preview-react'
import { Page as PageType } from '@/payload-types'
// Fetch the page in a server component, pass it to the client component, then thread it through the hook
// The hook will take over from there and keep the preview in sync with the changes you make
// The `data` property will contain the live data of the document
export const PageClient: React.FC<{
page: {
title: string
}
}> = ({ page: initialPage }) => {
const { data } = useLivePreview<PageType>({
initialData: initialPage,
serverURL: PAYLOAD_SERVER_URL,
depth: 2,
})
return <h1>{data.title}</h1>
}
```
<Banner type="info">
If is important that the `depth` argument matches exactly with the depth of your initial page
request. The depth property is used to populated relationships and uploads beyond their IDs. See
[Depth](../getting-started/concepts#depth) for more information.
</Banner>
## Building your own hook
No matter what front-end framework you are using, you can build your own hook using the same underlying tooling that Payload provides.
First, install the base `@payloadcms/live-preview` package:
```bash
npm install @payloadcms/live-preview
```
This package provides the following functions:
| Path | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------------------ |
| **`subscribe`** | Subscribes to the Admin panel's `window.postMessage` events and calls the provided callback function. |
| **`unsubscribe`** | Unsubscribes from the Admin panel's `window.postMessage` events. |
| **`ready`** | Sends a `window.postMessage` event to the Admin panel to indicate that the front-end is ready to receive messages. |
The `subscribe` function takes the following args:
| Path | Description |
| ------------------ | ------------------------------------------------------------------------------------------- |
| **`callback`** \* | A callback function that is called with `data` every time a change is made to the document. |
| **`serverURL`** \* | The URL of your Payload server. |
| **`initialData`** | The initial data of the document. The live data will be merged in as changes are made. |
| **`depth`** | The depth of the relationships to fetch. Defaults to `0`. |
With these functions, you can build your own hook using your front-end framework of choice:
```tsx
import { subscribe, unsubscribe } from '@payloadcms/live-preview'
// To build your own hook, subscribe to Live Preview events using the`subscribe` function
// It handles everything from:
// 1. Listening to `window.postMessage` events
// 2. Merging initial data with active form state
// 3. Populating relationships and uploads
// 4. Calling the `onChange` callback with the result
// Your hook should also:
// 1. Tell the Admin panel when it is ready to receive messages
// 2. Handle the results of the `onChange` callback to update the UI
// 3. Unsubscribe from the `window.postMessage` events when it unmounts
```
Here is an example of what the same `useLivePreview` React hook from above looks like under the hood:
```tsx
import { subscribe, unsubscribe, ready } from '@payloadcms/live-preview'
import { useCallback, useEffect, useState, useRef } from 'react'
export const useLivePreview = <T extends any>(props: {
depth?: number
initialData: T
serverURL: string
}): {
data: T
isLoading: boolean
} => {
const { depth = 0, initialData, serverURL } = props
const [data, setData] = useState<T>(initialData)
const [isLoading, setIsLoading] = useState<boolean>(true)
const hasSentReadyMessage = useRef<boolean>(false)
const onChange = useCallback((mergedData) => {
// When a change is made, the `onChange` callback will be called with the merged data
// Set this merged data into state so that React will re-render the UI
setData(mergedData)
setIsLoading(false)
}, [])
useEffect(() => {
// Listen for `window.postMessage` events from the Admin panel
// When a change is made, the `onChange` callback will be called with the merged data
const subscription = subscribe({
callback: onChange,
depth,
initialData,
serverURL,
})
// Once subscribed, send a `ready` message back up to the Admin panel
// This will indicate that the front-end is ready to receive messages
if (!hasSentReadyMessage.current) {
hasSentReadyMessage.current = true
ready({
serverURL,
})
}
// When the component unmounts, unsubscribe from the `window.postMessage` events
return () => {
unsubscribe(subscription)
}
}, [serverURL, onChange, depth, initialData])
return {
data,
isLoading,
}
}
```
<Banner type="info">
When building your own hook, ensure that the args and return values are consistent with the ones
listed at the top of this document. This will ensure that all hooks follow the same API.
</Banner>
## Example
For a working demonstration of this, check out the official [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload). There you will find examples of various front-end frameworks and how to integrate each one of them, including:
- [Next.js App Router](https://github.com/payloadcms/payload/tree/main/examples/live-preview/next-app)
- [Next.js Pages Router](https://github.com/payloadcms/payload/tree/main/examples/live-preview/next-pages)
## Troubleshooting
#### Relationships and/or uploads are not populating
If you are using relationships or uploads in your front-end application, and your front-end application runs on a different domain than your Payload server, you may need to configure [CORS](../configuration/overview) to allow requests to be made between the two domains. This includes sites that are running on a different port or subdomain. Similarly, if you are protecting resources behind user authentication, you may also need to configure [CSRF](../authentication/overview#csrf-protection) to allow cookies to be sent between the two domains. For example:
```ts
// payload.config.ts
{
// ...
// If your site is running on a different domain than your Payload server,
// This will allows requests to be made between the two domains
cors: {
[
'http://localhost:3001' // Your front-end application
],
},
// If you are protecting resources behind user authentication,
// This will allow cookies to be sent between the two domains
csrf: {
[
'http://localhost:3001' // Your front-end application
],
},
}
```
#### Relationships and/or uploads disappear after editing a document
It is possible that either you are setting an improper [`depth`](../getting-started/concepts#depth) in your initial request and/or your `useLivePreview` hook, or they're mismatched. Ensure that the `depth` parameter is set to the correct value, and that it matches exactly in both places. For example:
```tsx
// Your initial request
const { docs } = await payload.find({
collection: 'pages',
depth: 1, // Ensure this is set to the proper depth for your application
where: {
slug: {
equals: 'home',
},
},
})
```
```tsx
// Your hook
const { data } = useLivePreview<PageType>({
initialData: initialPage,
serverURL: PAYLOAD_SERVER_URL,
depth: 1, // Ensure this matches the depth of your initial request
})
```

View File

@@ -8,7 +8,7 @@ keywords: live preview, preview, live, iframe, iframe preview, visual editing, d
**With Live Preview you can render your front-end application directly within the Admin panel. As you type, your changes take effect in real-time. No need to save a draft or publish your changes.**
Live Preview works by rendering an iframe on the page that loads your front-end application. The Admin panel communicates with your app through [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) events. These events are emitted every time a change is made to the document. Your app then listens for these events and re-renders itself with the data it receives.
Live Preview works by rendering an iframe on the page that loads your front-end application. The Admin panel communicates with your app through [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) events. These events are emitted every time a change is made to the document. Your app then listens for these events and re-renders itself with the data it receives. Live Preview works in both server-side as well as client-side environments. See [Front-End](./frontend) for more details.
{/* IMAGE OF LIVE PREVIEW HERE */}

View File

@@ -0,0 +1,183 @@
---
title: Server-side Live Preview
label: Server-side
order: 30
desc: Learn how to implement Live Preview in your server-side front-end application.
keywords: live preview, frontend, react, next.js, vue, nuxt.js, svelte, hook, useLivePreview
---
<Banner type="info">
Server-side Live Preview is only for front-end frameworks that support the concept of Server Components, i.e. [React Server Components](https://react.dev/reference/rsc/server-components). If your front-end application is built with a client-side framework like the [Next.js Pages Router](https://nextjs.org/docs/pages), [React Router](https://reactrouter.com), [Vue 3](https://vuejs.org), etc., see [client-side Live Preview](./client).
</Banner>
Server-side Live Preview works by making a roundtrip to the server every time your document is saved, i.e. draft save, autosave, or publish. While using Live Preview, the Admin panel emits a new `window.postMessage` event which your front-end application can use to invoke this process. In Next.js, this means simply calling `router.refresh()` which will hydrate the HTML using new data straight from the [Local API](../local-api/overview).
<Banner type="warning">
It is recommended that you enable [Autosave](../versions/autosave) alongside Live Preview to make the experience feel more responsive.
</Banner>
If your front-end application is built with [React](#react), you can use the `RefreshRouteOnChange` function that Payload provides. In the future, all other major frameworks like Vue and Svelte will be officially supported. If you are using any of these frameworks today, you can still integrate with Live Preview yourself using the underlying tooling that Payload provides. See [building your own router refresh component](#building-your-own-router-refresh-component) for more information.
## React
If your front-end application is built with [React](https://react.dev) or [Next.js](https://nextjs.org), you can use the `RefreshRouteOnSave` component that Payload provides.
First, install the `@payloadcms/live-preview-react` package:
```bash
npm install @payloadcms/live-preview-react
```
Then, render `RefreshRouteOnSave` anywhere in your `page.tsx`. Here's an example:
`page.tsx`:
```tsx
import { RefreshRouteOnSave } from './RefreshRouteOnSave.tsx'
import { getPayloadHMR } from '@payloadcms/next'
import config from '../payload.config'
export default async function Page() {
const payload = await getPayloadHMR({ config })
const page = await payload.find({
collection: 'pages',
draft: true
})
return (
<Fragment>
<RefreshRouteOnSave />
<h1>{page.title}</h1>
</Fragment>
)
}
```
`RefreshRouteOnSave.tsx`:
```tsx
'use client'
import { RefreshRouteOnSave as PayloadLivePreview } from '@payloadcms/live-preview-react'
import { useRouter } from 'next/navigation.js'
import React from 'react'
export const RefreshRouteOnSave: React.FC = () => {
const router = useRouter()
return <PayloadLivePreview refresh={router.refresh} serverURL={process.env.PAYLOAD_SERVER_URL} />
}
```
## Building your own router refresh component
No matter what front-end framework you are using, you can build your own component using the same underlying tooling that Payload provides.
First, install the base `@payloadcms/live-preview` package:
```bash
npm install @payloadcms/live-preview
```
This package provides the following functions:
| Path | Description |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **`ready`** | Sends a `window.postMessage` event to the Admin panel to indicate that the front-end is ready to receive messages. |
| **`isDocumentEvent`** | Checks if a `MessageEvent` originates from the Admin panel and is a document-level event, i.e. draft save, autosave, publish, etc. |
With these functions, you can build your own hook using your front-end framework of choice:
```tsx
import { ready, isDocumentEvent } from '@payloadcms/live-preview'
// To build your own component:
// 1. Listen for document-level `window.postMessage` events sent from the Admin panel
// 2. Tell the Admin panel when it is ready to receive messages
// 3. Refresh the route every time a new document-level event is received
// 4. Unsubscribe from the `window.postMessage` events when it unmounts
```
Here is an example of what the same `RefreshRouteOnSave` React component from above looks like under the hood:
```tsx
'use client'
import type React from 'react'
import { isDocumentEvent, ready } from '@payloadcms/live-preview'
import { useCallback, useEffect, useRef } from 'react'
export const RefreshRouteOnSave: React.FC<{
apiRoute?: string
depth?: number
refresh: () => void
serverURL: string
}> = (props) => {
const { apiRoute, depth, refresh, serverURL } = props
const hasSentReadyMessage = useRef<boolean>(false)
const onMessage = useCallback(
(event: MessageEvent) => {
if (isDocumentEvent(event, serverURL)) {
if (typeof refresh === 'function') {
refresh()
}
}
},
[refresh, serverURL],
)
useEffect(() => {
if (typeof window !== 'undefined') {
window.addEventListener('message', onMessage)
}
if (!hasSentReadyMessage.current) {
hasSentReadyMessage.current = true
ready({
serverURL,
})
}
return () => {
if (typeof window !== 'undefined') {
window.removeEventListener('message', onMessage)
}
}
}, [serverURL, onMessage, depth, apiRoute])
return null
}
```
## Example
{/* TODO: add example once beta has been release and an example can be created */}
## Troubleshooting
### Updates do not appear as fast as client-side Live Preview
If you are noticing that updates feel less snappy than client-side Live Preview (i.e. the `useLivePreview` hook), this is because of how the two differ in how they work—instead of emitting events against form state as you type, server-side Live Preview refreshes the route after a new document is saved. You can use autosave to mimic this effect. Try decreasing the value of `versions.autoSave.interval` to make the experience feel more responsive:
```ts
// collection.ts
{
versions: {
drafts: {
autosave: {
interval: 375,
},
},
},
}
```
### Iframe refuses to connect
If your front-end application has set a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) (CSP) that blocks the Admin Panel from loading your front-end application, the iframe will not be able to load your site. To resolve this, you can whitelist the Admin Panel's domain in your CSP by setting the `frame-ancestors` directive:
```plaintext
frame-ancestors: "self" localhost:* https://your-site.com;
```

View File

@@ -26,11 +26,11 @@ Here are some common examples of how you can use the Local API:
- Opening custom Express routes which feature additional functionality but still rely on Payload
- Within access control and hook functions
### Accessing payload
## Accessing payload
You can gain access to the currently running `payload` object via two ways:
##### Importing it
#### Importing it
You can import or require `payload` into your own files after it's been initialized, but you need to make sure that
your `import` / `require` statements come **after** you call `payload.init()`—otherwise Payload won't have been
@@ -49,7 +49,7 @@ const afterChangeHook: CollectionAfterChangeHook = async () => {
}
```
##### Accessing from the `req`
#### Accessing from the `req`
Payload is available anywhere you have access to the Express `req` - including within your access control and hook
functions.
@@ -64,7 +64,7 @@ const afterChangeHook: CollectionAfterChangeHook = async ({ req: { payload } })
}
```
### Local options available
## Local options available
You can specify more options within the Local API vs. REST or GraphQL due to the server-only context that they are
executed in.
@@ -95,7 +95,7 @@ _There are more options available on an operation by operation basis outlined be
The following Collection operations are available through the Local API:
#### Create
### Create
```js
// The created Post document is returned
@@ -127,7 +127,7 @@ const post = await payload.create({
})
```
#### Find
### Find
```js
// Result will be a paginated set of Posts.
@@ -148,7 +148,7 @@ const result = await payload.find({
})
```
#### Find by ID
### Find by ID
```js
// Result will be a Post document.
@@ -164,7 +164,23 @@ const result = await payload.findByID({
})
```
#### Update by ID
### Count
```js
// Result will be an object with:
// {
// totalDocs: 10, // count of the documents satisfies query
// }
const result = await payload.count({
collection: 'posts', // required
locale: 'en',
where: {}, // pass a `where` query here
user: dummyUser,
overrideAccess: false,
})
```
### Update by ID
```js
// Result will be the updated Post document.
@@ -195,7 +211,7 @@ const result = await payload.update({
})
```
#### Update Many
### Update Many
```js
// Result will be an object with:
@@ -233,7 +249,7 @@ const result = await payload.update({
})
```
#### Delete
### Delete
```js
// Result will be the now-deleted Post document.
@@ -249,7 +265,7 @@ const result = await payload.delete({
})
```
#### Delete Many
### Delete Many
```js
// Result will be an object with:
@@ -277,7 +293,7 @@ const result = await payload.delete({
If a collection has [`Authentication`](/docs/authentication/overview) enabled, additional Local API operations will be
available:
#### Login
### Login
```js
// result will be formatted as follows:
@@ -304,7 +320,7 @@ const result = await payload.login({
})
```
#### Forgot Password
### Forgot Password
```js
// Returned token will allow for a password reset
@@ -318,7 +334,7 @@ const token = await payload.forgotPassword({
})
```
#### Reset Password
### Reset Password
```js
// Result will be formatted as follows:
@@ -338,7 +354,7 @@ const result = await payload.resetPassword({
})
```
#### Unlock
### Unlock
```js
// Returned result will be a boolean representing success or failure
@@ -353,7 +369,7 @@ const result = await payload.unlock({
})
```
#### Verify
### Verify
```js
// Returned result will be a boolean representing success or failure
@@ -367,7 +383,7 @@ const result = await payload.verifyEmail({
The following Global operations are available through the Local API:
#### Find
### Find
```js
// Result will be the Header Global.
@@ -382,7 +398,7 @@ const result = await payload.findGlobal({
})
```
#### Update
### Update
```js
// Result will be the updated Header Global.

View File

@@ -22,11 +22,11 @@ Our plugin template includes everything you need to build a full life-cycle plug
By abstracting your code into a plugin, you&apos;ll be able to reuse your feature across multiple projects and make it available for other developers to use.
### Plugins Recap
## Plugins Recap
Here is a brief recap of how to integrate plugins with Payload, to learn more head back to the [plugin overview page](https://payloadcms.com/docs/plugins/overview).
#### How to install a plugin
### How to install a plugin
To install any plugin, simply add it to your Payload config in the plugins array.
@@ -45,7 +45,7 @@ const config = buildConfig({
export default config;
```
#### Initialization
### Initialization
The initialization process goes in the following order:
@@ -55,7 +55,7 @@ The initialization process goes in the following order:
4. Sanitization cleans and validates data
5. Final config gets initialized
### Plugin Template
## Plugin Template
In the [Payload plugin template](https://github.com/payloadcms/payload-plugin-template), you will see a common file structure that is used across plugins:
@@ -63,14 +63,14 @@ In the [Payload plugin template](https://github.com/payloadcms/payload-plugin-te
2. /src folder - everything related to the plugin
3. /dev folder - sanitized test project for development
#### Root
### The root folder
In the root folder, you will see various files related to the configuration of the plugin. We set up our environment in a similar manner in Payload core and across other projects. The only two files you need to modify are:
- **README**.md - This contains instructions on how to use the template. When you are ready, update this to contain instructions on how to use your Plugin.
- **package**.json - Contains necessary scripts and dependencies. Overwrite the metadata in this file to describe your Plugin.
#### Dev
### The dev folder
The purpose of the **dev** folder is to provide a sanitized local Payload project. so you can run and test your plugin while you are actively developing it.
@@ -101,7 +101,7 @@ When you&apos;re ready to start development, navigate into this folder with `cd
And then start the project with `yarn dev` and pull up `http://localhost:3000` in your browser.
### Testing
## Testing
Another benefit of the dev folder is that you have the perfect environment established for testing.
@@ -129,7 +129,7 @@ describe('Plugin tests', () => {
})
```
### Seeding data
## Seeding data
For development and testing, you will likely need some data to work with. You can streamline this process by seeding and dropping your database - instead of manually entering data.
@@ -159,7 +159,7 @@ export const seed = async (payload: Payload): Promise<void> => {
```
#### Src
## Overview of the src folder
Now that we have our environment setup and dev project ready to go - it&apos;s time to build the plugin!
@@ -188,7 +188,7 @@ export const samplePlugin =
3. From here, you can extend the config however you like!
4. Finally, return the config and you&apos;re all set.
### Spread Syntax
## Spread Syntax
[Spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) (or the spread operator) is a feature in JavaScript that uses the dot notation **(...)** to spread elements from arrays, strings, or objects into various contexts.
@@ -235,7 +235,7 @@ If you wish to add to the `onInit`, you must include the async/await. We don&apo
In the template, we have stubbed out a basic `onInitExtension` file that you can use, if not needed feel free to delete it.
### Webpack
## Webpack
If any of your files use server only packages such as fs, stripe, nodemailer, etc, they will need to be removed from the browser bundle. To do that, you can [alias the file imports with webpack](https://payloadcms.com/docs/admin/webpack#aliasing-server-only-modules).
@@ -251,7 +251,7 @@ config.admin = {
}
```
### Types
## Types
If your plugin has options, you should define and provide types for these options in a separate file which gets exported from the main `index.ts`.
@@ -268,26 +268,26 @@ export interface PluginTypes {
If possible, include [JSDoc comments](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#types-1) to describe the options and their types. This allows a developer to see details about the options in their editor.
### Best practices
## Best practices
In addition to the setup covered above, here are other best practices to follow:
##### Providing an enable / disable option:
### Providing an enable / disable option
For a better user experience, provide a way to disable the plugin without uninstalling it. This is especially important if your plugin adds additional webpack aliases, this will allow you to still let the webpack run to prevent errors.
##### Include tests in your GitHub CI workflow:
### Include tests in your GitHub CI workflow
If you&apos;ve configured tests for your package, integrate them into your workflow to run the tests each time you commit to the plugin repository. Learn more about [how to configure tests into your GitHub CI workflow.](https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs)
##### Publish your finished plugin to NPM:
### Publish your finished plugin to NPM
The best way to share and allow others to use your plugin once it is complete is to publish an NPM package. This process is straightforward and well documented, find out more about [creating and publishing a NPM package here](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
##### Add payload-plugin topic tag:
### Add payload-plugin topic tag
Apply the tag **payload-plugin** to your GitHub repository. This will boost the visibility of your plugin and ensure it gets listed with [existing payload plugins](https://github.com/topics/payload-plugin).
##### Use [Semantic Versioning](https://semver.org/) (SemVer):
### Use Semantic Versioning (SemVer)
With the SemVer system you release version numbers that reflect the nature of changes (major, minor, patch). Ensure all major versions reference their Payload compatibility.
With the [Semantic Versioning](https://semver.org/) (SemVer) system you release version numbers that reflect the nature of changes (major, minor, patch). Ensure all major versions reference their Payload compatibility.

View File

@@ -23,7 +23,7 @@ Forms can be as simple or complex as you need, from a basic contact form, to a m
with as much detail as possible.
</Banner>
##### Core Features
## Core Features
- Build completely dynamic forms directly from the admin panel for a variety of use cases
- Render forms on your front-end using your own UI components and match your brand's design system
@@ -64,9 +64,9 @@ const config = buildConfig({
export default config
```
### Options
## Options
#### `fields` (option)
### `fields` (option)
The `fields` property is an object of field types to allow your admin editors to build forms with. To override default settings, pass either a boolean value or a partial [Payload Block](https://payloadcms.com/docs/fields/blocks#block-configs) _keyed to the block's slug_. See [Fields](#fields) for more details.
@@ -89,7 +89,7 @@ formBuilder({
})
```
#### `redirectRelationships`
### `redirectRelationships`
The `redirectRelationships` property is an array of collection slugs that, when enabled, are populated as options in the form's `redirect` field. This field is used to redirect the user to a dedicated confirmation page upon form submission (optional).
@@ -101,7 +101,7 @@ formBuilder({
})
```
#### `beforeEmail`
### `beforeEmail`
The `beforeEmail` property is a [beforeChange](<[beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange)>) hook that is called just after emails are prepared, but before they are sent. This is a great place to inject your own HTML template to add custom styles.
@@ -119,7 +119,7 @@ formBuilder({
})
```
#### `formOverrides`
### `formOverrides`
Override anything on the `forms` collection by sending a [Payload Collection Config](https://payloadcms.com/docs/configuration/collections) to the `formOverrides` property.
@@ -143,7 +143,7 @@ formBuilder({
})
```
#### `formSubmissionOverrides`
### `formSubmissionOverrides`
Override anything on the `form-submissions` collection by sending a [Payload Collection Config](https://payloadcms.com/docs/configuration/collections) to the `formSubmissionOverrides` property.
@@ -166,7 +166,7 @@ formBuilder({
})
```
#### `handlePayment`
### `handlePayment`
The `handlePayment` property is a [beforeChange](<[beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange)>) hook that is called upon form submission. You can integrate into any third-party payment processing API here to accept payment based on form input. You can use the `getPaymentTotal` function to calculate the total cost after all conditions have been applied. This is only applicable if the form has enabled the `payment` field.
@@ -206,7 +206,7 @@ Each field represents a form input. To override default settings pass either a b
of a collection_ which are set via `formOverrides.fields`.
</Banner>
#### Text
### Text
Maps to a `text` input in your front-end. Used to collect a simple string.
@@ -218,7 +218,7 @@ Maps to a `text` input in your front-end. Used to collect a simple string.
| `width` | string | The width of the field on the front-end. |
| `required` | checkbox | Whether or not the field is required when submitted. |
#### Textarea
### Textarea
Maps to a `textarea` input on your front-end. Used to collect a multi-line string.
@@ -230,7 +230,7 @@ Maps to a `textarea` input on your front-end. Used to collect a multi-line strin
| `width` | string | The width of the field on the front-end. |
| `required` | checkbox | Whether or not the field is required when submitted. |
#### Select
### Select
Maps to a `select` input on your front-end. Used to display a list of options.
@@ -243,7 +243,7 @@ Maps to a `select` input on your front-end. Used to display a list of options.
| `required` | checkbox | Whether or not the field is required when submitted. |
| `options` | array | An array of objects with `label` and `value` properties. |
#### Email (field)
### Email (field)
Maps to a `text` input with type `email` on your front-end. Used to collect an email address.
@@ -255,7 +255,7 @@ Maps to a `text` input with type `email` on your front-end. Used to collect an e
| `width` | string | The width of the field on the front-end. |
| `required` | checkbox | Whether or not the field is required when submitted. |
#### State
### State
Maps to a `select` input on your front-end. Used to collect a US state.
@@ -267,7 +267,7 @@ Maps to a `select` input on your front-end. Used to collect a US state.
| `width` | string | The width of the field on the front-end. |
| `required` | checkbox | Whether or not the field is required when submitted. |
#### Country
### Country
Maps to a `select` input on your front-end. Used to collect a country.
@@ -279,7 +279,7 @@ Maps to a `select` input on your front-end. Used to collect a country.
| `width` | string | The width of the field on the front-end. |
| `required` | checkbox | Whether or not the field is required when submitted. |
#### Checkbox
### Checkbox
Maps to a `checkbox` input on your front-end. Used to collect a boolean value.
@@ -291,7 +291,7 @@ Maps to a `checkbox` input on your front-end. Used to collect a boolean value.
| `width` | string | The width of the field on the front-end. |
| `required` | checkbox | Whether or not the field is required when submitted. |
#### Number
### Number
Maps to a `number` input on your front-end. Used to collect a number.
@@ -303,7 +303,7 @@ Maps to a `number` input on your front-end. Used to collect a number.
| `width` | string | The width of the field on the front-end. |
| `required` | checkbox | Whether or not the field is required when submitted. | | `defaultValue` | number | The default value of the field. |
#### Message
### Message
Maps to a `RichText` component on your front-end. Used to display an arbitrary message to the user anywhere in the form.
@@ -311,7 +311,7 @@ Maps to a `RichText` component on your front-end. Used to display an arbitrary m
| --------- | -------- | ----------------------------------- |
| `message` | richText | The message to display on the form. |
#### Payment
### Payment
Add this field to your form if it should collect payment. Upon submission, the `handlePayment` callback is executed with the form and submission data. You can use this to integrate with any third-party payment processing API.
@@ -324,7 +324,7 @@ Add this field to your form if it should collect payment. Upon submission, the `
| `required` | checkbox | Whether or not the field is required when submitted. |
| `priceConditions` | array | An array of objects that define the price conditions. See below for more details. |
##### Price Conditions
#### Price Conditions
Each of the `priceConditions` are executed by the `getPaymentTotal` utility that this plugin provides. You can call this function in your `handlePayment` callback to dynamically calculate the total price of a form upon submission based on the user's input. For example, you could create a price condition that says "if the user selects 'yes' for this checkbox, add $10 to the total price".
@@ -337,7 +337,7 @@ Each of the `priceConditions` are executed by the `getPaymentTotal` utility that
| `valueType` | string | The type of value to use to determine the price. |
| `value` | string | The value to use to determine the price. |
#### Field Overrides
### Field Overrides
You can provide your own custom fields by passing a new [Payload Block](https://payloadcms.com/docs/fields/blocks#block-configs) object into `fields`. You can override or extend any existing fields by first importing the `fields` from the plugin:
@@ -392,7 +392,7 @@ The [Examples Directory](https://github.com/payloadcms/payload/tree/main/example
Below are some common troubleshooting tips. To help other developers, please contribute to this section as you troubleshoot your own application.
##### SendGrid 403 Forbidden Error
#### SendGrid 403 Forbidden Error
- If you are using [SendGrid Link Branding](https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-link-branding) to remove the "via sendgrid.net" part of your email, you must also setup [Domain Authentication](https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-domain-authentication). This means you can only send emails from an address on this domain — so the `from` addresses in your form submission emails **_cannot_** be anything other than `something@your_domain.com`. This means that from `{{email}}` will not work, but `website@your_domain.com` will. You can still send the form's email address in the body of the email.

View File

@@ -33,7 +33,7 @@ but different parents.
with as much detail as possible.
</Banner>
##### Core features
## Core features
- Automatically adds a `parent` relationship field to each document
- Allows for parent/child relationships between documents within the same collection

View File

@@ -27,7 +27,7 @@ Writing plugins is no more complex than writing regular JavaScript. If you know
- Integrate all `upload`-enabled collections with a third-party file host like S3 or Cloudinary
- Add custom endpoints or GraphQL queries / mutations with any type of custom functionality that you can think of
### How to install plugins
## How to install plugins
The base Payload config allows for a `plugins` property which takes an `array` of [`Plugins`](https://github.com/payloadcms/payload/blob/main/packages/payload/src/config/types.ts).
@@ -82,7 +82,7 @@ const config = buildConfig({
export default config
```
#### When Plugins are initialized
### When Plugins are initialized
Payload Plugins are executed _after_ the incoming config is validated, but before it is sanitized and had default options merged in.
@@ -142,7 +142,7 @@ const addLastModified: Plugin = (incomingConfig: Config): Config => {
export default addLastModified
```
### Available Plugins
## Available Plugins
You can discover existing plugins by browsing the `payload-plugin` topic on [GitHub](https://github.com/topics/payload-plugin).

View File

@@ -21,7 +21,7 @@ For example, if you have a page at `/about` and you want to change it to `/about
with as much detail as possible.
</Banner>
##### Core features
## Core features
- Adds a `redirects` collection to your config that:
- includes a `from` and `to` fields

View File

@@ -25,7 +25,7 @@ This plugin is a great way to implement a fast, immersive search experience such
with as much detail as possible.
</Banner>
##### Core Features
## Core Features
- Automatically adds an indexed `search` collection to your database
- Automatically creates, syncs, and deletes search records as you manage your documents

133
docs/plugins/sentry.mdx Normal file
View File

@@ -0,0 +1,133 @@
---
title: Sentry Plugin
label: Sentry
order: 20
desc: Integrate Sentry error tracking into your Payload application
keywords: plugins, sentry, error, tracking, monitoring, logging, bug, reporting, performance
---
[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-sentry)](https://www.npmjs.com/package/@payloadcms/plugin-sentry)
This plugin allows you to integrate [Sentry](https://sentry.io/) seamlessly with your [Payload](https://github.com/payloadcms/payload) application.
## What is Sentry?
Sentry is a powerful error tracking and performance monitoring tool that helps developers identify, diagnose, and resolve issues in their applications.
<Banner type="success">
Sentry does smart stuff with error data to make bugs easier to find and fix. - [sentry.io](https://sentry.io/)
</Banner>
This multi-faceted software offers a range of features that will help you manage errors with greater ease and ultimately ensure your application is running smoothly:
## Core Features
- **Error Tracking**: Instantly captures and logs errors as they occur in your application
- **Performance Monitoring**: Tracks application performance to identify slowdowns and bottlenecks
- **Detailed Reports**: Provides comprehensive insights into errors, including stack traces and context
- **Alerts and Notifications**: Send and customize event-triggered notifications
- **Issue Grouping, Filtering and Search**: Automatically groups similar errors, and allows filtering and searching issues by custom criteria
- **Breadcrumbs**: Records user actions and events leading up to an error
- **Integrations**: Connects with various tools and services for enhanced workflow and issue management
<Banner type="info">
This plugin is completely open-source and the [source code can be found here](https://github.com/payloadcms/payload/tree/main/packages/plugin-sentry). If you need help, check out our [Community Help](https://payloadcms.com/community-help). If you think you've found a bug, please [open a new issue](https://github.com/payloadcms/payload/issues/new?assignees=&labels=plugin%3A%20seo&template=bug_report.md&title=plugin-seo%3A) with as much detail as possible.
</Banner>
## Installation
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
```bash
yarn add @payloadcms/plugin-sentry
```
## Basic Usage
In the `plugins` array of your [Payload config](https://payloadcms.com/docs/configuration/overview), call the plugin and pass in your Sentry DSN as an option.
```ts
import { buildConfig } from 'payload/config'
import { sentry } from '@payloadcms/plugin-sentry'
import { Pages, Media } from './collections'
const config = buildConfig({
collections: [Pages, Media],
plugins: [
sentry({
dsn: 'https://61edebas776889984d323d777@o4505289711681536.ingest.sentry.io/4505357433352176',
}),
],
})
export default config
```
## Options
- `dsn` : string | **required**
Sentry automatically assigns a DSN when you create a project, the unique DSN informs Sentry where to send events so they are associated with the correct project.
<Banner type="warning">
You can find your project DSN (Data Source Name) by visiting [sentry.io](sentry.io) and navigating to your [Project] > Settings > Client Keys (DSN).
</Banner>
- `enabled`: boolean | optional
Set to false to disable the plugin. Defaults to true.
- `init` : ClientOptions | optional
Sentry allows a variety of options to be passed into the Sentry.init() function, see the full list of options [here](https://docs.sentry.io/platforms/node/guides/express/configuration/options).
- `requestHandler` : RequestHandlerOptions | optional
Accepts options that let you decide what data should be included in the event sent to Sentry, checkout the options [here](https://docs.sentry.io/platforms/node/guides/express/configuration/options).
- `captureErrors`: number[] | optional
By default, `Sentry.errorHandler` will capture only errors with a status code of 500 or higher. To capture additional error codes, pass the values as numbers in an array.
To see all options available, visit the [Sentry Docs](https://docs.sentry.io/platforms/node/guides/express/configuration/options).
### Example
Configure any of these options by passing them to the plugin:
```ts
import { buildConfig } from 'payload/config'
import { sentry } from '@payloadcms/plugin-sentry'
import { Pages, Media } from './collections'
const config = buildConfig({
collections: [Pages, Media],
plugins: [
sentry({
dsn: 'https://61edebas777689984d323d777@o4505289711681536.ingest.sentry.io/4505357433352176',
options: {
init: {
debug: true,
environment: 'development',
tracesSampleRate: 1.0,
},
requestHandler: {
serverName: false,
user: ['email'],
},
captureErrors: [400, 403, 404],
},
}),
],
})
export default config
```
## TypeScript
All types can be directly imported:
```ts
import { PluginOptions } from '@payloadcms/plugin-sentry/types'
```

View File

@@ -23,7 +23,7 @@ To help you visualize what your page might look like in a search engine, a previ
with as much detail as possible.
</Banner>
##### Core features
## Core features
- Adds a `meta` field group to every SEO-enabled collection or global
- Allows you to define custom functions to auto-generate metadata

View File

@@ -25,7 +25,7 @@ The beauty of this plugin is the entirety of your application's content and busi
with as much detail as possible.
</Banner>
##### Core features
## Core features
- Hides your Stripe credentials when shipping SaaS applications
- Allows restricted keys through [Payload access control](https://payloadcms.com/docs/access-control/overview)
@@ -85,7 +85,7 @@ The following custom endpoints are automatically opened for you:
##### Stripe REST Proxy
If `rest` is true, proxies the [Stripe REST API](https://stripe.com/docs/api) behind [Payload access control](https://payloadcms.com/docs/access-control/overview) and returns the result. If you need to proxy the API server-side, use the [stripeProxy](#node) function.
If `rest` is true, proxies the [Stripe REST API](https://stripe.com/docs/api) behind [Payload access control](https://payloadcms.com/docs/access-control/overview) and returns the result. This flag should only be used for local development, see the security note below for more information.
```ts
const res = await fetch(`/api/stripe/rest`, {
@@ -106,6 +106,8 @@ const res = await fetch(`/api/stripe/rest`, {
})
```
If you need to proxy the API server-side, use the [stripeProxy](#node) function.
<Banner type="info">
<strong>Note:</strong>
<br />
@@ -113,6 +115,12 @@ const res = await fetch(`/api/stripe/rest`, {
config.
</Banner>
<Banner type="warning">
<strong>Warning:</strong>
<br />
Opening the REST proxy endpoint in production is a potential security risk. Authenticated users will have open access to the Stripe REST API. In production, open your own endpoint and use the [stripeProxy](#node) function to proxy the Stripe API server-side.
</Banner>
## Webhooks
[Stripe webhooks](https://stripe.com/docs/webhooks) are used to sync from Stripe to Payload. Webhooks listen for events on your Stripe account so you can trigger reactions to them. Follow the steps below to enable webhooks.

View File

@@ -31,14 +31,14 @@ npm script will build both and output these directories.
Payload features a suite of security features that you can rely on to strengthen your application's security. When
deploying to Production, it's a good idea to double-check that you are making proper use of each of them.
##### The Secret Key
### The Secret Key
When you initialize Payload, you provide it with a `secret` property. This property should be impossible to guess and
extremely difficult for brute-force attacks to crack. Make sure your Production `secret` is a long, complex string. It's
often best practice to store it in an `env` file which is not checked into your Git repository, using `dotenv` to supply
it to your `payload.init` call.
##### Double-check and thoroughly test all Access Control
### Double-check and thoroughly test all Access Control
Because _**you**_ are in complete control of who can do what with your data, you should double and triple-check that you
wield that power responsibly before deploying to Production.
@@ -47,13 +47,14 @@ wield that power responsibly before deploying to Production.
<strong>
By default, all Access Control functions require that a user is successfully logged in to
Payload to create, read, update, or delete data.
</strong>{' '}
</strong>
But, if you allow public user registration, for example, you will want to make sure that your
access control functions are more strict - permitting <strong>only appropriate users</strong> to
perform appropriate actions.
</Banner>
##### Building the Admin panel
### Building the Admin panel
Before running in Production, you need to have built a production-ready copy of the Payload Admin panel. To do this,
Payload provides the `build` NPM script. You can use it by adding a `script` to your `package.json` file like this:
@@ -75,19 +76,19 @@ Payload provides the `build` NPM script. You can use it by adding a `script` to
Then, to build Payload, you would run `npm run build` in your project folder. A production-ready Admin bundle will be
created in the `build` directory.
##### Setting Node to Production
### Setting Node to Production
Make sure you set the environment variable `NODE_ENV` to `production`. Based on this variable, many Node packages
automatically optimize themselves. In production, Payload automatically disables
the [GraphQL Playground](/docs/graphql/overview#graphql-playground), serves a production-ready version of the Admin
panel, and other changes.
##### Secure Cookie Settings
### Secure Cookie Settings
You should be using an SSL certificate for production Payload instances, which means you
can [enable secure cookies](/docs/authentication/config) in your Authentication-enabled Collection configs.
##### Preventing API Abuse
### Preventing API Abuse
Payload comes with a robust set of built-in anti-abuse measures, such as locking out users after X amount of failed
login attempts, request rate limiting, GraphQL query complexity limits, max `depth` settings, and
@@ -97,7 +98,7 @@ more. [Click here to learn more](/docs/production/preventing-abuse).
Payload can be used with any MongoDB compatible database including AWS DocumentDB or Azure Cosmos DB.
##### Managing MongoDB yourself
### Managing MongoDB yourself
If you are using a [persistent filesystem-based cloud host](#persistent-vs-ephemeral-filesystems) such as
a [DigitalOcean Droplet](https://www.digitalocean.com/products/droplets/) or
@@ -106,7 +107,7 @@ server, you might opt to install MongoDB directly on that server itself so that
With this approach, you can benefit from faster response times, but scaling can become more involved as your app's user
base grows.
##### Letting someone else do it
### Letting someone else do it
Alternatively, you can rely on a third-party MongoDB host such as [MongoDB Atlas](https://www.mongodb.com/). With Atlas
or a similar cloud provider, you can trust them to take care of your database's availability, security, redundancy, and
@@ -122,13 +123,13 @@ backups.
Limitations](https://www.mongodb.com/docs/atlas/reference/free-shared-limitations/?_ga=2.176267877.1329169847.1677683154-860992573.1647438381#operational-limitations).
</Banner>
##### DocumentDB
### DocumentDB
When using AWS DocumentDB, you will need to configure connection options for authentication in the `connectOptions`
passed to the `mongooseAdapter` . You also need to set `connectOptions.useFacet` to `false` to disable use of the
unsupported `$facet` aggregation.
##### CosmosDB
### CosmosDB
When using Azure Cosmos DB, an index is needed for any field you may want to sort on. To add the sort index for all
fields that may be sorted in the admin UI use the <a href="/docs/configuration/overview">indexSortableFields</a>
@@ -140,7 +141,7 @@ If you are using Payload to [manage file uploads](/docs/upload/overview), you ne
will be permanently stored. If you do not use Payload for file uploads, then this section does not impact your app
whatsoever.
#### Persistent vs Ephemeral Filesystems
### Persistent vs Ephemeral Filesystems
Some cloud app hosts such as [Heroku](https://heroku.com) use `ephemeral` file systems, which means that any files
uploaded to your server only last until the server restarts or shuts down. Heroku and similar providers schedule
@@ -169,7 +170,7 @@ perpetually.
with a persistent filesystem or have an integration with a third-party file host like Amazon S3.
</Banner>
##### Using ephemeral filesystem providers like Heroku
### Using ephemeral filesystem providers like Heroku
If you don't use Payload's `upload` functionality, you can go ahead and use Heroku or similar platform easily.
Everything will work exactly as you want it to.

View File

@@ -10,11 +10,11 @@ keywords: abuse, production, config, configuration, documentation, Content Manag
Payload has built-in security best practices that can be configured to your application-specific needs.
### Limit Failed Login Attempts
## Limit Failed Login Attempts
Set the max number of failed login attempts before a user account is locked out for a period of time. Set the `maxLoginAttempts` on the collections that feature Authentication to a reasonable but low number for your users to get in. Use the `lockTime` to set a number in milliseconds from the time a user fails their last allowed attempt that a user must wait to try again.
### Rate Limiting Requests
## Rate Limiting Requests
To prevent DDoS, brute-force, and similar attacks, you can set IP-based rate limits so that once a certain threshold of requests has been hit by a single IP, further requests from the same IP will be ignored. The Payload config `rateLimit` property accepts an object with the following properties:
@@ -35,19 +35,19 @@ To prevent DDoS, brute-force, and similar attacks, you can set IP-based rate lim
you set <strong>trustProxy</strong> to <strong>true</strong>.
</Banner>
### Max Depth
## Max Depth
Querying a collection and automatically including related documents via `depth` incurs a performance cost. Also, it's possible that your configs may have circular relationships, meaning scenarios where an infinite amount of relationships might populate back and forth until your server times out and crashes. You can prevent any potential of depth-related issues by setting a `maxDepth` property on your Payload config.. The maximum allowed depth should be as small as possible without interrupting dev experience, and it defaults to `10`.
### Cross-Site Request Forgery (CSRF)
## Cross-Site Request Forgery (CSRF)
CSRF prevention will verify the authenticity of each request to your API to prevent a malicious action from another site from authorized users. See how to configure CSRF [here](/docs/authentication/overview#csrf-protection).
### Cross Origin Resource Sharing (CORS)
## Cross Origin Resource Sharing (CORS)
To securely allow headless operation you will need to configure the allowed origins for requests to be able to use the Payload API. You can see how to set CORS as well as other payload configuration settings [here](/docs/configuration/overview)
### Limiting GraphQL Complexity
## Limiting GraphQL Complexity
Because GraphQL gives the power of query writing outside a server's control, someone with bad intentions might write a maliciously complex query and bog down your server. To prevent resource-intensive GraphQL requests, Payload provides a way specify complexity limits which are based on a complexity score that is calculated for each request.
@@ -55,7 +55,7 @@ Any GraphQL request that is calculated to be too expensive is rejected. On the P
If you do not need GraphQL it is advised that you disable it altogether with the Payload config by setting `graphQL.disable: true`. Should you wish to enable GraphQL again, you can remove this property or set it `false`, any time. By turning it off, Payload will bypass creating schemas from your collections and will not register the express route.
### Malicious File Uploads
## Malicious File Uploads
Payload does not execute uploaded files on the server, but depending on your setup it may be used to transmit and store potentially dangerous files. If your configuration allows file uploads there is the potential that a bad actor uploads a malicious file that is then served to other users. Consider the following ways to mitigate the risks.

View File

@@ -11,12 +11,13 @@ Payload provides an extremely granular querying language through all APIs. Each
<Banner>
<strong>
Here, "querying" relates to filtering or searching through documents within a Collection.
</strong>{' '}
</strong>
You can build queries to pass to Find operations as well as to [restrict which documents certain
users can access](/docs/access-control/overview) via access control functions.
</Banner>
### Simple queries
## Simple queries
For example, say you have a collection as follows:
@@ -52,7 +53,7 @@ const query = {
The above example demonstrates a simple query but you can get much more complex.
### Operators
## Operators
| Operator | Description |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -77,7 +78,7 @@ The above example demonstrates a simple query but you can get much more complex.
</strong> to a field's config which will speed up searches using that field immensely.
</Banner>
### And / Or Logic
## And / Or Logic
In addition to defining simple queries, you can join multiple queries together using simple AND / OR logic. Let's take the above `Post` collection for example and write a more complex query using AND / OR:
@@ -111,7 +112,7 @@ const query = {
Written in plain English, if the above query were passed to a `find` operation, it would translate to finding posts where either the `color` is `mint` OR the `color` is `white` AND `featured` is set to false.
### Nested properties
## Nested properties
When working with nested properties, which can happen when using relational fields, it is possible to use the dot notation to access the nested property. For example, when working with a `Song` collection that has a `artists` field which is related to an `Artists` collection using the `name: 'artists'`. You can access a property within the collection `Artists` like so:
@@ -124,7 +125,7 @@ const query = {
}
```
### GraphQL Find Queries
## GraphQL Find Queries
All GraphQL `find` queries support the `where` argument, which accepts queries exactly as detailed above.
@@ -141,7 +142,7 @@ query {
}
```
### REST Queries
## REST Queries
With the REST API, you can use the full power of Payload queries as well but they become a bit more unwieldy the more complex that they get.
@@ -177,7 +178,7 @@ const getPosts = async () => {
}
```
### Local API Queries
## Local API Queries
The Local API's `find` operation accepts an object exactly how you write it. For example:

View File

@@ -90,6 +90,19 @@ Note: Collection slugs must be formatted in kebab-case
},
},
},
{
operation: "Count",
method: "GET",
path: "/api/{collection-slug}/count",
description: "Count the documents",
example: {
slug: "count",
req: true,
res: {
totalDocs: 10
},
},
},
{
operation: "Create",
method: "POST",

View File

@@ -6,641 +6,4 @@ desc: Built by Meta, Lexical is an incredibly powerful rich text editor, and it
keywords: lexical, rich text, editor, headless cms
---
One of Payload's goals is to build the best rich text editor experience that we possibly can. We want to combine the beauty and polish of the Medium editing experience with the strength and features of the Notion editor - all in one place.
Classically, we've used SlateJS to work toward this goal, but building custom elements into Slate has proven to be more difficult than we'd like, and we've been keeping our options open.
<Banner type="warning">
Payload's Lexical rich text editor is currently in beta. It's stable enough to use as you build on
Payload, so if you're up for helping us fine-tune it, you should use it. But if you're looking for
stability, use Slate instead.
</Banner>
Lexical is extremely impressive and trivializes a lot of the hard parts of building new elements into a rich text editor. It has a few distinct advantages over Slate, including the following:
1. A "/" menu, which allows editors to easily add new elements while never leaving their keyboard
1. A "hover" toolbar that pops up if you select text
1. It supports Payload blocks natively, directly within your rich text editor
1. Custom elements, called "features", are much easier to build in Lexical vs. Slate
To use the Lexical editor, first you need to install it:
```
npm install @payloadcms/richtext-lexical
```
Once you have it installed, you can pass it to your top-level Payload config as follows:
```ts
import { buildConfig } from 'payload/config'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
export default buildConfig({
collections: [
// your collections here
],
// Pass the Lexical editor to the root config
editor: lexicalEditor({}),
})
```
You can also override Lexical settings on a field-by-field basis as follows:
```ts
import type { CollectionConfig } from 'payload/types'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
export const Pages: CollectionConfig = {
slug: 'pages',
fields: [
{
name: 'content',
type: 'richText',
// Pass the Lexical editor here and override base settings as necessary
editor: lexicalEditor({}),
},
],
}
```
## Extending the lexical editor with Features
Lexical has been designed with extensibility in mind. Whether you're aiming to introduce new functionalities or tweak the existing ones, Lexical makes it seamless for you to bring those changes to life.
### Features: The Building Blocks
At the heart of Lexical's customization potential are "features". While Lexical ships with a set of default features we believe are essential for most use cases, the true power lies in your ability to redefine, expand, or prune these as needed.
If you remove all the default features, you're left with a blank editor. You can then add in only the features you need, or you can build your own custom features from scratch.
### Integrating New Features
To weave in your custom features, utilize the `features` prop when initializing the Lexical Editor. Here's a basic example of how this is done:
```ts
import {
BlocksFeature,
LinkFeature,
UploadFeature,
lexicalEditor,
} from '@payloadcms/richtext-lexical'
import { Banner } from '../blocks/Banner'
import { CallToAction } from '../blocks/CallToAction'
{
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
LinkFeature({
// Example showing how to customize the built-in fields
// of the Link feature
fields: [
{
name: 'rel',
label: 'Rel Attribute',
type: 'select',
hasMany: true,
options: ['noopener', 'noreferrer', 'nofollow'],
admin: {
description:
'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',
},
},
],
}),
UploadFeature({
collections: {
uploads: {
// Example showing how to customize the built-in fields
// of the Upload feature
fields: [
{
name: 'caption',
type: 'richText',
editor: lexicalEditor(),
},
],
},
},
}),
// This is incredibly powerful. You can re-use your Payload blocks
// directly in the Lexical editor as follows:
BlocksFeature({
blocks: [Banner, CallToAction],
}),
],
})
}
```
## Features overview
Here's an overview of all the included features:
| Feature Name | Included by default | Description |
| ------------------------------ | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`BoldTextFeature`** | Yes | Handles the bold text format |
| **`ItalicTextFeature`** | Yes | Handles the italic text format |
| **`UnderlineTextFeature`** | Yes | Handles the underline text format |
| **`StrikethroughTextFeature`** | Yes | Handles the strikethrough text format |
| **`SubscriptTextFeature`** | Yes | Handles the subscript text format |
| **`SuperscriptTextFeature`** | Yes | Handles the superscript text format |
| **`InlineCodeTextFeature`** | Yes | Handles the inline-code text format |
| **`ParagraphFeature`** | Yes | Handles paragraphs. Since they are already a key feature of lexical itself, this Feature mainly handles the Slash and Add-Block menu entries for paragraphs |
| **`HeadingFeature`** | Yes | Adds Heading Nodes (by default, H1 - H6, but that can be customized) |
| **`AlignFeature`** | Yes | Allows you to align text left, centered and right |
| **`IndentFeature`** | Yes | Allows you to indent text with the tab key |
| **`UnorderedListFeature`** | Yes | Adds unordered lists (ul) |
| **`OrderedListFeature`** | Yes | Adds ordered lists (ol) |
| **`CheckListFeature`** | Yes | Adds checklists |
| **`LinkFeature`** | Yes | Allows you to create internal and external links |
| **`RelationshipFeature`** | Yes | Allows you to create block-level (not inline) relationships to other documents |
| **`BlockQuoteFeature`** | Yes | Allows you to create block-level quotes |
| **`UploadFeature`** | Yes | Allows you to create block-level upload nodes - this supports all kinds of uploads, not just images |
| **`BlocksFeature`** | No | Allows you to use Payload's [Blocks Field](/docs/fields/blocks) directly inside your editor. In the feature props, you can specify the allowed blocks - just like in the Blocks field. |
| **`TreeViewFeature`** | No | Adds a debug box under the editor, which allows you to see the current editor state live, the dom, as well as time travel. Very useful for debugging |
## Creating your own, custom Feature
Creating your own custom feature requires deep knowledge of the Lexical editor. We recommend you take a look at the [Lexical documentation](https://lexical.dev/docs/intro) first - especially the "concepts" section.
Next, take a look at the [features we've already built](https://github.com/payloadcms/payload/tree/main/packages/richtext-lexical/src/field/features) - understanding how they work will help you understand how to create your own. There is no difference between the features included by default and the ones you create yourself - since those features are all isolated from the "core", you have access to the same APIs, whether the feature is part of payload or not!
## Converters
### Lexical => HTML
Lexical saves data in JSON, but can also generate its HTML representation via two main methods:
1. **Outputting HTML from the Collection:** Create a new field in your collection to convert saved JSON content to HTML. Payload generates and outputs the HTML for use in your frontend.
2. **Generating HTML on any server** Convert JSON to HTML on-demand on the server.
The editor comes with built-in HTML serializers, simplifying the process of converting JSON to HTML.
#### Outputting HTML from the Collection
To add HTML generation directly within the collection, follow the example below:
```ts
import type { CollectionConfig } from 'payload/types'
import { HTMLConverterFeature, lexicalEditor, lexicalHTML } from '@payloadcms/richtext-lexical'
const Pages: CollectionConfig = {
slug: 'pages',
fields: [
{
name: 'nameOfYourRichTextField',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
// The HTMLConverter Feature is the feature which manages the HTML serializers. If you do not pass any arguments to it, it will use the default serializers.
HTMLConverterFeature({}),
],
}),
},
lexicalHTML('nameOfYourRichTextField', { name: 'nameOfYourRichTextField_html' }),
],
}
```
The `lexicalHTML()` function creates a new field that automatically converts the referenced lexical richText field into HTML through an afterRead hook.
#### Generating HTML anywhere on the server:
If you wish to convert JSON to HTML ad-hoc, use this code snippet:
```ts
import type { SerializedEditorState } from 'lexical'
import {
type SanitizedEditorConfig,
convertLexicalToHTML,
consolidateHTMLConverters,
} from '@payloadcms/richtext-lexical'
async function lexicalToHTML(
editorData: SerializedEditorState,
editorConfig: SanitizedEditorConfig,
) {
return await convertLexicalToHTML({
converters: consolidateHTMLConverters({ editorConfig }),
data: editorData,
})
}
```
This method employs `convertLexicalToHTML` from `@payloadcms/richtext-lexical`, which converts the serialized editor state into HTML.
Because every `Feature` is able to provide html converters, and because the `htmlFeature` can modify those or provide their own, we need to consolidate them with the default html Converters using the `consolidateHTMLConverters` function.
#### Creating your own HTML Converter
HTML Converters are typed as `HTMLConverter`, which contains the node type it should handle, and a function that accepts the serialized node from the lexical editor, and outputs the HTML string. Here's the HTML Converter of the Upload node as an example:
```ts
import type { HTMLConverter } from '@payloadcms/richtext-lexical'
import payload from 'payload'
const UploadHTMLConverter: HTMLConverter<SerializedUploadNode> = {
converter: async ({ node }) => {
const uploadDocument = await payload.findByID({
id: node.value.id,
collection: node.relationTo,
})
const url = (payload?.config?.serverURL || '') + uploadDocument?.url
if (!(uploadDocument?.mimeType as string)?.startsWith('image')) {
// Only images can be serialized as HTML
return ``
}
return `<img src="${url}" alt="${uploadDocument?.filename}" width="${uploadDocument?.width}" height="${uploadDocument?.height}"/>`
},
nodeTypes: [UploadNode.getType()], // This is the type of the lexical node that this converter can handle. Instead of hardcoding 'upload' we can get the node type directly from the UploadNode, since it's static.
}
```
As you can see, we have access to all the information saved in the node (for the Upload node, this is `value`and `relationTo`) and we can use that to generate the HTML.
The `convertLexicalToHTML` is part of `@payloadcms/richtext-lexical` automatically handles traversing the editor state and calling the correct converter for each node.
#### Embedding the HTML Converter in your Feature
You can embed your HTML Converter directly within your custom `Feature`, allowing it to be handled automatically by the `consolidateHTMLConverters` function. Here is an example:
```ts
export const UploadFeature = (props?: UploadFeatureProps): FeatureProvider => {
return {
feature: () => {
return {
nodes: [
{
converters: {
html: yourHTMLConverter, // <= This is where you define your HTML Converter
},
node: UploadNode,
type: UploadNode.getType(),
//...
},
],
plugins: [
/*...*/
],
props: props,
slashMenu: {
/*...*/
},
}
},
key: 'upload',
}
}
```
### Headless Editor
Lexical provides a seamless way to perform conversions between various other formats:
- HTML to Lexical (or, importing HTML into the lexical editor)
- Markdown to Lexical (or, importing Markdown into the lexical editor)
- Lexical to Markdown
A headless editor can perform such conversions outside of the main editor instance. Follow this method to initiate a headless editor:
```ts
import { createHeadlessEditor } from '@lexical/headless' // <= make sure this package is installed
import { getEnabledNodes, sanitizeEditorConfig } from '@payloadcms/richtext-lexical'
const yourEditorConfig // <= your editor config here
const headlessEditor = createHeadlessEditor({
nodes: getEnabledNodes({
editorConfig: sanitizeEditorConfig(yourEditorConfig),
}),
})
```
### Getting the editor config
As you can see, you need to provide an editor config in order to create a headless editor. This is because the editor config is used to determine which nodes & features are enabled, and which converters are used.
To get the editor config, simply import the default editor config and adjust it - just like you did inside of the `editor: lexicalEditor({})` property:
```ts
import { defaultEditorConfig, defaultEditorFeatures } from '@payloadcms/richtext-lexical' // <= make sure this package is installed
const yourEditorConfig = defaultEditorConfig
// If you made changes to the features of the field's editor config, you should also make those changes here:
yourEditorConfig.features = [
...defaultEditorFeatures,
// Add your custom features here
]
```
### HTML => Lexical
Once you have your headless editor instance, you can use it to convert HTML to Lexical:
```ts
import { $generateNodesFromDOM } from '@lexical/html'
import { $getRoot, $getSelection } from 'lexical'
import { JSDOM } from 'jsdom'
headlessEditor.update(
() => {
// In a headless environment you can use a package such as JSDom to parse the HTML string.
const dom = new JSDOM(htmlString)
// Once you have the DOM instance it's easy to generate LexicalNodes.
const nodes = $generateNodesFromDOM(headlessEditor, dom.window.document)
// Select the root
$getRoot().select()
// Insert them at a selection.
const selection = $getSelection()
selection.insertNodes(nodes)
},
{ discrete: true },
)
// Do this if you then want to get the editor JSON
const editorJSON = headlessEditor.getEditorState().toJSON()
```
Functions prefixed with a `$` can only be run inside of an `editor.update()` or `editorState.read()` callback.
This has been taken from the [lexical serialization & deserialization docs](https://lexical.dev/docs/concepts/serialization#html---lexical).
<Banner type="success">
<strong>Note:</strong>
<br />
Using the <code>discrete: true</code> flag ensures instant updates to the editor state. If
immediate reading of the updated state isn't necessary, you can omit the flag.
</Banner>
### Markdown => Lexical
Convert markdown content to the Lexical editor format with the following:
```ts
import { $convertFromMarkdownString } from '@lexical/markdown'
import { sanitizeEditorConfig } from '@payloadcms/richtext-lexical'
const yourSanitizedEditorConfig = sanitizeEditorConfig(yourEditorConfig) // <= your editor config here
const markdown = `# Hello World`
headlessEditor.update(
() => {
$convertFromMarkdownString(markdown, yourSanitizedEditorConfig.features.markdownTransformers)
},
{ discrete: true },
)
// Do this if you then want to get the editor JSON
const editorJSON = headlessEditor.getEditorState().toJSON()
```
### Lexical => Markdown
Export content from the Lexical editor into Markdown format using these steps:
1. Import your current editor state into the headless editor.
2. Convert and fetch the resulting markdown string.
Here's the code for it:
```ts
import { $convertToMarkdownString } from '@lexical/markdown'
import { sanitizeEditorConfig } from '@payloadcms/richtext-lexical'
import type { SerializedEditorState } from 'lexical'
const yourSanitizedEditorConfig = sanitizeEditorConfig(yourEditorConfig) // <= your editor config here
const yourEditorState: SerializedEditorState // <= your current editor state here
// Import editor state into your headless editor
try {
headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState)) // This should commit the editor state immediately
} catch (e) {
logger.error({ err: e }, 'ERROR parsing editor state')
}
// Export to markdown
let markdown: string
headlessEditor.getEditorState().read(() => {
markdown = $convertToMarkdownString(yourSanitizedEditorConfig?.features?.markdownTransformers)
})
```
The `.setEditorState()` function immediately updates your editor state. Thus, there's no need for the `discrete: true` flag when reading the state afterward.
### Lexical => Plain Text
Export content from the Lexical editor into plain text using these steps:
1. Import your current editor state into the headless editor.
2. Convert and fetch the resulting plain text string.
Here's the code for it:
```ts
import type { SerializedEditorState } from 'lexical'
import { $getRoot } from 'lexical'
const yourEditorState: SerializedEditorState // <= your current editor state here
// Import editor state into your headless editor
try {
headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState)) // This should commit the editor state immediately
} catch (e) {
logger.error({ err: e }, 'ERROR parsing editor state')
}
// Export to plain text
const plainTextContent =
headlessEditor.getEditorState().read(() => {
return $getRoot().getTextContent()
}) || ''
```
## Migrating from Slate
While both Slate and Lexical save the editor state in JSON, the structure of the JSON is different.
### Migration via SlateToLexicalFeature
One way to handle this is to just give your lexical editor the ability to read the slate JSON.
Simply add the `SlateToLexicalFeature` to your editor:
```ts
import type { CollectionConfig } from 'payload/types'
import { SlateToLexicalFeature, lexicalEditor } from '@payloadcms/richtext-lexical'
const Pages: CollectionConfig = {
slug: 'pages',
fields: [
{
name: 'nameOfYourRichTextField',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [...defaultFeatures, SlateToLexicalFeature({})],
}),
},
],
}
```
and done! Now, everytime this lexical editor is initialized, it converts the slate date to lexical on-the-fly. If the data is already in lexical format, it will just pass it through.
This is by far the easiest way to migrate from Slate to Lexical, although it does come with a few caveats:
- There is a performance hit when initializing the lexical editor
- The editor will still output the Slate data in the output JSON, as the on-the-fly converter only runs for the admin panel
The easy way to solve this: Just save the document! This overrides the slate data with the lexical data, and the next time the document is loaded, the lexical data will be used. This solves both the performance and the output issue for that specific document.
### Migration via migration script
The method described above does not solve the issue for all documents, though. If you want to convert all your documents to lexical, you can use a migration script. Here's a simple example:
```ts
import type { Payload } from 'payload'
import type { YourDocumentType } from 'payload/generated-types'
import {
cloneDeep,
convertSlateToLexical,
defaultSlateConverters,
} from '@payloadcms/richtext-lexical'
import { AnotherCustomConverter } from './lexicalFeatures/converters/AnotherCustomConverter'
export async function convertAll(payload: Payload, collectionName: string, fieldName: string) {
const docs: YourDocumentType[] = await payload.db.collections[collectionName].find({}).exec() // Use MongoDB models directly to query all documents at once
console.log(`Found ${docs.length} ${collectionName} docs`)
const converters = cloneDeep([...defaultSlateConverters, AnotherCustomConverter])
// Split docs into batches of 20.
const batchSize = 20
const batches = []
for (let i = 0; i < docs.length; i += batchSize) {
batches.push(docs.slice(i, i + batchSize))
}
let processed = 0 // Number of processed docs
for (const batch of batches) {
// Process each batch asynchronously
const promises = batch.map(async (doc: YourDocumentType) => {
const richText = doc[fieldName]
if (richText && Array.isArray(richText) && !('root' in richText)) {
// It's Slate data - skip already-converted data
const converted = convertSlateToLexical({
converters: converters,
slateData: richText,
})
await payload.update({
id: doc.id,
collection: collectionName as any,
data: {
[fieldName]: converted,
},
})
}
})
// Wait for all promises in the batch to complete. Resolving batches of 20 asynchronously is faster than waiting for each doc to update individually
await Promise.all(promises)
// Update the count of processed docs
processed += batch.length
console.log(`Converted ${processed} of ${docs.length}`)
}
}
```
The `convertSlateToLexical` is the same method used in the `SlateToLexicalFeature` - it handles traversing the Slate JSON for you.
Do note that this script might require adjustment depending on your document structure, especially if you have nested richText fields or localization enabled.
### Converting custom Slate nodes
If you have custom Slate nodes, create a custom converter for them. Here's the Upload converter as an example:
```ts
import type { SerializedUploadNode } from '../uploadNode.'
import type { SlateNodeConverter } from '@payloadcms/richtext-lexical'
export const SlateUploadConverter: SlateNodeConverter = {
converter({ slateNode }) {
return {
fields: {
...slateNode.fields,
},
format: '',
relationTo: slateNode.relationTo,
type: 'upload',
value: {
id: slateNode.value?.id || '',
},
version: 1,
} as const as SerializedUploadNode
},
nodeTypes: ['upload'],
}
```
It's pretty simple: You get a Slate node as input, and you return the lexical node. The `nodeTypes` array is used to determine which Slate nodes this converter can handle.
When using a migration script, you can add your custom converters to the `converters` property of the `convertSlateToLexical` props, as seen in the example above
When using the `SlateToLexicalFeature`, you can add your custom converters to the `converters` property of the `SlateToLexicalFeature` props:
```ts
import type { CollectionConfig } from 'payload/types'
import {
SlateToLexicalFeature,
lexicalEditor,
defaultSlateConverters,
} from '@payloadcms/richtext-lexical'
import { YourCustomConverter } from '../converters/YourCustomConverter'
const Pages: CollectionConfig = {
slug: 'pages',
fields: [
{
name: 'nameOfYourRichTextField',
type: 'richText',
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
SlateToLexicalFeature({
converters: [...defaultSlateConverters, YourCustomConverter],
}),
],
}),
},
],
}
```
## Migrating from payload-plugin-lexical
Migrating from [payload-plugin-lexical](https://github.com/AlessioGr/payload-plugin-lexical) works similar to migrating from Slate.
Instead of a `SlateToLexicalFeature` there is a `LexicalPluginToLexicalFeature` you can use. And instead of `convertSlateToLexical` you can use `convertLexicalPluginToLexical`.
## Coming Soon
Lots more documentation will be coming soon, which will show in detail how to create your own custom features within Lexical.
For now, take a look at the TypeScript interfaces and let us know if you need a hand. Much more will be coming from the Payload team on this topic soon.
The new lexical docs can be found at [Lexical](/docs/lexical/overview).

View File

@@ -9,7 +9,7 @@ keywords: slatejs, lexical, rich text, json, custom editor, javascript, typescri
Payload currently supports two official rich text editors and you can choose either one depending on your needs.
1. [SlateJS](/docs/rich-text/slate) - stable, backwards-compatible with 1.0
2. [Lexical](/docs/rich-text/lexical) - beta, where things will be moving in the future
2. [Lexical](/docs/lexical/overview) - beta, where things will be moving in the future
These editors are built on an "adapter pattern" which means that you will need to install the editor you'd like to use. Take a look at the docs for the editor you'd like to use for instructions on how to install it.

Some files were not shown because too many files have changed in this diff Show More