Compare commits

...

30 Commits

Author SHA1 Message Date
Elliot DeNolf
63edecddd8 chore(release): bundler-webpack/1.0.3 2023-10-11 14:41:17 -04:00
Elliot DeNolf
65cdf6bfd0 fix(bundler-webpack): pnpm webpack aliases 2023-10-11 14:39:44 -04:00
Elliot DeNolf
9e831a6a00 chore(deps): revert drizzle-kit bump 2023-10-11 11:53:27 -04:00
Elliot DeNolf
0b64c5fb66 chore(deps): bump graphql, graphql-request, mongoose, drizzle-kit 2023-10-11 11:20:58 -04:00
James Mikrut
40426a25df Update CHANGELOG.md 2023-10-11 11:07:16 -04:00
James Mikrut
c91b1e8310 Merge pull request #3578 from payloadcms/fix/#3570
fix: postgres select fields within groups (#3570)
2023-10-11 11:00:49 -04:00
James
06e2fa9d11 fix: postgres select fields within groups (#3570) 2023-10-11 10:47:10 -04:00
Nathan Clevenger
c6925ec29f Fix typo on graphql-schema.mdx (#3548) 2023-10-11 10:25:20 -04:00
Leonard Struck
8f46b31249 fix "crop" translation de (#3567) 2023-10-11 10:24:43 -04:00
James Mikrut
e3c776523a fix: #3511, documents don't delete their versions (#3520)
Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2023-10-11 10:20:57 -04:00
Dan Ribbens
c09e9d96cf fix(db-postgres): update password error (#3575) 2023-10-11 10:19:00 -04:00
Dan Ribbens
aabc0650f8 fix(db-postgres): drafts cannot be saved because not null constraint (#3547) 2023-10-11 10:18:44 -04:00
Elliot DeNolf
4dd4e9aaae chore(deps): script deps 2023-10-11 10:16:06 -04:00
Elliot DeNolf
3b63b7fc3c chore: update contributing.md links 2023-10-10 22:31:25 -04:00
Alexander
f0e2e78b82 fix(i18n): "crop" translation for ru and ua (#3566) 2023-10-10 19:31:36 -04:00
Elliot DeNolf
a154adf066 chore(examples): update nodemon to respond to prompts 2023-10-10 18:37:33 -04:00
Elliot DeNolf
d86bcc1495 chore: update publish script 2023-10-10 17:48:42 -04:00
Elliot DeNolf
2b7043c6e6 chore(release): payload/2.0.4 2023-10-10 17:45:36 -04:00
Jarrod Flesch
e0afeeca97 fix: API tab breadcrumbs and results indentation (#3564) 2023-10-10 17:33:15 -04:00
Jacob Fletcher
76e306ddd8 fix: sticky sidebar (#3563) 2023-10-10 17:20:11 -04:00
Jacob Fletcher
cfc78ed4f5 fix: sidebar width when fields have long descriptions (#3562) 2023-10-10 17:04:54 -04:00
Elliot DeNolf
64b0db5a7a chore(readme): announcement link adjustment 2023-10-10 16:14:40 -04:00
Elliot DeNolf
48600f0c66 chore(release): db-mongodb/1.0.3 2023-10-10 16:09:53 -04:00
Elliot DeNolf
6d2dd5849d chore(release): db-postgres/0.1.3 2023-10-10 16:09:24 -04:00
Jacob Fletcher
6d9353b53f fix: row field margins (#3558) 2023-10-10 16:00:41 -04:00
James Mikrut
1914be75aa Merge pull request #3555 from payloadcms/fix/#3521
fix: #3521
2023-10-10 15:59:52 -04:00
Elliot DeNolf
b17f627e02 chore(release): richtext-slate/1.0.2 2023-10-10 15:59:33 -04:00
Elliot DeNolf
bef79621ee chore(release): db-postgres/0.1.2 2023-10-10 15:59:33 -04:00
Jarrod Flesch
af892ecb0e fix: removes nested array field configs from array value (#3549)
* fix: array controls 'addBelow' was adding above
2023-10-10 15:55:00 -04:00
James
eb97acd408 fix: #3521 2023-10-10 14:46:52 -04:00
60 changed files with 794 additions and 435 deletions

View File

@@ -2,7 +2,7 @@
<!-- Please include a summary of the pull request and any related issues it fixes. Please also include relevant motivation and context. -->
- [ ] I have read and understand the [CONTRIBUTING.md](../CONTRIBUTING.md) document in this repository.
- [ ] I have read and understand the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository.
## Type of change

View File

@@ -121,6 +121,12 @@ This means that in some fringe cases, if you are creating a doc and then instant
To avoid any issues, you can pass the `req.transactionID` through to your Local API calls, so that your Local API calls are included as part of the parent transaction.
### ⚠️ Locales now have more functionality, and in some places, you might need to update custom code
Payload's locales have become more powerful and now allow you to customize more aspects per locale such as a human-friendly label and if the locale is RTL or not.
This means that certain functions now return a different shape, such as `useLocale`. This hook used to return a string of the locale code you are currently editing, but it now returns an object with type of `Locale`.
### ⚠️ Admin panel CSS classes may have changed
The revisions we've made in 2.0 required changes to both HTML and CSS within the admin panel. For this reason, if you were loading custom CSS into the admin panel to customize the look and feel, your stylesheets may need to be updated. If your CSS is targeting elements on the page using HTML selectors or class names, you may need to update these selectors based on the current markup. It may also be necessary to update your style definitions if the core Payload component you are targeting has undergone significant change.

View File

@@ -26,7 +26,9 @@
</h4>
<hr/>
### 🎉 Payload 2.0 is now available! Read more in the [announcement post](https://payloadcms.com/blog/payload-2-0).
<h3>
🎉 Payload 2.0 is now available! Read more in the <a target="_blank" href="https://payloadcms.com/blog/payload-2-0" rel="dofollow"><strong>announcement post</strong></a>
</h3>
<h3>Benefits over a regular CMS</h3>
<ul>

View File

@@ -347,7 +347,7 @@ The `useForm` hook returns an object with the following properties: |
value: <strong><code>rowIndex</code></strong>,
},
{
value: "The index of the row to add",
value: "The index of the row to add. If omitted, the row will be added to the end of the array.",
},
],
[

View File

@@ -65,7 +65,7 @@ After a user logs in, they can change their language selection in the `/account`
<strong>Note:</strong>
<br />
If there is a language that Payload does not yet support, we accept code
[contributions](https://github.com/payloadcms/payload/blob/main/contributing.md).
[contributions](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md).
</Banner>
### Node Express

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 generatation script
### Schema generation script
Run the following command in a Payload project to generate your project's GraphQL schema from Payload:

View File

@@ -1,4 +1,5 @@
{
"ext": "ts",
"exec": "ts-node src/server.ts"
"exec": "ts-node src/server.ts -- -I",
"stdin": false
}

View File

@@ -1,5 +1,6 @@
{
"watch": ["server.ts"],
"exec": "ts-node --project tsconfig.server.json src/server.ts",
"ext": "js ts"
"exec": "ts-node --project tsconfig.server.json src/server.ts -- -I",
"ext": "js ts",
"stdin": false
}

View File

@@ -1,4 +1,5 @@
{
"ext": "ts",
"exec": "ts-node src/server.ts"
"exec": "ts-node src/server.ts -- -I",
"stdin": false
}

View File

@@ -1,4 +1,5 @@
{
"ext": "ts",
"exec": "ts-node src/server.ts"
"exec": "ts-node src/server.ts -- -I",
"stdin": false
}

View File

@@ -1,4 +1,5 @@
{
"ext": "ts",
"exec": "ts-node src/server.ts"
"exec": "ts-node src/server.ts -- -I",
"stdin": false
}

View File

@@ -1,4 +1,5 @@
{
"ext": "ts",
"exec": "ts-node src/server.ts"
"exec": "ts-node src/server.ts -- -I",
"stdin": false
}

View File

@@ -1,4 +1,5 @@
{
"ext": "ts",
"exec": "ts-node src/server.ts"
"exec": "ts-node src/server.ts -- -I",
"stdin": false
}

View File

@@ -1,4 +1,5 @@
{
"ext": "ts",
"exec": "ts-node src/server.ts"
"exec": "ts-node src/server.ts -- -I",
"stdin": false
}

View File

@@ -1,4 +1,5 @@
{
"ext": "ts",
"exec": "ts-node src/server.ts"
"exec": "ts-node src/server.ts -- -I",
"stdin": false
}

View File

@@ -50,6 +50,7 @@
"@types/shelljs": "0.8.12",
"@types/testing-library__jest-dom": "5.14.8",
"chalk": "^5.3.0",
"chalk-template": "1.1.0",
"copyfiles": "2.4.1",
"cross-env": "7.0.3",
"dotenv": "8.6.0",
@@ -58,7 +59,6 @@
"fs-extra": "10.1.0",
"get-port": "5.1.1",
"glob": "8.1.0",
"graphql-request": "6.1.0",
"husky": "^8.0.3",
"isomorphic-fetch": "3.0.0",
"jest": "29.6.4",
@@ -77,6 +77,7 @@
"slash": "3.0.0",
"slate": "0.91.4",
"ts-node": "10.9.1",
"tsx": "^3.13.0",
"turbo": "^1.10.15",
"typescript": "5.2.2",
"uuid": "^9.0.0"

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/bundler-webpack",
"version": "1.0.2",
"version": "1.0.3",
"description": "The officially supported Webpack bundler adapter for Payload",
"repository": "https://github.com/payloadcms/payload",
"license": "MIT",
@@ -25,6 +25,7 @@
"css-loader": "5.2.7",
"css-minimizer-webpack-plugin": "^5.0.0",
"file-loader": "6.2.0",
"find-node-modules": "^2.1.3",
"html-webpack-plugin": "^5.5.0",
"md5": "2.3.0",
"mini-css-extract-plugin": "1.6.2",
@@ -48,6 +49,7 @@
"devDependencies": {
"@payloadcms/eslint-config": "workspace:*",
"@types/extract-text-webpack-plugin": "^3.0.7",
"@types/find-node-modules": "^2.1.0",
"@types/html-webpack-plugin": "^3.2.6",
"@types/mini-css-extract-plugin": "^1.4.3",
"@types/optimize-css-assets-webpack-plugin": "^5.0.5",

View File

@@ -1,6 +1,7 @@
import type { SanitizedConfig } from 'payload/config'
import type { Configuration } from 'webpack'
import findNodeModules from 'find-node-modules'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import path from 'path'
import webpack from 'webpack'
@@ -8,7 +9,8 @@ import webpack from 'webpack'
const mockModulePath = path.resolve(__dirname, '../mocks/emptyModule.js')
const mockDotENVPath = path.resolve(__dirname, '../mocks/dotENV.js')
const nodeModulesPath = path.resolve(__dirname, '../../../../')
const nodeModulesPaths = findNodeModules({ cwd: process.cwd() })
const nodeModulesPath = path.resolve(nodeModulesPaths[0])
const adminFolderPath = path.resolve(nodeModulesPath, 'payload/dist/admin')
export const getBaseConfig = (payloadConfig: SanitizedConfig): Configuration => ({

View File

@@ -23,7 +23,9 @@ export const getDevConfig = (payloadConfig: SanitizedConfig): Configuration => {
entry: {
...baseConfig.entry,
main: [
`webpack-hot-middleware/client?path=${payloadConfig.routes.admin}/__webpack_hmr`,
`${require.resolve('webpack-hot-middleware/client')}?path=${
payloadConfig.routes.admin
}/__webpack_hmr`,
...(baseConfig.entry.main as string[]),
],
},

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-mongodb",
"version": "1.0.2",
"version": "1.0.3",
"description": "The officially supported MongoDB database adapter for Payload",
"repository": "https://github.com/payloadcms/payload",
"license": "MIT",
@@ -23,7 +23,7 @@
"bson-objectid": "2.0.4",
"deepmerge": "4.3.1",
"get-port": "5.1.1",
"mongoose": "6.11.4",
"mongoose": "6.12.0",
"mongoose-aggregate-paginate-v2": "1.0.6",
"mongoose-paginate-v2": "1.7.22",
"prompts": "2.4.2",

View File

@@ -68,7 +68,9 @@ export type MongooseAdapter = BaseDatabaseAdapter &
type MongooseAdapterResult = (args: { payload: Payload }) => MongooseAdapter
declare module 'payload' {
export interface DatabaseAdapter extends Args {
export interface DatabaseAdapter
extends Omit<BaseDatabaseAdapter, 'sessions'>,
Omit<Args, 'migrationDir'> {
collections: {
[slug: string]: CollectionModel
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-postgres",
"version": "0.1.1",
"version": "0.1.3",
"description": "The officially supported Postgres database adapter for Payload",
"repository": "https://github.com/payloadcms/payload",
"license": "MIT",

View File

@@ -24,6 +24,7 @@ export const init: Init = async function init(this: PostgresAdapter) {
buildTable({
adapter: this,
buildRelationships: true,
disableNotNull: !!collection?.versions?.drafts,
disableUnique: false,
fields: collection.fields,
tableName,
@@ -37,6 +38,7 @@ export const init: Init = async function init(this: PostgresAdapter) {
buildTable({
adapter: this,
buildRelationships: true,
disableNotNull: !!collection.versions?.drafts,
disableUnique: true,
fields: versionFields,
tableName: versionsTableName,
@@ -51,6 +53,7 @@ export const init: Init = async function init(this: PostgresAdapter) {
buildTable({
adapter: this,
buildRelationships: true,
disableNotNull: !!global?.versions?.drafts,
disableUnique: false,
fields: global.fields,
tableName,
@@ -64,6 +67,7 @@ export const init: Init = async function init(this: PostgresAdapter) {
buildTable({
adapter: this,
buildRelationships: true,
disableNotNull: !!global.versions?.drafts,
disableUnique: true,
fields: versionFields,
tableName: versionsTableName,

View File

@@ -27,6 +27,7 @@ type Args = {
baseColumns?: Record<string, PgColumnBuilder>
baseExtraConfig?: Record<string, (cols: GenericColumns) => IndexBuilder | UniqueConstraintBuilder>
buildRelationships?: boolean
disableNotNull: boolean
disableUnique: boolean
fields: Field[]
rootRelationsToBuild?: Map<string, string>
@@ -46,6 +47,7 @@ export const buildTable = ({
baseColumns = {},
baseExtraConfig = {},
buildRelationships,
disableNotNull,
disableUnique = false,
fields,
rootRelationsToBuild,
@@ -102,6 +104,7 @@ export const buildTable = ({
adapter,
buildRelationships,
columns,
disableNotNull,
disableUnique,
fields,
indexes,

View File

@@ -34,6 +34,7 @@ type Args = {
buildRelationships: boolean
columnPrefix?: string
columns: Record<string, PgColumnBuilder>
disableNotNull: boolean
disableUnique?: boolean
fieldPrefix?: string
fields: (Field | TabAsField)[]
@@ -62,6 +63,7 @@ export const traverseFields = ({
buildRelationships,
columnPrefix,
columns,
disableNotNull,
disableUnique = false,
fieldPrefix,
fields,
@@ -174,7 +176,7 @@ export const traverseFields = ({
case 'radio':
case 'select': {
const enumName = `enum_${newTableName}_${columnPrefix || ''}${toSnakeCase(field.name)}`
const enumName = `enum_${newTableName}_${toSnakeCase(field.name)}`
adapter.enums[enumName] = pgEnum(
enumName,
@@ -188,7 +190,7 @@ export const traverseFields = ({
)
if (field.type === 'select' && field.hasMany) {
const selectTableName = `${newTableName}_${toSnakeCase(fieldName)}`
const selectTableName = `${newTableName}_${toSnakeCase(field.name)}`
const baseColumns: Record<string, PgColumnBuilder> = {
order: integer('order').notNull(),
parent: parentIDColumnMap[parentIDColType]('parent_id')
@@ -218,6 +220,7 @@ export const traverseFields = ({
adapter,
baseColumns,
baseExtraConfig,
disableNotNull,
disableUnique,
fields: [],
tableName: selectTableName,
@@ -274,6 +277,7 @@ export const traverseFields = ({
adapter,
baseColumns,
baseExtraConfig,
disableNotNull,
disableUnique,
fields: field.fields,
rootRelationsToBuild,
@@ -339,6 +343,7 @@ export const traverseFields = ({
adapter,
baseColumns,
baseExtraConfig,
disableNotNull,
disableUnique,
fields: block.fields,
rootRelationsToBuild,
@@ -399,6 +404,7 @@ export const traverseFields = ({
buildRelationships,
columnPrefix,
columns,
disableNotNull,
disableUnique,
fieldPrefix,
fields: field.fields,
@@ -432,6 +438,7 @@ export const traverseFields = ({
buildRelationships,
columnPrefix: `${columnName}_`,
columns,
disableNotNull,
disableUnique,
fieldPrefix: `${fieldName}_`,
fields: field.fields,
@@ -466,6 +473,7 @@ export const traverseFields = ({
buildRelationships,
columnPrefix,
columns,
disableNotNull,
disableUnique,
fieldPrefix,
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
@@ -502,6 +510,7 @@ export const traverseFields = ({
buildRelationships,
columnPrefix,
columns,
disableNotNull,
disableUnique,
fieldPrefix,
fields: field.fields,
@@ -544,7 +553,13 @@ export const traverseFields = ({
const condition = field.admin && field.admin.condition
if (targetTable[fieldName] && 'required' in field && field.required && !condition) {
if (
!disableNotNull &&
targetTable[fieldName] &&
'required' in field &&
field.required &&
!condition
) {
targetTable[fieldName].notNull()
}
})

View File

@@ -69,7 +69,9 @@ export type MigrateUpArgs = { payload: Payload }
export type MigrateDownArgs = { payload: Payload }
declare module 'payload' {
export interface DatabaseAdapter extends Omit<Args, 'pool'> {
export interface DatabaseAdapter
extends Omit<Args, 'migrationDir' | 'pool'>,
BaseDatabaseAdapter {
drizzle: DrizzleDB
enums: Record<string, GenericEnum>
pool: Pool

View File

@@ -26,7 +26,9 @@
</h4>
<hr/>
### 🎉 Payload 2.0 is now available! Read more in the [announcement post](https://payloadcms.com/blog/payload-2-0).
<h3>
🎉 Payload 2.0 is now available! Read more in the <a target="_blank" href="https://payloadcms.com/blog/payload-2-0" rel="dofollow"><strong>announcement post</strong></a>
</h3>
<h3>Benefits over a regular CMS</h3>
<ul>

View File

@@ -1,6 +1,6 @@
{
"name": "payload",
"version": "2.0.3",
"version": "2.0.4",
"description": "Node, React and MongoDB Headless CMS and Application Framework",
"license": "MIT",
"main": "./dist/index.js",
@@ -77,7 +77,7 @@
"flatley": "5.2.0",
"fs-extra": "10.1.0",
"get-tsconfig": "4.6.2",
"graphql": "16.7.1",
"graphql": "16.8.1",
"graphql-http": "1.21.0",
"graphql-playground-middleware-express": "1.7.23",
"graphql-query-complexity": "0.12.0",
@@ -187,7 +187,7 @@
"file-loader": "6.2.0",
"form-data": "3.0.1",
"get-port": "5.1.1",
"graphql-request": "3.7.0",
"graphql-request": "6.1.0",
"mini-css-extract-plugin": "1.6.2",
"node-fetch": "2.6.12",
"nodemon": "3.0.1",

View File

@@ -66,7 +66,7 @@ export const ArrayAction: React.FC<Props> = ({
<PopupList.Button
className={`${baseClass}__action ${baseClass}__add`}
onClick={() => {
addRow(index)
addRow(index + 1)
close()
}}
>

View File

@@ -132,7 +132,9 @@ export function fieldReducer(state: Fields, action: FieldAction): Fields {
}
case 'ADD_ROW': {
const { blockType, path, rowIndex, subFieldState } = action
const { blockType, path, rowIndex: rowIndexFromArgs, subFieldState } = action
const rowIndex =
typeof rowIndexFromArgs === 'number' ? rowIndexFromArgs : state[path]?.rows?.length || 0
const rowsMetadata = [...(state[path]?.rows || [])]
rowsMetadata.splice(
@@ -155,19 +157,22 @@ export function fieldReducer(state: Fields, action: FieldAction): Fields {
}
}
const { remainingFields, rows } = separateRows(path, state)
// add new row to array _field state_
const { remainingFields, rows: siblingRows } = separateRows(path, state)
siblingRows.splice(rowIndex, 0, subFieldState)
// actual form state (value saved in db)
rows.splice(rowIndex, 0, subFieldState)
// add new row to array _value_
const currentValue = (Array.isArray(state[path]?.value) ? state[path]?.value : []) as Fields[]
const newValue = currentValue.splice(rowIndex, 0, reduceFieldsToValues(subFieldState, true))
const newState: Fields = {
...remainingFields,
...flattenRows(path, rows),
...flattenRows(path, siblingRows),
[path]: {
...state[path],
disableFormData: true,
rows: rowsMetadata,
value: rows,
value: newValue,
},
}
@@ -176,8 +181,8 @@ export function fieldReducer(state: Fields, action: FieldAction): Fields {
case 'REPLACE_ROW': {
const { blockType, path, rowIndex: rowIndexArg, subFieldState } = action
const { remainingFields, rows } = separateRows(path, state)
const rowIndex = Math.max(0, Math.min(rowIndexArg, rows?.length - 1 || 0))
const { remainingFields, rows: siblingRows } = separateRows(path, state)
const rowIndex = Math.max(0, Math.min(rowIndexArg, siblingRows?.length - 1 || 0))
const rowsMetadata = [...(state[path]?.rows || [])]
rowsMetadata[rowIndex] = {
@@ -195,17 +200,21 @@ export function fieldReducer(state: Fields, action: FieldAction): Fields {
}
}
// replace form field state
rows[rowIndex] = subFieldState
// replace form _field state_
siblingRows[rowIndex] = subFieldState
// replace array _value_
const newValue = Array.isArray(state[path]?.value) ? state[path]?.value : []
newValue[rowIndex] = reduceFieldsToValues(subFieldState, true)
const newState: Fields = {
...remainingFields,
...flattenRows(path, rows),
...flattenRows(path, siblingRows),
[path]: {
...state[path],
disableFormData: true,
rows: rowsMetadata,
value: rows,
value: newValue,
},
}

View File

@@ -431,7 +431,7 @@ const Form: React.FC<Props> = (props) => {
[],
)
const getRowConfigByPath = React.useCallback(
const getRowSchemaByPath = React.useCallback(
({ blockType, path }: { blockType?: string; path: string }) => {
const rowConfig = traverseRowConfigs({
fieldConfig: collection?.fields || global?.fields,
@@ -449,23 +449,24 @@ const Form: React.FC<Props> = (props) => {
const addFieldRow: Context['addFieldRow'] = useCallback(
async ({ data, path, rowIndex }) => {
const preferences = await getDocPreferences()
const fieldConfig = getRowConfigByPath({
const rowSchema = getRowSchemaByPath({
blockType: data?.blockType,
path,
})
if (fieldConfig) {
if (rowSchema) {
const subFieldState = await buildStateFromSchema({
id,
config,
data,
fieldSchema: fieldConfig,
fieldSchema: rowSchema,
locale,
operation,
preferences,
t,
user,
})
dispatchFields({
blockType: data?.blockType,
path,
@@ -475,11 +476,11 @@ const Form: React.FC<Props> = (props) => {
})
}
},
[dispatchFields, getDocPreferences, id, user, operation, locale, t, getRowConfigByPath, config],
[dispatchFields, getDocPreferences, id, user, operation, locale, t, getRowSchemaByPath, config],
)
const removeFieldRow: Context['removeFieldRow'] = useCallback(
async ({ path, rowIndex }) => {
({ path, rowIndex }) => {
dispatchFields({ path, rowIndex, type: 'REMOVE_ROW' })
},
[dispatchFields],
@@ -488,17 +489,17 @@ const Form: React.FC<Props> = (props) => {
const replaceFieldRow: Context['replaceFieldRow'] = useCallback(
async ({ data, path, rowIndex }) => {
const preferences = await getDocPreferences()
const fieldConfig = getRowConfigByPath({
const rowSchema = getRowSchemaByPath({
blockType: data?.blockType,
path,
})
if (fieldConfig) {
if (rowSchema) {
const subFieldState = await buildStateFromSchema({
id,
config,
data,
fieldSchema: fieldConfig,
fieldSchema: rowSchema,
locale,
operation,
preferences,
@@ -514,7 +515,7 @@ const Form: React.FC<Props> = (props) => {
})
}
},
[dispatchFields, getDocPreferences, id, user, operation, locale, t, getRowConfigByPath, config],
[dispatchFields, getDocPreferences, id, user, operation, locale, t, getRowSchemaByPath, config],
)
const getFields = useCallback(() => contextRef.current.fields, [contextRef])

View File

@@ -12,9 +12,9 @@ export const separateRows = (path: string, fields: Fields): Result => {
const newRows = incomingRows
if (fieldPath.indexOf(`${path}.`) === 0) {
const index = Number(fieldPath.replace(`${path}.`, '').split('.')[0])
if (!newRows[index]) newRows[index] = {}
newRows[index][fieldPath.replace(`${path}.${String(index)}.`, '')] = { ...field }
const [rowIndex] = fieldPath.replace(`${path}.`, '').split('.')
if (!newRows[rowIndex]) newRows[rowIndex] = {}
newRows[rowIndex][fieldPath.replace(`${path}.${String(rowIndex)}.`, '')] = { ...field }
} else {
remainingFields[fieldPath] = field
}

View File

@@ -174,7 +174,10 @@ export type Context = {
}: {
data?: Data
path: string
rowIndex: number
/*
* by default the new row will be added to the end of the list
*/
rowIndex?: number
}) => Promise<void>
buildRowErrors: () => void
createFormData: CreateFormData
@@ -190,7 +193,7 @@ export type Context = {
getField: GetField
getFields: GetFields
getSiblingData: GetSiblingData
removeFieldRow: ({ path, rowIndex }: { path: string; rowIndex: number }) => Promise<void>
removeFieldRow: ({ path, rowIndex }: { path: string; rowIndex: number }) => void
replaceFieldRow: ({
data,
path,

View File

@@ -5,9 +5,9 @@ import type { Props } from './types'
import { createNestedFieldPath } from '../../Form/createNestedFieldPath'
import RenderFields from '../../RenderFields'
import withCondition from '../../withCondition'
import { fieldBaseClass } from '../shared'
import './index.scss'
import { RowProvider } from './provider'
import { fieldBaseClass } from '../shared'
const Row: React.FC<Props> = (props) => {
const {
@@ -29,7 +29,6 @@ const Row: React.FC<Props> = (props) => {
}))}
fieldTypes={fieldTypes}
indexPath={indexPath}
margins={false}
permissions={permissions}
readOnly={readOnly}
/>

View File

@@ -7,6 +7,10 @@
gap: calc(var(--base) * 2);
align-items: flex-start;
ul {
padding-left: calc(var(--base) * 1);
}
&--fullscreen {
padding-left: 0;
.query-inspector__configuration {
@@ -173,6 +177,10 @@
}
}
&__row-line--nested {
margin-left: 25px;
}
&__bracket {
position: relative;

View File

@@ -1,6 +1,8 @@
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import type { EditViewProps } from '../types'
import { Chevron } from '../..'
import { requests } from '../../../api'
import CopyToClipboard from '../../elements/CopyToClipboard'
@@ -11,6 +13,7 @@ import { MinimizeMaximize } from '../../icons/MinimizeMaximize'
import { useConfig } from '../../utilities/Config'
import { useDocumentInfo } from '../../utilities/DocumentInfo'
import { useLocale } from '../../utilities/Locale'
import { SetStepNav } from '../collections/Edit/SetStepNav'
import './index.scss'
const chars = {
@@ -119,12 +122,19 @@ const RecursivelyRenderObjectData = ({
}
if (type === 'date' || type === 'string' || type === 'null' || type === 'number') {
const parentHasKey = Boolean(parentType === 'object' && key)
const rowClasses = [
`${baseClass}__row-line`,
`${baseClass}__value-type--${type}`,
`${baseClass}__row-line--${objectKey ? 'nested' : 'top'}`,
]
.filter(Boolean)
.join(' ')
return (
<li
className={`${baseClass}__row-line ${baseClass}__value-type--${type}`}
key={`${key}-${keyIndex}`}
>
{parentType === 'object' ? <span>{`"${key}": `}</span> : null}
<li className={rowClasses} key={`${key}-${keyIndex}`}>
{parentHasKey ? <span>{`"${key}": `}</span> : null}
{type === 'string' ? (
<span className={`${baseClass}__value`}>{`"${value}"`}</span>
@@ -156,7 +166,8 @@ function createURL(url: string) {
}
}
export const API = ({ apiURL }) => {
export const API: React.FC<EditViewProps> = (props) => {
const { apiURL } = props
const { i18n } = useTranslation()
const {
localization,
@@ -201,8 +212,15 @@ export const API = ({ apiURL }) => {
const classes = [baseClass, fullscreen && `${baseClass}--fullscreen`].filter(Boolean).join(' ')
let isEditing: boolean
if ('collection' in props) {
isEditing = props?.isEditing
}
return (
<Gutter className={classes} right={false}>
<SetStepNav collection={collection} global={global} id={id} isEditing={isEditing} />
<div className={`${baseClass}__configuration`}>
<div className={`${baseClass}__api-url`}>
<span className={`${baseClass}__label`}>

View File

@@ -49,8 +49,9 @@
&__sidebar-wrap {
position: sticky;
top: 0;
flex-grow: 1;
top: var(--doc-controls-height);
width: 33.33%;
height: 100%;
}
&__sidebar {

View File

@@ -53,8 +53,9 @@
&__sidebar-wrap {
position: sticky;
top: 0;
flex-grow: 1;
top: var(--doc-controls-height);
width: 33.33%;
height: 100%;
}
&__sidebar {

View File

@@ -13,7 +13,7 @@ import { useConfig } from '../../../utilities/Config'
export const SetStepNav: React.FC<
| {
collection: SanitizedCollectionConfig
id: string
id: number | string
isEditing: boolean
}
| {
@@ -27,7 +27,7 @@ export const SetStepNav: React.FC<
let pluralLabel: SanitizedCollectionConfig['labels']['plural']
let slug: string
let isEditing = false
let id: string | undefined
let id: number | string | undefined
if ('collection' in props) {
const {
@@ -70,7 +70,7 @@ export const SetStepNav: React.FC<
if (isEditing) {
nav.push({
label: useAsTitle && useAsTitle !== 'id' ? title || `[${t('untitled')}]` : id,
label: useAsTitle && useAsTitle !== 'id' ? title || `[${t('untitled')}]` : `${id}`,
})
} else {
nav.push({

View File

@@ -139,6 +139,19 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
t,
})
// /////////////////////////////////////
// Delete versions
// /////////////////////////////////////
if (collectionConfig.versions) {
await deleteCollectionVersions({
id,
payload,
req,
slug: collectionConfig.slug,
})
}
// /////////////////////////////////////
// Delete document
// /////////////////////////////////////
@@ -153,19 +166,6 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
},
})
// /////////////////////////////////////
// Delete versions
// /////////////////////////////////////
if (collectionConfig.versions) {
await deleteCollectionVersions({
id,
payload,
req,
slug: collectionConfig.slug,
})
}
// /////////////////////////////////////
// afterRead - Fields
// /////////////////////////////////////

View File

@@ -109,6 +109,19 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
t,
})
// /////////////////////////////////////
// Delete versions
// /////////////////////////////////////
if (collectionConfig.versions) {
await deleteCollectionVersions({
id,
payload,
req,
slug: collectionConfig.slug,
})
}
// /////////////////////////////////////
// Delete document
// /////////////////////////////////////
@@ -130,19 +143,6 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
req,
})
// /////////////////////////////////////
// Delete versions
// /////////////////////////////////////
if (collectionConfig.versions) {
await deleteCollectionVersions({
id,
payload,
req,
slug: collectionConfig.slug,
})
}
// /////////////////////////////////////
// afterRead - Fields
// /////////////////////////////////////

View File

@@ -243,8 +243,8 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
const { hash, salt } = await generatePasswordSaltHash({ password })
dataToUpdate.salt = salt
dataToUpdate.hash = hash
delete dataToUpdate.password
delete data.password
delete result.password
}
// /////////////////////////////////////

View File

@@ -273,7 +273,7 @@
"near": "in der Nähe"
},
"upload": {
"crop": "Ernte",
"crop": "Zuschneiden",
"cropToolDescription": "Ziehen Sie die Ecken des ausgewählten Bereichs, zeichnen Sie einen neuen Bereich oder passen Sie die Werte unten an.",
"dragAndDrop": "Ziehen Sie eine Datei per Drag-and-Drop",
"dragAndDropHere": "oder ziehe eine Datei hier",
@@ -368,4 +368,4 @@
"viewingVersions": "Betrachte Versionen für {{entityLabel}} {{documentTitle}}",
"viewingVersionsGlobal": "`Betrachte Versionen für das Globale Dokument {{entityLabel}}"
}
}
}

View File

@@ -273,7 +273,7 @@
"near": "рядом"
},
"upload": {
"crop": "Урожай",
"crop": "Обрезать",
"cropToolDescription": "Перетащите углы выбранной области, нарисуйте новую область или отрегулируйте значения ниже.",
"dragAndDrop": "Перетащите файл",
"dragAndDropHere": "или перетащите файл сюда",
@@ -368,4 +368,4 @@
"viewingVersions": "Просмотр версий для {{entityLabel}} {{documentTitle}}",
"viewingVersionsGlobal": "`Просмотр версии для глобальной Коллекции {{entityLabel}}"
}
}
}

View File

@@ -273,7 +273,7 @@
"near": "поруч"
},
"upload": {
"crop": "Врожай",
"crop": "Обрізати",
"cropToolDescription": "Перетягніть кути обраної області, намалюйте нову область або скоригуйте значення нижче.",
"dragAndDrop": "Перемістіть файл",
"dragAndDropHere": "або перемістіть сюди файл",
@@ -368,4 +368,4 @@
"viewingVersions": "Огляд версій для {{entityLabel}} {{documentTitle}}",
"viewingVersionsGlobal": "Огляд версій для глобальної колекції {{entityLabel}}"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/richtext-slate",
"version": "1.0.1",
"version": "1.0.2",
"description": "The officially supported Slate richtext adapter for Payload",
"repository": "https://github.com/payloadcms/payload",
"license": "MIT",

226
pnpm-lock.yaml generated
View File

@@ -69,6 +69,9 @@ importers:
chalk:
specifier: ^5.3.0
version: 5.3.0
chalk-template:
specifier: 1.1.0
version: 1.1.0
copyfiles:
specifier: 2.4.1
version: 2.4.1
@@ -93,9 +96,6 @@ importers:
glob:
specifier: 8.1.0
version: 8.1.0
graphql-request:
specifier: 6.1.0
version: 6.1.0(graphql@16.7.1)
husky:
specifier: ^8.0.3
version: 8.0.3
@@ -150,6 +150,9 @@ importers:
ts-node:
specifier: 10.9.1
version: 10.9.1(@swc/core@1.3.76)(@types/node@20.5.7)(typescript@5.2.2)
tsx:
specifier: ^3.13.0
version: 3.13.0
turbo:
specifier: ^1.10.15
version: 1.10.15
@@ -199,6 +202,9 @@ importers:
packages/bundler-webpack:
dependencies:
'@swc/core':
specifier: '>= 1.3.76'
version: 1.3.76
compression:
specifier: 1.7.4
version: 1.7.4
@@ -214,6 +220,9 @@ importers:
file-loader:
specifier: 6.2.0
version: 6.2.0(webpack@5.88.2)
find-node-modules:
specifier: ^2.1.3
version: 2.1.3
html-webpack-plugin:
specifier: ^5.5.0
version: 5.5.3(webpack@5.88.2)
@@ -278,6 +287,9 @@ importers:
'@types/extract-text-webpack-plugin':
specifier: ^3.0.7
version: 3.0.7
'@types/find-node-modules':
specifier: ^2.1.0
version: 2.1.0
'@types/html-webpack-plugin':
specifier: ^3.2.6
version: 3.2.7
@@ -312,8 +324,8 @@ importers:
specifier: 5.1.1
version: 5.1.1
mongoose:
specifier: 6.11.4
version: 6.11.4
specifier: 6.12.0
version: 6.12.0
mongoose-aggregate-paginate-v2:
specifier: 1.0.6
version: 1.0.6
@@ -546,23 +558,23 @@ importers:
specifier: 4.6.2
version: 4.6.2
graphql:
specifier: 16.7.1
version: 16.7.1
specifier: 16.8.1
version: 16.8.1
graphql-http:
specifier: 1.21.0
version: 1.21.0(graphql@16.7.1)
version: 1.21.0(graphql@16.8.1)
graphql-playground-middleware-express:
specifier: 1.7.23
version: 1.7.23(express@4.18.2)
graphql-query-complexity:
specifier: 0.12.0
version: 0.12.0(graphql@16.7.1)
version: 0.12.0(graphql@16.8.1)
graphql-scalars:
specifier: 1.22.2
version: 1.22.2(graphql@16.7.1)
version: 1.22.2(graphql@16.8.1)
graphql-type-json:
specifier: 0.3.2
version: 0.3.2(graphql@16.7.1)
version: 0.3.2(graphql@16.8.1)
html-webpack-plugin:
specifier: 5.5.3
version: 5.5.3(webpack@5.88.2)
@@ -871,8 +883,8 @@ importers:
specifier: 5.1.1
version: 5.1.1
graphql-request:
specifier: 3.7.0
version: 3.7.0(graphql@16.7.1)
specifier: 6.1.0
version: 6.1.0(graphql@16.8.1)
mini-css-extract-plugin:
specifier: 1.6.2
version: 1.6.2(webpack@5.88.2)
@@ -2411,7 +2423,7 @@ packages:
resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==}
dependencies:
'@esbuild-kit/core-utils': 3.3.2
get-tsconfig: 4.7.0
get-tsconfig: 4.7.2
dev: false
/@esbuild/android-arm64@0.18.20:
@@ -2678,12 +2690,12 @@ packages:
resolution: {integrity: sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA==}
dev: false
/@graphql-typed-document-node/core@3.2.0(graphql@16.7.1):
/@graphql-typed-document-node/core@3.2.0(graphql@16.8.1):
resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==}
peerDependencies:
graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
dependencies:
graphql: 16.7.1
graphql: 16.8.1
dev: true
/@hapi/hoek@9.3.0:
@@ -3327,7 +3339,6 @@ packages:
requiresBuild: true
dependencies:
sparse-bitfield: 3.0.3
dev: true
optional: true
/@nodelib/fs.scandir@2.1.5:
@@ -4324,6 +4335,10 @@ packages:
'@types/webpack': 4.41.33
dev: true
/@types/find-node-modules@2.1.0:
resolution: {integrity: sha512-rnh1kFUbwp5xEu54GhGDnzxm5/iJC3irypLO9+YECIal+XXKU5ki4I1h0Sm43KNpvKzFfyYFeGOm7o6tGI9Bgw==}
dev: true
/@types/fs-extra@11.0.2:
resolution: {integrity: sha512-c0hrgAOVYr21EX8J0jBMXGLMgJqVf/v6yxi0dLaJboW9aQPh16Id+z6w2Tx1hm+piJOLv8xPfVKZCLfjPw/IMQ==}
dependencies:
@@ -4495,7 +4510,7 @@ packages:
/@types/mongoose-aggregate-paginate-v2@1.0.9:
resolution: {integrity: sha512-YKDKtSuE1vzMY/SAtlDTWJr52UhTYdrOypCqyx7T2xFYEWfybLnV98m4ZoVgYJH0XowVl7Y2Gnn6p1sF+3NbLA==}
dependencies:
mongoose: 6.11.4
mongoose: 6.12.0
transitivePeerDependencies:
- aws-crt
- supports-color
@@ -6044,6 +6059,13 @@ packages:
/caniuse-lite@1.0.30001535:
resolution: {integrity: sha512-48jLyUkiWFfhm/afF7cQPqPjaUmSraEhK4j+FCTJpgnGGEZHqyLe3hmWH7lIooZdSzXL0ReMvHz0vKDoTBsrwg==}
/chalk-template@1.1.0:
resolution: {integrity: sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==}
engines: {node: '>=14.16'}
dependencies:
chalk: 5.3.0
dev: true
/chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
@@ -7114,6 +7136,11 @@ packages:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
/detect-file@1.0.0:
resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==}
engines: {node: '>=0.10.0'}
dev: false
/detect-libc@2.0.2:
resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
engines: {node: '>=8'}
@@ -8135,6 +8162,13 @@ packages:
engines: {node: '>=6'}
dev: false
/expand-tilde@2.0.2:
resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==}
engines: {node: '>=0.10.0'}
dependencies:
homedir-polyfill: 1.0.3
dev: false
/expect@29.7.0:
resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -8224,11 +8258,6 @@ packages:
tmp: 0.0.33
dev: true
/extract-files@9.0.0:
resolution: {integrity: sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==}
engines: {node: ^10.17.0 || ^12.0.0 || >= 13.7.0}
dev: true
/fast-copy@3.0.1:
resolution: {integrity: sha512-Knr7NOtK3HWRYGtHoJrjkaWepqT8thIVGAwt0p0aUs1zqkAzXZV4vo9fFNwyb5fcqK1GKYFYxldQdIDVKhUAfA==}
dev: false
@@ -8387,6 +8416,13 @@ packages:
pkg-dir: 4.2.0
dev: true
/find-node-modules@2.1.3:
resolution: {integrity: sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==}
dependencies:
findup-sync: 4.0.0
merge: 2.1.1
dev: false
/find-root@1.1.0:
resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==}
dev: false
@@ -8427,6 +8463,16 @@ packages:
semver-regex: 4.0.5
dev: true
/findup-sync@4.0.0:
resolution: {integrity: sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==}
engines: {node: '>= 8'}
dependencies:
detect-file: 1.0.0
is-glob: 4.0.3
micromatch: 4.0.5
resolve-dir: 1.0.1
dev: false
/flat-cache@3.1.0:
resolution: {integrity: sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==}
engines: {node: '>=12.0.0'}
@@ -8634,11 +8680,10 @@ packages:
resolve-pkg-maps: 1.0.0
dev: false
/get-tsconfig@4.7.0:
resolution: {integrity: sha512-pmjiZ7xtB8URYm74PlGJozDNyhvsVLUcpBa8DZBG3bWHwaHa9bPiRpiSfovw+fjhwONSCWKRyk+JQHEGZmMrzw==}
/get-tsconfig@4.7.2:
resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==}
dependencies:
resolve-pkg-maps: 1.0.0
dev: false
/get-uri@6.0.1:
resolution: {integrity: sha512-7ZqONUVqaabogsYNWlYj0t3YZaL6dhuEueZXGF+/YVmf6dHmaFg8/6psJKqhx9QykIDKzpGcy2cn4oV4YC7V/Q==}
@@ -8755,6 +8800,26 @@ packages:
ini: 2.0.0
dev: true
/global-modules@1.0.0:
resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==}
engines: {node: '>=0.10.0'}
dependencies:
global-prefix: 1.0.2
is-windows: 1.0.2
resolve-dir: 1.0.1
dev: false
/global-prefix@1.0.2:
resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==}
engines: {node: '>=0.10.0'}
dependencies:
expand-tilde: 2.0.2
homedir-polyfill: 1.0.3
ini: 1.3.8
is-windows: 1.0.2
which: 1.3.1
dev: false
/globals@11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
@@ -8866,13 +8931,13 @@ packages:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
dev: false
/graphql-http@1.21.0(graphql@16.7.1):
/graphql-http@1.21.0(graphql@16.8.1):
resolution: {integrity: sha512-yrItPfHj5WeT4n7iusbVin+vGSQjXFAX6U/GnYytdCJRXVad1TWGtYFDZ2ROjCKpXQzIwvfbiWCEwfuXgR3B6A==}
engines: {node: '>=12'}
peerDependencies:
graphql: '>=0.11 <=16'
dependencies:
graphql: 16.7.1
graphql: 16.8.1
dev: false
/graphql-playground-html@1.6.30:
@@ -8890,60 +8955,47 @@ packages:
graphql-playground-html: 1.6.30
dev: false
/graphql-query-complexity@0.12.0(graphql@16.7.1):
/graphql-query-complexity@0.12.0(graphql@16.8.1):
resolution: {integrity: sha512-fWEyuSL6g/+nSiIRgIipfI6UXTI7bAxrpPlCY1c0+V3pAEUo1ybaKmSBgNr1ed2r+agm1plJww8Loig9y6s2dw==}
peerDependencies:
graphql: ^14.6.0 || ^15.0.0 || ^16.0.0
dependencies:
graphql: 16.7.1
graphql: 16.8.1
lodash.get: 4.4.2
dev: false
/graphql-request@3.7.0(graphql@16.7.1):
resolution: {integrity: sha512-dw5PxHCgBneN2DDNqpWu8QkbbJ07oOziy8z+bK/TAXufsOLaETuVO4GkXrbs0WjhdKhBMN3BkpN/RIvUHkmNUQ==}
peerDependencies:
graphql: 14 - 16
dependencies:
cross-fetch: 3.1.8
extract-files: 9.0.0
form-data: 3.0.1
graphql: 16.7.1
transitivePeerDependencies:
- encoding
dev: true
/graphql-request@6.1.0(graphql@16.7.1):
/graphql-request@6.1.0(graphql@16.8.1):
resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==}
peerDependencies:
graphql: 14 - 16
dependencies:
'@graphql-typed-document-node/core': 3.2.0(graphql@16.7.1)
'@graphql-typed-document-node/core': 3.2.0(graphql@16.8.1)
cross-fetch: 3.1.8
graphql: 16.7.1
graphql: 16.8.1
transitivePeerDependencies:
- encoding
dev: true
/graphql-scalars@1.22.2(graphql@16.7.1):
/graphql-scalars@1.22.2(graphql@16.8.1):
resolution: {integrity: sha512-my9FB4GtghqXqi/lWSVAOPiTzTnnEzdOXCsAC2bb5V7EFNQjVjwy3cSSbUvgYOtDuDibd+ZsCDhz+4eykYOlhQ==}
engines: {node: '>=10'}
peerDependencies:
graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
dependencies:
graphql: 16.7.1
graphql: 16.8.1
tslib: 2.6.2
dev: false
/graphql-type-json@0.3.2(graphql@16.7.1):
/graphql-type-json@0.3.2(graphql@16.8.1):
resolution: {integrity: sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg==}
peerDependencies:
graphql: '>=0.8.0'
dependencies:
graphql: 16.7.1
graphql: 16.8.1
dev: false
/graphql@16.7.1:
resolution: {integrity: sha512-DRYR9tf+UGU0KOsMcKAlXeFfX89UiiIZ0dRU3mR0yJfu6OjZqUcp68NnFLnqQU5RexygFoDy1EW+ccOYcPfmHg==}
/graphql@16.8.1:
resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==}
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
/gzip-size@6.0.0:
@@ -9051,6 +9103,13 @@ packages:
react-is: 16.13.1
dev: false
/homedir-polyfill@1.0.3:
resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==}
engines: {node: '>=0.10.0'}
dependencies:
parse-passwd: 1.0.0
dev: false
/hosted-git-info@2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
dev: true
@@ -9660,6 +9719,11 @@ packages:
call-bind: 1.0.2
get-intrinsic: 1.2.1
/is-windows@1.0.2:
resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==}
engines: {node: '>=0.10.0'}
dev: false
/is-wsl@2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'}
@@ -10884,6 +10948,10 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
/merge@2.1.1:
resolution: {integrity: sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==}
dev: false
/method-override@3.0.0:
resolution: {integrity: sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==}
engines: {node: '>= 0.10'}
@@ -11106,19 +11174,6 @@ packages:
- supports-color
dev: true
/mongodb@4.16.0:
resolution: {integrity: sha512-0EB113Fsucaq1wsY0dOhi1fmZOwFtLOtteQkiqOXGklvWMnSH3g2QS53f0KTP+/6qOkuoXE2JksubSZNmxeI+g==}
engines: {node: '>=12.9.0'}
dependencies:
bson: 4.7.2
mongodb-connection-string-url: 2.6.0
socks: 2.7.1
optionalDependencies:
'@aws-sdk/credential-providers': 3.414.0
saslprep: 1.0.3
transitivePeerDependencies:
- aws-crt
/mongodb@4.17.1:
resolution: {integrity: sha512-MBuyYiPUPRTqfH2dV0ya4dcr2E5N52ocBuZ8Sgg/M030nGF78v855B3Z27mZJnp8PxjnUquEnAtjOsphgMZOlQ==}
engines: {node: '>=12.9.0'}
@@ -11131,7 +11186,6 @@ packages:
'@mongodb-js/saslprep': 1.1.0
transitivePeerDependencies:
- aws-crt
dev: true
/mongoose-aggregate-paginate-v2@1.0.6:
resolution: {integrity: sha512-UuALu+mjhQa1K9lMQvjLL3vm3iALvNw8PQNIh2gp1b+tO5hUa0NC0Wf6/8QrT9PSJVTihXaD8hQVy3J4e0jO0Q==}
@@ -11143,13 +11197,13 @@ packages:
engines: {node: '>=4.0.0'}
dev: false
/mongoose@6.11.4:
resolution: {integrity: sha512-q9NaW9/BBYZofx80SqlR7uoSR09CS3g02y+KMj1lNLUxcFFsPshupY3WWisNFauYG9gyuDF4L/RgyIK3obSghg==}
/mongoose@6.12.0:
resolution: {integrity: sha512-sd/q83C6TBRPBrrD2A/POSbA/exbCFM2WOuY7Lf2JuIJFlHFG39zYSDTTAEiYlzIfahNOLmXPxBGFxdAch41Mw==}
engines: {node: '>=12.0.0'}
dependencies:
bson: 4.7.2
kareem: 2.5.1
mongodb: 4.16.0
mongodb: 4.17.1
mpath: 0.9.0
mquery: 4.0.3
ms: 2.1.3
@@ -11756,6 +11810,11 @@ packages:
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
/parse-passwd@1.0.0:
resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==}
engines: {node: '>=0.10.0'}
dev: false
/parse-path@7.0.0:
resolution: {integrity: sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==}
dependencies:
@@ -13531,6 +13590,14 @@ packages:
dependencies:
resolve-from: 5.0.0
/resolve-dir@1.0.1:
resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==}
engines: {node: '>=0.10.0'}
dependencies:
expand-tilde: 2.0.2
global-modules: 1.0.0
dev: false
/resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -13545,7 +13612,6 @@ packages:
/resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
dev: false
/resolve.exports@2.0.2:
resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==}
@@ -13682,14 +13748,6 @@ packages:
truncate-utf8-bytes: 1.0.2
dev: false
/saslprep@1.0.3:
resolution: {integrity: sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==}
engines: {node: '>=6'}
requiresBuild: true
dependencies:
sparse-bitfield: 3.0.3
optional: true
/sass-loader@12.6.0(sass@1.64.0)(webpack@5.88.2):
resolution: {integrity: sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==}
engines: {node: '>= 12.13.0'}
@@ -14749,6 +14807,17 @@ packages:
typescript: 5.2.2
dev: false
/tsx@3.13.0:
resolution: {integrity: sha512-rjmRpTu3as/5fjNq/kOkOtihgLxuIz6pbKdj9xwP4J5jOLkBxw/rjN5ANw+KyrrOXV5uB7HC8+SrrSJxT65y+A==}
hasBin: true
dependencies:
esbuild: 0.18.20
get-tsconfig: 4.7.2
source-map-support: 0.5.21
optionalDependencies:
fsevents: 2.3.3
dev: true
/tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
dependencies:
@@ -15436,7 +15505,6 @@ packages:
hasBin: true
dependencies:
isexe: 2.0.0
dev: true
/which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}

View File

@@ -3,89 +3,126 @@ import { ExecSyncOptions, execSync } from 'child_process'
import chalk from 'chalk'
import prompts from 'prompts'
import minimist from 'minimist'
import chalkTemplate from 'chalk-template'
const execOpts: ExecSyncOptions = { stdio: 'inherit' }
const args = minimist(process.argv.slice(2))
async function main() {
const args = minimist(process.argv.slice(2))
const { _: packageNames } = args
// If packageNames contains a comma, parse
if (packageNames[0]?.includes(',')) {
packageNames.length = 0
const splitNames = process.argv[2].split(',')
packageNames.push(...splitNames)
}
const { _: packageNames, tag = 'latest', bump = 'patch' } = args
if (packageNames.length === 0) {
console.error('Please specify a package to publish')
process.exit(1)
abort('Please specify a package to publish')
}
if (packageNames.length > 1 && packageNames.find((p) => p === 'payload')) {
console.error(chalk.bold.red('Cannot publish payload with other packages'))
process.exit(1)
if (packageNames.find((p) => p === 'payload' && packageNames.length > 1)) {
abort('Cannot publish payload with other packages')
}
console.log(`\n${chalk.bold.green('Publishing packages:')}\n`)
console.log(`${packageNames.map((p) => ` ${p}`).join('\n')}`)
console.log('\n')
// Get current version of each package from package.json
const packageDetails = await Promise.all(
packageNames.map(async (packageName) => {
const packageDir = `packages/${packageName}`
if (!(await fse.pathExists(packageDir))) {
abort(`Package path ${packageDir} does not exist`)
}
const packageObj = await fse.readJson(`${packageDir}/package.json`)
const { confirm } = await prompts(
{
name: 'confirm',
initial: false,
message: `Publish ${packageNames.length} package(s)?`,
type: 'confirm',
},
{
onCancel: () => {
console.log(chalk.bold.red('\nAborted'))
process.exit(0)
},
},
return { name: packageName, version: packageObj.version, dir: packageDir }
}),
)
if (!confirm) {
console.log(chalk.bold.red('\nAborted\n'))
process.exit(0)
console.log(chalkTemplate`
{bold.green Publishing packages:}
{bold.yellow Bump: ${bump}}
{bold.yellow Tag: ${tag}}
${packageDetails.map((p) => ` ${p.name} - current: ${p.version}`).join('\n')}
`)
const confirmPublish = await confirm(`Publish ${packageNames.length} package(s)?`)
if (!confirmPublish) {
abort()
}
const results: { name: string; success: boolean }[] = []
for (const packageName of packageNames) {
const packageDir = `packages/${packageName}`
try {
console.log(chalk.bold(`Publishing ${packageName}...`))
execSync(`npm --prefix ${packageDir} version pre --preid beta`, execOpts)
execSync(`git add ${packageDir}/package.json`, execOpts)
for (const pkg of packageDetails) {
const { dir, name } = pkg
const packageObj = await fse.readJson(`${packageDir}/package.json`)
try {
console.log(chalk.bold(`\n\nPublishing ${name}...\n\n`))
execSync(`npm --no-git-tag-version --prefix ${dir} version ${bump}`, execOpts)
execSync(`git add ${dir}/package.json`, execOpts)
const packageObj = await fse.readJson(`${dir}/package.json`)
const newVersion = packageObj.version
execSync(`git commit -m "chore(release): ${packageName}@${newVersion}"`, execOpts)
execSync(`pnpm publish -C ${packageDir} --tag latest --no-git-checks`, execOpts)
results.push({ name: packageName, success: true })
const tagName = `${name}/${newVersion}`
execSync(`git commit -m "chore(release): ${tagName}"`, execOpts)
execSync(`git tag -a ${tagName} -m "${tagName}"`, execOpts)
execSync(`pnpm publish -C ${dir} --no-git-checks`, execOpts)
results.push({ name, success: true })
} catch (error) {
console.error(`ERROR: ${error.message}`)
results.push({ name: packageName, success: false })
console.error(chalk.bold.red(`ERROR: ${error.message}`))
results.push({ name, success: false })
}
}
console.log('\n')
console.log(`${chalk.bold('Results:')}\n`)
console.log(
results
.map(
({ name, success }) =>
` ${success ? chalk.bold.green('✔') : chalk.bold.red('✘')} ${name}`,
)
.join('\n'),
console.log(chalkTemplate`
{bold.green Results:}
${results
.map(({ name, success }) => ` ${success ? chalk.bold.green('✔') : chalk.bold.red('✘')} ${name}`)
.join('\n')}
`)
// Show unpushed commits and tags
execSync(
`git log --oneline $(git rev-parse --abbrev-ref --symbolic-full-name @{u})..HEAD`,
execOpts,
)
console.log('\n')
const push = await confirm(`Push commits and tags?`)
if (push) {
console.log(chalk.bold(`\n\nPushing commits and tags...\n\n`))
execSync(`git push --follow-tags`, execOpts)
}
console.log(chalk.bold.green(`\n\nDone!\n\n`))
}
main().catch((error) => {
console.error(error)
process.exit(1)
})
async function abort(message = 'Abort', exitCode = 1) {
console.error(chalk.bold.red(`\n${message}\n`))
process.exit(exitCode)
}
async function confirm(message: string): Promise<boolean> {
const { confirm } = await prompts(
{
name: 'confirm',
initial: false,
message,
type: 'confirm',
},
{
onCancel: () => {
abort()
},
},
)
return confirm
}

View File

@@ -151,6 +151,8 @@ export default buildConfigWithDefaults({
type: 'text',
admin: {
position: 'sidebar',
description:
'This is a very long description that takes many characters to complete and hopefully will wrap instead of push the sidebar open, lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, voluptatum voluptates. Quisquam, voluptatum voluptates.',
},
},
],

View File

@@ -42,11 +42,11 @@ describe('Auth', () => {
// language=graphQL
const query = `mutation {
loginUser(email: "${devUser.email}", password: "${devUser.password}") {
token
user {
id
email
}
token
user {
id
email
}
}
}`
const response = await client.request(query)
@@ -62,7 +62,7 @@ describe('Auth', () => {
it('should have fields saved to JWT', async () => {
const decoded = jwtDecode<User>(token)
const { email: jwtEmail, collection, roles, iat, exp } = decoded
const { collection, email: jwtEmail, exp, iat, roles } = decoded
expect(jwtEmail).toBeDefined()
expect(collection).toEqual('users')
@@ -132,6 +132,19 @@ describe('Auth', () => {
loggedInUser = data.user
})
it('should allow a user to change password without returning password', async () => {
const result = await payload.update({
id: loggedInUser.id,
collection: slug,
data: {
password: 'test',
},
})
expect(result.id).toStrictEqual(loggedInUser.id)
expect(result.password).toBeUndefined()
})
it('should return a logged in user from /me', async () => {
const response = await fetch(`${apiUrl}/${slug}/me`, {
headers: {
@@ -149,16 +162,16 @@ describe('Auth', () => {
it('should have fields saved to JWT', async () => {
const decoded = jwtDecode<User>(token)
const {
email: jwtEmail,
collection,
email: jwtEmail,
exp,
iat,
roles,
[saveToJWTKey]: customJWTPropertyKey,
'x-lifted-from-group': liftedFromGroup,
'x-tab-field': unnamedTabSaveToJWTString,
tabLiftedSaveToJWT,
unnamedTabSaveToJWTFalse,
iat,
exp,
'x-lifted-from-group': liftedFromGroup,
'x-tab-field': unnamedTabSaveToJWTString,
} = decoded
const group = decoded['x-group'] as Record<string, unknown>
@@ -190,9 +203,9 @@ describe('Auth', () => {
const user = await payload.create({
collection: slug,
data: {
apiKey,
email: 'dev@example.com',
password: 'test',
apiKey,
},
})
@@ -212,10 +225,10 @@ describe('Auth', () => {
it('should refresh a token and reset its expiration', async () => {
const response = await fetch(`${apiUrl}/${slug}/refresh-token`, {
method: 'post',
headers: {
Authorization: `JWT ${token}`,
},
method: 'post',
})
const data = await response.json()
@@ -228,18 +241,18 @@ describe('Auth', () => {
expect(loggedInUser?.custom).toBe('Hello, world!')
await payload.update({
collection: slug,
id: loggedInUser?.id || '',
collection: slug,
data: {
custom: 'Goodbye, world!',
},
})
const response = await fetch(`${apiUrl}/${slug}/refresh-token`, {
method: 'post',
headers: {
Authorization: `JWT ${token}`,
},
method: 'post',
})
const data = await response.json()
@@ -303,7 +316,7 @@ describe('Auth', () => {
},
})
const { _verified, _verificationToken } = userResult.docs[0]
const { _verificationToken, _verified } = userResult.docs[0]
expect(_verified).toBe(false)
expect(_verificationToken).toBeDefined()
@@ -331,7 +344,7 @@ describe('Auth', () => {
},
})
const { _verified: afterVerified, _verificationToken: afterToken } =
const { _verificationToken: afterToken, _verified: afterVerified } =
afterVerifyResult.docs[0]
expect(afterVerified).toBe(true)
expect(afterToken).toBeNull()
@@ -374,8 +387,8 @@ describe('Auth', () => {
password,
}),
headers: {
'Content-Type': 'application/json',
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
},
method: 'post',
})
@@ -396,7 +409,7 @@ describe('Auth', () => {
},
})
const { loginAttempts, lockUntil } = userResult.docs[0]
const { lockUntil, loginAttempts } = userResult.docs[0]
expect(loginAttempts).toBe(2)
expect(lockUntil).toBeDefined()
@@ -409,14 +422,14 @@ describe('Auth', () => {
await payload.update({
collection: slug,
data: {
lockUntil: Date.now() - 605 * 1000,
},
where: {
email: {
equals: userEmail,
},
},
data: {
lockUntil: Date.now() - 605 * 1000,
},
})
// login
@@ -443,7 +456,7 @@ describe('Auth', () => {
},
})
const { loginAttempts, lockUntil } = userResult.docs[0]
const { lockUntil, loginAttempts } = userResult.docs[0]
expect(loginAttempts).toBe(0)
expect(lockUntil).toBeNull()
@@ -454,13 +467,13 @@ describe('Auth', () => {
it('should allow forgot-password by email', async () => {
// TODO: Spy on payload sendEmail function
const response = await fetch(`${apiUrl}/${slug}/forgot-password`, {
method: 'post',
body: JSON.stringify({
email,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'post',
})
// expect(mailSpy).toHaveBeenCalled();
@@ -495,10 +508,10 @@ describe('Auth', () => {
const user = await payload.create({
collection: slug,
data: {
adminOnlyField: 'admin secret',
email: 'insecure@me.com',
password: 'test',
roles: ['admin'],
adminOnlyField: 'admin secret',
},
})
@@ -520,8 +533,8 @@ describe('Auth', () => {
expect(adminMe.user.adminOnlyField).toEqual('admin secret')
await payload.update({
collection: slug,
id: user?.id || '',
collection: slug,
data: {
roles: ['editor'],
},
@@ -546,8 +559,8 @@ describe('Auth', () => {
const success = await fetch(`${apiUrl}/api-keys/${user2.id}`, {
headers: {
'Content-Type': 'application/json',
Authorization: `api-keys API-Key ${user2.apiKey}`,
'Content-Type': 'application/json',
},
}).then((res) => res.json())
@@ -555,8 +568,8 @@ describe('Auth', () => {
const fail = await fetch(`${apiUrl}/api-keys/${user1.id}`, {
headers: {
'Content-Type': 'application/json',
Authorization: `api-keys API-Key ${user2.apiKey}`,
'Content-Type': 'application/json',
},
})

View File

@@ -10,8 +10,6 @@ export const AddCustomBlocks: React.FC = () => {
const { addFieldRow, replaceFieldRow } = useForm()
const { value } = useField({ path: 'customBlocks' })
const nextIndex = Array.isArray(value) ? value.length : 0
return (
<div className={baseClass}>
<div className={`${baseClass}__blocks-grid`}>
@@ -21,7 +19,6 @@ export const AddCustomBlocks: React.FC = () => {
addFieldRow({
data: { block1Title: 'Block 1: Prefilled Title', blockType: 'block-1' },
path: 'customBlocks',
rowIndex: nextIndex,
})
}
type="button"
@@ -35,7 +32,6 @@ export const AddCustomBlocks: React.FC = () => {
addFieldRow({
data: { block2Title: 'Block 2: Prefilled Title', blockType: 'block-2' },
path: 'customBlocks',
rowIndex: nextIndex,
})
}
type="button"
@@ -51,12 +47,12 @@ export const AddCustomBlocks: React.FC = () => {
replaceFieldRow({
data: { block1Title: 'REPLACED BLOCK', blockType: 'block-1' },
path: 'customBlocks',
rowIndex: nextIndex - 1,
rowIndex: (Array.isArray(value) ? value.length : 0) - 1,
})
}
type="button"
>
Replace Block {nextIndex}
Replace Block {Array.isArray(value) ? value.length : 0}
</button>
</div>
</div>

View File

@@ -1,8 +1,7 @@
import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types'
import { CollapsibleLabelComponent } from './LabelComponent'
export const collapsibleFieldsSlug = 'collapsible-fields'
import { collapsibleFieldsSlug } from './shared'
const CollapsibleFields: CollectionConfig = {
slug: collapsibleFieldsSlug,

View File

@@ -0,0 +1 @@
export const collapsibleFieldsSlug = 'collapsible-fields'

View File

@@ -122,12 +122,30 @@ const SelectFields: CollectionConfig = {
type: 'select',
options: ['One', 'Two', 'Three'],
},
{
type: 'group',
name: 'settings',
fields: [
{
name: 'category',
type: 'select',
hasMany: true,
options: [
{ value: 'a', label: 'A' },
{ value: 'b', label: 'B' },
],
},
],
},
],
}
export const selectsDoc = {
select: 'one',
selectHasMany: ['two', 'four'],
settings: {
category: ['a'],
},
}
export default SelectFields

View File

@@ -9,7 +9,7 @@ import wait from '../../packages/payload/src/utilities/wait'
import { saveDocAndAssert, saveDocHotkeyAndAssert } from '../helpers'
import { AdminUrlUtil } from '../helpers/adminUrlUtil'
import { initPayloadE2E } from '../helpers/configHelpers'
import { collapsibleFieldsSlug } from './collections/Collapsible'
import { collapsibleFieldsSlug } from './collections/Collapsible/shared'
import { jsonDoc } from './collections/JSON'
import { numberDoc } from './collections/Number'
import { pointFieldsSlug } from './collections/Point'

View File

@@ -0,0 +1,48 @@
import type { Page } from '@playwright/test'
import { expect, test } from '@playwright/test'
import { saveDocAndAssert } from '../helpers'
import { AdminUrlUtil } from '../helpers/adminUrlUtil'
import { initPayloadTest } from '../helpers/configHelpers'
const { beforeAll, describe } = test
let url: AdminUrlUtil
const slug = 'nested-fields'
let page: Page
describe('Nested Fields', () => {
beforeAll(async ({ browser }) => {
const { serverURL } = await initPayloadTest({
__dirname,
init: {
local: false,
},
})
url = new AdminUrlUtil(serverURL, slug)
const context = await browser.newContext()
page = await context.newPage()
})
test('should save deeply nested fields', async () => {
const assertionValue = 'sample block value'
await page.goto(url.create)
await page.locator('#field-array > button').click()
await page.locator('#field-array__0__group__namedTab__blocks > button').click()
await page.locator('button[title="Block With Field"]').click()
await page.locator('#field-array__0__group__namedTab__blocks__0__text').fill(assertionValue)
await saveDocAndAssert(page)
await expect(page.locator('#field-array__0__group__namedTab__blocks__0__text')).toHaveValue(
assertionValue,
)
})
})

View File

@@ -6,16 +6,16 @@ import Posts from './collections/Posts'
import VersionPosts from './collections/Versions'
import AutosaveGlobal from './globals/Autosave'
import DraftGlobal from './globals/Draft'
import { draftSlug } from './shared'
import { draftSlug, titleToDelete } from './shared'
export default buildConfigWithDefaults({
collections: [Posts, AutosavePosts, DraftPosts, VersionPosts],
globals: [AutosaveGlobal, DraftGlobal],
localization: {
locales: ['en', 'es'],
defaultLocale: 'en',
},
indexSortableFields: true,
localization: {
defaultLocale: 'en',
locales: ['en', 'es'],
},
onInit: async (payload) => {
await payload.create({
collection: 'users',
@@ -27,43 +27,52 @@ export default buildConfigWithDefaults({
const { id: draftID } = await payload.create({
collection: draftSlug,
draft: true,
data: {
id: 1,
title: 'draft title',
description: 'draft description',
radio: 'test',
title: 'draft title',
},
draft: true,
})
await payload.create({
collection: draftSlug,
draft: false,
data: {
id: 2,
title: 'published title',
_status: 'published',
description: 'published description',
radio: 'test',
_status: 'published',
title: 'published title',
},
draft: false,
})
await payload.create({
collection: draftSlug,
data: {
description: 'published description',
title: titleToDelete,
},
draft: true,
})
await payload.update({
collection: draftSlug,
id: draftID,
draft: true,
collection: draftSlug,
data: {
title: 'draft title 2',
},
draft: true,
})
await payload.update({
collection: draftSlug,
id: draftID,
draft: true,
collection: draftSlug,
data: {
title: 'draft title 3',
},
draft: true,
})
},
})

View File

@@ -31,7 +31,7 @@ import wait from '../../packages/payload/src/utilities/wait'
import { changeLocale } from '../helpers'
import { AdminUrlUtil } from '../helpers/adminUrlUtil'
import { initPayloadE2E } from '../helpers/configHelpers'
import { autosaveSlug, draftSlug } from './shared'
import { autosaveSlug, draftSlug, titleToDelete } from './shared'
const { beforeAll, describe } = test
@@ -53,6 +53,24 @@ describe('versions', () => {
url = new AdminUrlUtil(serverURL, draftSlug)
})
// This test has to run before bulk updates that will rename the title
test('should delete', async () => {
await page.goto(url.list)
const rows = page.locator(`tr`)
const rowToDelete = rows.filter({ hasText: titleToDelete })
await rowToDelete.locator('.cell-_select input').click()
await page.locator('.delete-documents__toggle').click()
await page.locator('#confirm-delete').click()
await expect(page.locator('.Toastify__toast--success')).toContainText(
'Deleted 1 Draft Post successfully.',
)
await expect(page.locator('.row-1 .cell-title')).not.toHaveText(titleToDelete)
})
test('should bulk publish', async () => {
await page.goto(url.list)
@@ -92,7 +110,7 @@ describe('versions', () => {
await page.locator('.form-submit .edit-many__publish').click()
await expect(page.locator('.Toastify__toast--success')).toContainText(
'Updated 2 Draft Posts successfully.',
'Draft Posts successfully.',
)
await expect(page.locator('.row-1 .cell-_status')).toContainText('Published')
await expect(page.locator('.row-2 .cell-_status')).toContainText('Published')
@@ -111,7 +129,7 @@ describe('versions', () => {
await page.locator('.form-submit .edit-many__draft').click()
await expect(page.locator('.Toastify__toast--success')).toContainText(
'Updated 2 Draft Posts successfully.',
'Draft Posts successfully.',
)
await expect(page.locator('.row-1 .cell-_status')).toContainText('Draft')
await expect(page.locator('.row-2 .cell-_status')).toContainText('Draft')

View File

@@ -6,6 +6,7 @@ import { initPayloadTest } from '../helpers/configHelpers'
import AutosavePosts from './collections/Autosave'
import configPromise from './config'
import AutosaveGlobal from './globals/Autosave'
import { autosaveSlug } from './shared'
let collectionLocalPostID: string
let collectionLocalVersionID
@@ -56,8 +57,8 @@ describe('Versions', () => {
// First: delete potential existing versions from previous tests
if (collectionLocalPostID) {
await payload.delete({
collection,
id: collectionLocalPostID,
collection,
})
}
@@ -65,8 +66,8 @@ describe('Versions', () => {
const autosavePost = await payload.create({
collection,
data: {
title: 'Here is an autosave post in EN',
description: '345j23o4ifj34jf54g',
title: 'Here is an autosave post in EN',
},
})
collectionLocalPostID = autosavePost.id
@@ -91,10 +92,23 @@ describe('Versions', () => {
describe('Collections - Local', () => {
describe('Create', () => {
it('should allow creating a draft with missing required field data', async () => {
const draft = await payload.create({
collection: autosaveSlug,
data: {
description: undefined,
title: 'i have a title',
},
draft: true,
})
expect(draft.id).toBeDefined()
})
it('should allow a new version to be created and updated', async () => {
const updatedPost = await payload.findByID({
collection,
id: collectionLocalPostID,
collection,
})
expect(updatedPost.title).toBe(updatedTitle)
expect(updatedPost._status).toStrictEqual('draft')
@@ -105,8 +119,8 @@ describe('Versions', () => {
const autosavePost = await payload.create({
collection,
data: {
title: 'unique unchanging title',
description: 'description 1',
title: 'unique unchanging title',
},
})
@@ -133,8 +147,8 @@ describe('Versions', () => {
it('should allow a version to be retrieved by ID', async () => {
const version = await payload.findVersionByID({
collection,
id: collectionLocalVersionID,
collection,
})
expect(version.id).toStrictEqual(collectionLocalVersionID)
@@ -145,20 +159,20 @@ describe('Versions', () => {
const spanishTitle = 'Title in ES'
await payload.update({
collection,
id: collectionLocalPostID,
collection,
data: {
title: englishTitle,
},
})
const updatedPostES = await payload.update({
collection,
id: collectionLocalPostID,
locale: 'es',
collection,
data: {
title: spanishTitle,
},
locale: 'es',
})
expect(updatedPostES.title).toBe(spanishTitle)
@@ -166,8 +180,8 @@ describe('Versions', () => {
const newEnglishTitle = 'New title in EN'
await payload.update({
collection,
id: collectionLocalPostID,
collection,
data: {
title: newEnglishTitle,
},
@@ -193,14 +207,14 @@ describe('Versions', () => {
const somePost = await payload.create({
collection,
data: {
title: 'first post',
description: 'description 1',
title: 'first post',
},
})
const updatedPost = await payload.update({
collection,
id: collectionLocalPostID,
collection,
data: {
title: 'This should be the latest version',
},
@@ -216,8 +230,8 @@ describe('Versions', () => {
const title2 = 'Another updated post title in EN'
const updatedPost = await payload.update({
collection,
id: collectionLocalPostID,
collection,
data: {
title: title2,
},
@@ -227,8 +241,8 @@ describe('Versions', () => {
// Make sure it was updated correctly
const draftFromUpdatedPost = await payload.findByID({
collection,
id: collectionLocalPostID,
collection,
draft: true,
})
expect(draftFromUpdatedPost.title).toBe(title2)
@@ -239,15 +253,15 @@ describe('Versions', () => {
// restore to latest version
const restoredVersion = await payload.restoreVersion({
collection,
id: versions.docs[1].id,
collection,
})
expect(restoredVersion.title).toBeDefined()
const latestDraft = await payload.findByID({
collection,
id: collectionLocalPostID,
collection,
draft: true,
})
@@ -262,9 +276,9 @@ describe('Versions', () => {
const originalPublishedPost = await payload.create({
collection,
data: {
title: originalTitle,
description: 'kjnjyhbbdsfseankuhsjsfghb',
_status: 'published',
description: 'kjnjyhbbdsfseankuhsjsfghb',
title: originalTitle,
},
})
@@ -273,11 +287,11 @@ describe('Versions', () => {
await payload.update({
id: originalPublishedPost.id,
collection,
locale: 'en',
data: {
title: patchedTitle,
},
draft: true,
locale: 'en',
})
const spanishTitle = 'es title'
@@ -286,23 +300,23 @@ describe('Versions', () => {
await payload.update({
id: originalPublishedPost.id,
collection,
locale: 'es',
data: {
title: spanishTitle,
},
draft: true,
locale: 'es',
})
const publishedPost = await payload.findByID({
collection,
id: originalPublishedPost.id,
collection,
})
const draftPost = await payload.findByID({
collection,
locale: 'all',
id: originalPublishedPost.id,
collection,
draft: true,
locale: 'all',
})
expect(publishedPost.title).toBe(originalTitle)
@@ -310,44 +324,86 @@ describe('Versions', () => {
expect(draftPost.title.es).toBe(spanishTitle)
})
})
describe('Delete', () => {
let postToDelete
beforeAll(async () => {
postToDelete = await payload.create({
collection,
data: {
_status: 'draft',
description: 'description',
title: 'title to delete',
},
})
})
it('should delete drafts', async () => {
const drafts = await payload.db.queryDrafts({
collection,
where: {
parent: {
equals: postToDelete.id,
},
},
})
await payload.delete({
collection,
where: {
id: { equals: postToDelete.id },
},
})
const result = await payload.db.queryDrafts({
collection,
where: {
id: {
in: drafts.docs.map(({ id }) => id),
},
// appendVersionToQueryKey,
},
})
expect(result.docs).toHaveLength(0)
})
})
describe('Draft Count', () => {
it('creates proper number of drafts', async () => {
const originalDraft = await payload.create({
collection: 'draft-posts',
draft: true,
data: {
title: 'A',
_status: 'draft',
description: 'A',
_status: 'draft',
title: 'A',
},
draft: true,
})
await payload.update({
collection: 'draft-posts',
id: originalDraft.id,
draft: true,
collection: 'draft-posts',
data: {
title: 'B',
_status: 'draft',
description: 'B',
_status: 'draft',
title: 'B',
},
draft: true,
})
await payload.update({
collection: 'draft-posts',
id: originalDraft.id,
draft: true,
collection: 'draft-posts',
data: {
title: 'C',
description: 'C',
_status: 'draft',
description: 'C',
title: 'C',
},
draft: true,
})
const mostRecentDraft = await payload.findByID({
collection: 'draft-posts',
id: originalDraft.id,
collection: 'draft-posts',
draft: true,
})
@@ -373,52 +429,52 @@ describe('Versions', () => {
const doc1 = await payload.create({
collection: 'version-posts',
data: {
title: 'A',
description: 'A',
title: 'A',
},
})
await payload.update({
collection: 'version-posts',
id: doc1.id,
collection: 'version-posts',
data: {
title: 'B',
description: 'B',
title: 'B',
},
})
await payload.update({
collection: 'version-posts',
id: doc1.id,
collection: 'version-posts',
data: {
title: 'C',
description: 'C',
title: 'C',
},
})
const doc2 = await payload.create({
collection: 'version-posts',
data: {
title: 'D',
description: 'D',
title: 'D',
},
})
await payload.update({
collection: 'version-posts',
id: doc2.id,
collection: 'version-posts',
data: {
title: 'E',
description: 'E',
title: 'E',
},
})
await payload.update({
collection: 'version-posts',
id: doc2.id,
collection: 'version-posts',
data: {
title: 'F',
description: 'F',
title: 'F',
},
})
@@ -473,31 +529,31 @@ describe('Versions', () => {
firstDraft = await payload.create({
collection: 'draft-posts',
data: {
title: originalTitle,
description: 'my description',
radio: 'test',
title: originalTitle,
},
})
// This will be created in the `_draft-posts_versions` collection
await payload.update({
collection: 'draft-posts',
id: firstDraft.id,
draft: true,
collection: 'draft-posts',
data: {
title: updatedTitle1,
},
draft: true,
})
// This will be created in the `_draft-posts_versions` collection
// and will be the newest draft, able to be queried on
await payload.update({
collection: 'draft-posts',
id: firstDraft.id,
draft: true,
collection: 'draft-posts',
data: {
title: updatedTitle2,
},
draft: true,
})
})
@@ -583,8 +639,8 @@ describe('Versions', () => {
// First: delete potential existing versions from previous tests
if (collectionGraphQLPostID) {
await payload.delete({
collection,
id: collectionGraphQLPostID,
collection,
})
}
@@ -636,25 +692,25 @@ describe('Versions', () => {
// modify the post to create a new version
// language=graphQL
const update = `mutation {
updateAutosavePost(id: ${formatGraphQLID(
collectionGraphQLPostID,
)}, data: {title: "${updatedTitle2}"}) {
title
updatedAt
createdAt
}
updateAutosavePost(id: ${formatGraphQLID(
collectionGraphQLPostID,
)}, data: {title: "${updatedTitle2}"}) {
title
updatedAt
createdAt
}
}`
await graphQLClient.request(update)
// language=graphQL
const query = `query {
versionsAutosavePosts(where: { parent: { equals: ${formatGraphQLID(
collectionGraphQLPostID,
)} } }) {
docs {
id
versionsAutosavePosts(where: { parent: { equals: ${formatGraphQLID(
collectionGraphQLPostID,
)} } }) {
docs {
id
}
}
}
}`
const response = await graphQLClient.request(query)
@@ -687,17 +743,17 @@ describe('Versions', () => {
it('should allow read of versions by querying version content', async () => {
// language=graphQL
const query = `query {
versionsAutosavePosts(where: { version__title: {equals: "${collectionGraphQLOriginalTitle}" } }) {
docs {
id
parent {
id
}
version {
title
}
versionsAutosavePosts(where: { version__title: {equals: "${collectionGraphQLOriginalTitle}" } }) {
docs {
id
parent {
id
}
version {
title
}
}
}
}
}`
const response = await graphQLClient.request(query)
@@ -716,25 +772,25 @@ describe('Versions', () => {
// modify the post to create a new version
// language=graphQL
const update = `mutation {
updateAutosavePost(id: ${formatGraphQLID(
collectionGraphQLPostID,
)}, data: {title: "${collectionGraphQLOriginalTitle}"}) {
title
updatedAt
createdAt
}
updateAutosavePost(id: ${formatGraphQLID(
collectionGraphQLPostID,
)}, data: {title: "${collectionGraphQLOriginalTitle}"}) {
title
updatedAt
createdAt
}
}`
await graphQLClient.request(update)
// language=graphQL
const query = `query {
versionsAutosavePosts(where: { parent: { equals: ${formatGraphQLID(
collectionGraphQLPostID,
)} } }) {
docs {
id
versionsAutosavePosts(where: { parent: { equals: ${formatGraphQLID(
collectionGraphQLPostID,
)} } }) {
docs {
id
}
}
}
}`
const response = await graphQLClient.request(query)
@@ -780,17 +836,17 @@ describe('Versions', () => {
beforeEach(async () => {
const title2 = 'Here is an updated global title in EN'
await payload.updateGlobal({
slug: globalSlug,
data: {
title: 'Test Global',
},
slug: globalSlug,
})
const updatedGlobal = await payload.updateGlobal({
slug: globalSlug,
data: {
title: title2,
},
slug: globalSlug,
})
const versions = await payload.findGlobalVersions({
@@ -814,8 +870,8 @@ describe('Versions', () => {
describe('Read', () => {
it('should allow a version to be retrieved by ID', async () => {
const version = await payload.findGlobalVersionByID({
slug: globalSlug,
id: globalLocalVersionID,
slug: globalSlug,
})
expect(version.id).toStrictEqual(globalLocalVersionID)
@@ -828,18 +884,18 @@ describe('Versions', () => {
const spanishTitle = 'Title in ES'
await payload.updateGlobal({
slug: globalSlug,
data: {
title: englishTitle,
},
slug: globalSlug,
})
const updatedGlobalES = await payload.updateGlobal({
slug: globalSlug,
locale: 'es',
data: {
title: spanishTitle,
},
locale: 'es',
slug: globalSlug,
})
expect(updatedGlobalES.title).toBe(spanishTitle)
@@ -847,15 +903,15 @@ describe('Versions', () => {
const newEnglishTitle = 'New title in EN'
await payload.updateGlobal({
slug: globalSlug,
data: {
title: newEnglishTitle,
},
slug: globalSlug,
})
const versions = await payload.findGlobalVersions({
slug: globalSlug,
locale: 'all',
slug: globalSlug,
})
expect(versions.docs[0].version.title.en).toStrictEqual(newEnglishTitle)
@@ -868,18 +924,18 @@ describe('Versions', () => {
const title2 = 'Another updated title in EN'
const updatedGlobal = await payload.updateGlobal({
slug: globalSlug,
data: {
title: title2,
},
slug: globalSlug,
})
expect(updatedGlobal.title).toBe(title2)
// Make sure it was updated correctly
const foundUpdatedGlobal = await payload.findGlobal({
slug: globalSlug,
draft: true,
slug: globalSlug,
})
expect(foundUpdatedGlobal.title).toBe(title2)
@@ -890,15 +946,15 @@ describe('Versions', () => {
globalLocalVersionID = versions.docs[1].id
const restore = await payload.restoreGlobalVersion({
slug: globalSlug,
id: globalLocalVersionID,
slug: globalSlug,
})
expect(restore.title).toBeDefined()
const restoredGlobal = await payload.findGlobal({
slug: globalSlug,
draft: true,
slug: globalSlug,
})
expect(restoredGlobal.title).toBe(restore.title)
@@ -910,43 +966,43 @@ describe('Versions', () => {
const originalTitle = 'Here is a published global'
await payload.updateGlobal({
slug: globalSlug,
data: {
title: originalTitle,
description: 'kjnjyhbbdsfseankuhsjsfghb',
_status: 'published',
description: 'kjnjyhbbdsfseankuhsjsfghb',
title: originalTitle,
},
slug: globalSlug,
})
const publishedGlobal = await payload.findGlobal({
slug: globalSlug,
draft: true,
slug: globalSlug,
})
const updatedTitle2 = 'Here is a draft global with a patched title'
await payload.updateGlobal({
slug: globalSlug,
locale: 'en',
data: {
title: updatedTitle2,
},
draft: true,
locale: 'en',
slug: globalSlug,
})
await payload.updateGlobal({
slug: globalSlug,
locale: 'es',
data: {
title: updatedTitle2,
},
draft: true,
locale: 'es',
slug: globalSlug,
})
const updatedGlobal = await payload.findGlobal({
slug: globalSlug,
locale: 'all',
draft: true,
locale: 'all',
slug: globalSlug,
})
expect(publishedGlobal.title).toBe(originalTitle)
@@ -958,22 +1014,22 @@ describe('Versions', () => {
const originalTitle = 'Here is a draft'
await payload.updateGlobal({
slug: globalSlug,
data: {
title: originalTitle,
_status: 'draft',
title: originalTitle,
},
draft: true,
slug: globalSlug,
})
const updatedTitle2 = 'Now try to publish'
const result = await payload.updateGlobal({
slug: globalSlug,
data: {
title: updatedTitle2,
_status: 'published',
title: updatedTitle2,
},
slug: globalSlug,
})
expect(result.title).toBe(updatedTitle2)
@@ -985,25 +1041,25 @@ describe('Versions', () => {
beforeAll(async () => {
// language=graphql
const update = `mutation {
updateAutosaveGlobal(draft: true, data: {
title: "${globalGraphQLOriginalTitle}"
}) {
_status
title
}
updateAutosaveGlobal(draft: true, data: {
title: "${globalGraphQLOriginalTitle}"
}) {
_status
title
}
}`
await graphQLClient.request(update)
// language=graphQL
const query = `query {
versionsAutosaveGlobal(where: { version__title: { equals: "${globalGraphQLOriginalTitle}" } }) {
docs {
id
version {
title
}
versionsAutosaveGlobal(where: { version__title: { equals: "${globalGraphQLOriginalTitle}" } }) {
docs {
id
version {
title
}
}
}
}
}`
const response = await graphQLClient.request(query)
@@ -1014,12 +1070,12 @@ describe('Versions', () => {
it('should allow read of versions by version id', async () => {
// language=graphql
const query = `query {
versionAutosaveGlobal(id: ${formatGraphQLID(globalGraphQLVersionID)}) {
id
version {
title
versionAutosaveGlobal(id: ${formatGraphQLID(globalGraphQLVersionID)}) {
id
version {
title
}
}
}
}`
const response = await graphQLClient.request(query)
@@ -1033,14 +1089,14 @@ describe('Versions', () => {
it('should allow read of versions by querying version content', async () => {
// language=graphQL
const query = `query {
versionsAutosaveGlobal(where: { version__title: {equals: "${globalGraphQLOriginalTitle}" } }) {
docs {
id
version {
title
}
versionsAutosaveGlobal(where: { version__title: {equals: "${globalGraphQLOriginalTitle}" } }) {
docs {
id
version {
title
}
}
}
}
}`
const response = await graphQLClient.request(query)
@@ -1057,9 +1113,9 @@ describe('Versions', () => {
it('should allow a version to be restored', async () => {
// language=graphql
const restore = `mutation {
restoreVersionAutosaveGlobal(id: ${formatGraphQLID(globalGraphQLVersionID)}) {
title
}
restoreVersionAutosaveGlobal(id: ${formatGraphQLID(globalGraphQLVersionID)}) {
title
}
}`
await graphQLClient.request(restore)

View File

@@ -5,3 +5,5 @@ export const versionSlug = 'version-posts'
export const autoSaveGlobalSlug = 'autosave-global'
export const draftGlobalSlug = 'draft-global'
export const titleToDelete = 'title to delete'