Merge branch 'alpha' of github.com:payloadcms/payload into feat/config-i18n
This commit is contained in:
66
.github/workflows/main.yml
vendored
66
.github/workflows/main.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
||||
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
|
||||
echo "templates: ${{ steps.filter.outputs.templates }}"
|
||||
|
||||
core-build:
|
||||
build:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.needs_build == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
@@ -65,65 +65,29 @@ jobs:
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 720
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
pnpm-store-
|
||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- run: pnpm install
|
||||
- run: pnpm run build:core
|
||||
- run: pnpm run build:all
|
||||
|
||||
- name: Cache build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
plugins-build:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.needs_build == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- run: pnpm install
|
||||
- run: pnpm run build:plugins
|
||||
|
||||
tests-unit:
|
||||
runs-on: ubuntu-latest
|
||||
needs: core-build
|
||||
needs: build
|
||||
if: false # Disable until tests are updated for 3.0
|
||||
|
||||
steps:
|
||||
@@ -140,6 +104,7 @@ jobs:
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -151,7 +116,7 @@ jobs:
|
||||
|
||||
tests-int:
|
||||
runs-on: ubuntu-latest
|
||||
needs: core-build
|
||||
needs: build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -184,6 +149,7 @@ jobs:
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -242,7 +208,7 @@ jobs:
|
||||
|
||||
tests-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
needs: core-build
|
||||
needs: build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -282,6 +248,7 @@ jobs:
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -302,7 +269,7 @@ jobs:
|
||||
tests-type-generation:
|
||||
if: false # This should be replaced with gen on a real Payload project
|
||||
runs-on: ubuntu-latest
|
||||
needs: core-build
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- name: Use Node.js 18
|
||||
@@ -318,6 +285,7 @@ jobs:
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"workspaces:": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-payload-app",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"homepage": "https://payloadcms.com",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/graphql",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.d.ts",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/next",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"main": "./src/index.js",
|
||||
"types": "./src/index.js",
|
||||
"type": "module",
|
||||
|
||||
@@ -2,12 +2,12 @@ import type { I18n } from '@payloadcms/translations'
|
||||
import type { Metadata } from 'next'
|
||||
import type { AdminViewComponent, SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { getNextI18n } from '@payloadcms/next/utilities'
|
||||
import { HydrateClientUser } from '@payloadcms/ui/elements/HydrateClientUser'
|
||||
import { DefaultTemplate } from '@payloadcms/ui/templates/Default'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import { initPage } from '../../utilities/initPage.js'
|
||||
import { getNextI18n } from '.././../utilities/getNextI18n.js'
|
||||
import { NotFoundClient } from './index.client.js'
|
||||
|
||||
export const generatePageMetadata = async ({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||
"license": "MIT",
|
||||
"main": "./src/index.ts",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud-storage",
|
||||
"description": "The official cloud storage plugin for Payload CMS",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud",
|
||||
"description": "The official Payload Cloud plugin",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-form-builder",
|
||||
"description": "Form builder plugin for Payload CMS",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-nested-docs",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"description": "The official Nested Docs plugin for Payload",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-redirects",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-search",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-seo",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-lexical",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"description": "The officially supported Lexical richtext adapter for Payload",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -246,10 +246,10 @@ export const TOGGLE_LINK_COMMAND: LexicalCommand<LinkPayload | null> =
|
||||
export function toggleLink(payload: LinkPayload): void {
|
||||
const selection = $getSelection()
|
||||
|
||||
if (!$isRangeSelection(selection)) {
|
||||
if (!$isRangeSelection(selection) && !payload.selectedNodes.length) {
|
||||
return
|
||||
}
|
||||
const nodes = selection.extract()
|
||||
const nodes = $isRangeSelection(selection) ? selection.extract() : payload.selectedNodes
|
||||
|
||||
if (payload === null) {
|
||||
// Remove LinkNodes
|
||||
|
||||
@@ -8,6 +8,8 @@ const { useLexicalComposerContext } = lexicalComposerContextImport
|
||||
|
||||
import lexicalUtilsImport from '@lexical/utils'
|
||||
const { $findMatchingParent, mergeRegister } = lexicalUtilsImport
|
||||
import type { LexicalNode } from 'lexical'
|
||||
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import lexicalImport from 'lexical'
|
||||
const {
|
||||
@@ -57,6 +59,8 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
|
||||
const { closeModal, toggleModal } = useModal()
|
||||
const editDepth = useEditDepth()
|
||||
const [isLink, setIsLink] = useState(false)
|
||||
const [selectedNodes, setSelectedNodes] = useState<LexicalNode[]>([])
|
||||
|
||||
const [isAutoLink, setIsAutoLink] = useState(false)
|
||||
|
||||
const drawerSlug = formatDrawerSlug({
|
||||
@@ -78,6 +82,7 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
|
||||
setIsAutoLink(false)
|
||||
setLinkUrl('')
|
||||
setLinkLabel('')
|
||||
setSelectedNodes([])
|
||||
return
|
||||
}
|
||||
|
||||
@@ -115,6 +120,8 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
|
||||
|
||||
setStateData(data)
|
||||
setIsLink(true)
|
||||
setSelectedNodes(selection ? selection?.getNodes() : [])
|
||||
|
||||
if ($isAutoLinkNode(linkParent)) {
|
||||
setIsAutoLink(true)
|
||||
} else {
|
||||
@@ -291,18 +298,26 @@ export function LinkEditor({ anchorElem }: { anchorElem: HTMLElement }): React.R
|
||||
|
||||
const newLinkPayload: LinkPayload = data as LinkPayload
|
||||
|
||||
newLinkPayload.selectedNodes = selectedNodes
|
||||
|
||||
// See: https://github.com/facebook/lexical/pull/5536. This updates autolink nodes to link nodes whenever a change was made (which is good!).
|
||||
editor.update(() => {
|
||||
const selection = $getSelection()
|
||||
let linkParent = null
|
||||
if ($isRangeSelection(selection)) {
|
||||
const parent = getSelectedNode(selection).getParent()
|
||||
if ($isAutoLinkNode(parent)) {
|
||||
const linkNode = $createLinkNode({
|
||||
fields: newLinkPayload.fields,
|
||||
})
|
||||
parent.replace(linkNode, true)
|
||||
linkParent = getSelectedNode(selection).getParent()
|
||||
} else {
|
||||
if (selectedNodes.length) {
|
||||
linkParent = selectedNodes[0].getParent()
|
||||
}
|
||||
}
|
||||
|
||||
if (linkParent && $isAutoLinkNode(linkParent)) {
|
||||
const linkNode = $createLinkNode({
|
||||
fields: newLinkPayload.fields,
|
||||
})
|
||||
linkParent.replace(linkNode, true)
|
||||
}
|
||||
})
|
||||
|
||||
// Needs to happen AFTER a potential auto link => link node conversion, as otherwise, the updated text to display may be lost due to
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { LexicalNode } from 'lexical'
|
||||
|
||||
import type { LinkFields } from '../../nodes/types.js'
|
||||
|
||||
/**
|
||||
@@ -6,6 +8,7 @@ import type { LinkFields } from '../../nodes/types.js'
|
||||
*/
|
||||
export type LinkPayload = {
|
||||
fields: LinkFields
|
||||
selectedNodes?: LexicalNode[]
|
||||
/**
|
||||
* The text content of the link node - will be displayed in the drawer
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,7 @@ type FilteredCollectionsT = (
|
||||
|
||||
const filterRichTextCollections: FilteredCollectionsT = (collections, options) => {
|
||||
return collections.filter(({ slug, admin: { enableRichTextRelationship }, upload }) => {
|
||||
if (options.visibleEntities.collections.includes(slug)) {
|
||||
if (!options.visibleEntities.collections.includes(slug)) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-slate",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"description": "The officially supported Slate richtext adapter for Payload",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/translations",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"main": "./src/exports/index.ts",
|
||||
"types": "./src/types.ts",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/ui",
|
||||
"version": "3.0.0-alpha.59",
|
||||
"version": "3.0.0-alpha.60",
|
||||
"type": "module",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -6,19 +6,23 @@ import execa from 'execa'
|
||||
import fse from 'fs-extra'
|
||||
import minimist from 'minimist'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import pMap from 'p-map'
|
||||
import pLimit from 'p-limit'
|
||||
import path from 'path'
|
||||
import prompts from 'prompts'
|
||||
import semver from 'semver'
|
||||
import { simpleGit } from 'simple-git'
|
||||
|
||||
import type { PackageDetails } from './lib/getPackageDetails.js'
|
||||
|
||||
import { getPackageDetails } from './lib/getPackageDetails.js'
|
||||
import { updateChangelog } from './utils/updateChangelog.js'
|
||||
|
||||
const npmPublishLimit = pLimit(2)
|
||||
|
||||
// Update this list with any packages to publish
|
||||
const packageWhitelist = [
|
||||
'payload',
|
||||
// 'translations',
|
||||
'translations',
|
||||
'ui',
|
||||
'next',
|
||||
'graphql',
|
||||
@@ -27,7 +31,7 @@ const packageWhitelist = [
|
||||
'richtext-slate',
|
||||
'richtext-lexical',
|
||||
|
||||
// 'create-payload-app',
|
||||
'create-payload-app',
|
||||
|
||||
// Plugins
|
||||
'plugin-cloud',
|
||||
@@ -143,26 +147,13 @@ async function main() {
|
||||
|
||||
await execa('pnpm', ['install'], execaOpts)
|
||||
|
||||
const buildResult = await execa('pnpm', ['build:core', '--output-logs=errors-only'], execaOpts)
|
||||
// const buildResult = execSync('pnpm build:all', execOpts)
|
||||
const buildResult = await execa('pnpm', ['build:all', '--output-logs=errors-only'], execaOpts)
|
||||
if (buildResult.exitCode !== 0) {
|
||||
console.error(chalk.bold.red('Build failed'))
|
||||
console.log(buildResult.stderr)
|
||||
abort('Build failed')
|
||||
}
|
||||
|
||||
const buildPluginsResult = await execa(
|
||||
'pnpm',
|
||||
['build:plugins', '--output-logs=errors-only'],
|
||||
execaOpts,
|
||||
)
|
||||
|
||||
if (buildPluginsResult.exitCode !== 0) {
|
||||
console.error(chalk.bold.red('Build failed'))
|
||||
console.log(buildPluginsResult.stderr)
|
||||
abort('Build failed')
|
||||
}
|
||||
|
||||
// Update changelog
|
||||
if (changelog) {
|
||||
header(`${logPrefix}📝 Updating changelog...`)
|
||||
@@ -209,35 +200,8 @@ async function main() {
|
||||
abort('2FA code is required')
|
||||
}
|
||||
|
||||
// Publish
|
||||
const results: { name: string; success: boolean; details?: string }[] = await Promise.all(
|
||||
packageDetails.map(async (pkg) => {
|
||||
try {
|
||||
console.log(logPrefix, chalk.bold(`🚀 ${pkg.name} publishing...`))
|
||||
const cmdArgs = ['publish', '-C', pkg.packagePath, '--no-git-checks', '--tag', tag]
|
||||
if (dryRun) {
|
||||
cmdArgs.push('--dry-run')
|
||||
} else {
|
||||
cmdArgs.push('--otp', otp)
|
||||
}
|
||||
const { exitCode, stderr } = await execa('pnpm', cmdArgs, {
|
||||
cwd,
|
||||
stdio: ['ignore', 'ignore', 'pipe'],
|
||||
// stdio: 'inherit',
|
||||
})
|
||||
|
||||
if (exitCode !== 0) {
|
||||
console.log(chalk.bold.red(`\n\n❌ ${pkg.name} ERROR: pnpm publish failed\n\n`))
|
||||
return { name: pkg.name, success: false, details: stderr }
|
||||
}
|
||||
|
||||
console.log(`${logPrefix} ${chalk.green(`✅ ${pkg.name} published`)}`)
|
||||
return { name: pkg.name, success: true }
|
||||
} catch (error) {
|
||||
console.error(chalk.bold.red(`\n\n❌ ${pkg.name} ERROR: ${error.message}\n\n`))
|
||||
return { name: pkg.name, success: false }
|
||||
}
|
||||
}),
|
||||
const results = await Promise.all(
|
||||
packageDetails.map((pkg) => publishPackageThrottled(pkg, { dryRun, otp })),
|
||||
)
|
||||
|
||||
console.log(chalk.bold.green(`\n\nResults:\n`))
|
||||
@@ -270,6 +234,42 @@ main().catch((error) => {
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
/** Publish with promise concurrency throttling */
|
||||
async function publishPackageThrottled(
|
||||
pkg: PackageDetails,
|
||||
opts?: { dryRun?: boolean; otp?: string },
|
||||
) {
|
||||
const { dryRun = false, otp } = opts ?? {}
|
||||
return npmPublishLimit(() => publishSinglePackage(pkg, { dryRun, otp }))
|
||||
}
|
||||
|
||||
async function publishSinglePackage(
|
||||
pkg: PackageDetails,
|
||||
opts?: { dryRun?: boolean; otp?: string },
|
||||
) {
|
||||
const { dryRun = false, otp } = opts ?? {}
|
||||
console.log(chalk.bold(`🚀 ${pkg.name} publishing...`))
|
||||
const cmdArgs = ['publish', '-C', pkg.packagePath, '--no-git-checks', '--json', '--tag', tag]
|
||||
if (dryRun) {
|
||||
cmdArgs.push('--dry-run')
|
||||
} else {
|
||||
cmdArgs.push('--otp', otp)
|
||||
}
|
||||
const { exitCode, stderr } = await execa('pnpm', cmdArgs, {
|
||||
cwd,
|
||||
stdio: ['ignore', 'ignore', 'pipe'],
|
||||
// stdio: 'inherit',
|
||||
})
|
||||
|
||||
if (exitCode !== 0) {
|
||||
console.log(chalk.bold.red(`\n\n❌ ${pkg.name} ERROR: pnpm publish failed\n\n${stderr}`))
|
||||
return { name: pkg.name, success: false, details: stderr }
|
||||
}
|
||||
|
||||
console.log(`${logPrefix} ${chalk.green(`✅ ${pkg.name} published`)}`)
|
||||
return { name: pkg.name, success: true }
|
||||
}
|
||||
|
||||
function abort(message = 'Abort', exitCode = 1) {
|
||||
console.error(chalk.bold.red(`\n${message}\n`))
|
||||
process.exit(exitCode)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SerializedBlockNode } from '@payloadcms/richtext-lexical'
|
||||
import type { SerializedBlockNode, SerializedLinkNode } from '@payloadcms/richtext-lexical'
|
||||
import type { Page } from '@playwright/test'
|
||||
import type { SerializedEditorState, SerializedParagraphNode, SerializedTextNode } from 'lexical'
|
||||
import type { Payload } from 'payload'
|
||||
@@ -402,7 +402,7 @@ describe('lexical', () => {
|
||||
for (let i = 0; i < 18; i++) {
|
||||
await page.keyboard.press('Shift+ArrowRight')
|
||||
}
|
||||
// The following text should now be selected: elationship node 1
|
||||
// The following text should now be selectedelationship node 1
|
||||
|
||||
const floatingToolbar_formatSection = page.locator(
|
||||
'.floating-select-toolbar-popup__section-format',
|
||||
@@ -463,6 +463,92 @@ describe('lexical', () => {
|
||||
timeout: POLL_TOPASS_TIMEOUT,
|
||||
})
|
||||
})
|
||||
|
||||
test('should be able to select text, make it an external link and receive the updated link value', async () => {
|
||||
// Reproduces https://github.com/payloadcms/payload/issues/4025
|
||||
await navigateToLexicalFields()
|
||||
const richTextField = page.locator('.rich-text-lexical').nth(1) // second
|
||||
await richTextField.scrollIntoViewIfNeeded()
|
||||
await expect(richTextField).toBeVisible()
|
||||
|
||||
// Find span in contentEditable with text "Some text below relationship node"
|
||||
const spanInEditor = richTextField.locator('span').getByText('Upload Node:').first()
|
||||
await expect(spanInEditor).toBeVisible()
|
||||
await spanInEditor.click() // Use click, because focus does not work
|
||||
|
||||
await page.keyboard.press('ArrowRight')
|
||||
// Now select some text
|
||||
for (let i = 0; i < 4; i++) {
|
||||
await page.keyboard.press('Shift+ArrowRight')
|
||||
}
|
||||
// The following text should now be "Node"
|
||||
|
||||
const floatingToolbar = page.locator('.floating-select-toolbar-popup')
|
||||
|
||||
await expect(floatingToolbar).toBeVisible()
|
||||
|
||||
const linkButton = floatingToolbar
|
||||
.locator('.floating-select-toolbar-popup__button-link')
|
||||
.first()
|
||||
|
||||
await expect(linkButton).toBeVisible()
|
||||
await linkButton.click()
|
||||
|
||||
/**
|
||||
* In drawer
|
||||
*/
|
||||
const drawerContent = page.locator('.drawer__content').first()
|
||||
await expect(drawerContent).toBeVisible()
|
||||
|
||||
const urlField = drawerContent.locator('input#field-fields__url').first()
|
||||
await expect(urlField).toBeVisible()
|
||||
// Fill with https://www.payloadcms.com
|
||||
await urlField.fill('https://www.payloadcms.com')
|
||||
await expect(urlField).toHaveValue('https://www.payloadcms.com')
|
||||
await drawerContent.locator('.form-submit button').click({ delay: 100 })
|
||||
await expect(drawerContent).toBeHidden()
|
||||
|
||||
/**
|
||||
* check if it worked correctly
|
||||
*/
|
||||
|
||||
const linkInEditor = richTextField.locator('a.LexicalEditorTheme__link').first()
|
||||
await expect(linkInEditor).toBeVisible()
|
||||
await expect(linkInEditor).toHaveAttribute('href', 'https://www.payloadcms.com')
|
||||
|
||||
await saveDocAndAssert(page)
|
||||
|
||||
// Check if it persists after saving
|
||||
await expect(linkInEditor).toBeVisible()
|
||||
await expect(linkInEditor).toHaveAttribute('href', 'https://www.payloadcms.com')
|
||||
|
||||
// Make sure it's being returned from the API as well
|
||||
await expect(async () => {
|
||||
const lexicalDoc: LexicalField = (
|
||||
await payload.find({
|
||||
collection: lexicalFieldsSlug,
|
||||
depth: 0,
|
||||
where: {
|
||||
title: {
|
||||
equals: lexicalDocData.title,
|
||||
},
|
||||
},
|
||||
})
|
||||
).docs[0] as never
|
||||
|
||||
const lexicalField: SerializedEditorState = lexicalDoc.lexicalWithBlocks
|
||||
|
||||
expect(
|
||||
(
|
||||
(lexicalField.root.children[0] as SerializedParagraphNode)
|
||||
.children[1] as SerializedLinkNode
|
||||
).fields.url,
|
||||
).toBe('https://www.payloadcms.com')
|
||||
}).toPass({
|
||||
timeout: POLL_TOPASS_TIMEOUT,
|
||||
})
|
||||
})
|
||||
|
||||
test('ensure slash menu is not hidden behind other blocks', async () => {
|
||||
// This test makes sure there are no z-index issues here
|
||||
await navigateToLexicalFields()
|
||||
@@ -799,6 +885,22 @@ describe('lexical', () => {
|
||||
await shouldRespectRowRemovalTest()
|
||||
})
|
||||
|
||||
test('ensure pre-seeded uploads node is visible', async () => {
|
||||
// Due to issues with the relationships condition, we had issues with that not being visible. Checking for visibility ensures there is no breakage there again
|
||||
await navigateToLexicalFields()
|
||||
const richTextField = page.locator('.rich-text-lexical').nth(1) // second
|
||||
await richTextField.scrollIntoViewIfNeeded()
|
||||
await expect(richTextField).toBeVisible()
|
||||
|
||||
const uploadBlock = richTextField.locator('.ContentEditable__root > div').first() // Check for the first div, as we wanna make sure it's the first div in the editor (1. node is a paragraph, second node is a div which is the upload node)
|
||||
await uploadBlock.scrollIntoViewIfNeeded()
|
||||
await expect(uploadBlock).toBeVisible()
|
||||
|
||||
await expect(uploadBlock.locator('.lexical-upload__doc-drawer-toggler strong')).toHaveText(
|
||||
'payload.jpg',
|
||||
)
|
||||
})
|
||||
|
||||
test.skip('should respect required error state in deeply nested text field', async () => {
|
||||
await navigateToLexicalFields()
|
||||
const richTextField = page.locator('.rich-text-lexical').nth(1) // second
|
||||
|
||||
@@ -26,7 +26,7 @@ import Uploads from './collections/Upload/index.js'
|
||||
import Uploads2 from './collections/Upload2/index.js'
|
||||
import Uploads3 from './collections/Uploads3/index.js'
|
||||
import TabsWithRichText from './globals/TabsWithRichText.js'
|
||||
import { seed } from './seed.js'
|
||||
import { clearAndSeedEverything } from './seed.js'
|
||||
|
||||
export const collectionSlugs: CollectionConfig[] = [
|
||||
LexicalFields,
|
||||
@@ -79,7 +79,7 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
onInit: async (payload) => {
|
||||
if (process.env.SEED_IN_CONFIG_ONINIT !== 'false') {
|
||||
await seed(payload)
|
||||
await clearAndSeedEverything(payload)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"./packages/ui/src/scss/app.scss"
|
||||
],
|
||||
"@payloadcms/next/*": [
|
||||
"./packages/next/src/exports/*"
|
||||
"./packages/next/src/*"
|
||||
],
|
||||
"@payloadcms/next": [
|
||||
"./packages/next/src/exports/*"
|
||||
|
||||
Reference in New Issue
Block a user