Merge branch 'alpha' of github.com:payloadcms/payload into feat/config-i18n

This commit is contained in:
James
2024-04-09 12:35:57 -04:00
28 changed files with 217 additions and 129 deletions

View File

@@ -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 }}

View File

@@ -1,6 +1,6 @@
{
"name": "payload-monorepo",
"version": "3.0.0-alpha.59",
"version": "3.0.0-alpha.60",
"private": true,
"type": "module",
"workspaces:": [

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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 ({

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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

View File

@@ -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

View File

@@ -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
*/

View File

@@ -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
}

View File

@@ -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",

View File

@@ -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",

View File

@@ -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": {

View File

@@ -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)

View File

@@ -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

View File

@@ -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)
}
},
})

View File

@@ -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/*"