Merge branch '2.0' of github.com:payloadcms/payload into 2.0

This commit is contained in:
James
2023-10-05 16:00:21 -04:00
11 changed files with 147 additions and 75 deletions

View File

@@ -320,3 +320,28 @@ jobs:
- name: Build richtext-lexical - name: Build richtext-lexical
run: pnpm turbo run build --filter=richtext-lexical run: pnpm turbo run build --filter=richtext-lexical
build-plugin-live-preview:
runs-on: ubuntu-latest
needs: core-build
steps:
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
run_install: false
- name: Restore build
uses: actions/cache@v3
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Build live-preview
run: pnpm turbo run build --filter=live-preview

View File

@@ -16,6 +16,11 @@ export async function migrate(this: PostgresAdapter): Promise<void> {
const { payload } = this const { payload } = this
const migrationFiles = await readMigrationFiles({ payload }) const migrationFiles = await readMigrationFiles({ payload })
if (!migrationFiles.length) {
payload.logger.info({ msg: 'No migrations to run.' })
return
}
let latestBatch = 0 let latestBatch = 0
let migrationsInDB = [] let migrationsInDB = []

View File

@@ -1,6 +1,6 @@
{ {
"name": "@payloadcms/live-preview", "name": "@payloadcms/live-preview",
"version": "1.0.0-beta.0", "version": "1.0.0-beta.1",
"description": "The official live preview JavaScript SDK for Payload", "description": "The official live preview JavaScript SDK for Payload",
"repository": "https://github.com/payloadcms/payload", "repository": "https://github.com/payloadcms/payload",
"license": "MIT", "license": "MIT",

View File

@@ -1,19 +1,20 @@
import { useWindowInfo } from '@faceless-ui/window-info' import { useWindowInfo } from '@faceless-ui/window-info'
import { clearAllBodyScrollLocks, disableBodyScroll, enableBodyScroll } from 'body-scroll-lock'
import React, { useEffect, useRef } from 'react' import React, { useEffect, useRef } from 'react'
import { useHistory } from 'react-router-dom' import { useHistory } from 'react-router-dom'
import { usePreferences } from '../../utilities/Preferences' import { usePreferences } from '../../utilities/Preferences'
import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from 'body-scroll-lock'
type NavContextType = { type NavContextType = {
navOpen: boolean navOpen: boolean
setNavOpen: (value: boolean) => void
navRef: React.RefObject<HTMLDivElement> navRef: React.RefObject<HTMLDivElement>
setNavOpen: (value: boolean) => void
} }
export const NavContext = React.createContext<NavContextType>({ export const NavContext = React.createContext<NavContextType>({
navOpen: false, navOpen: true,
setNavOpen: () => {},
navRef: null, navRef: null,
setNavOpen: () => {},
}) })
export const useNav = () => React.useContext(NavContext) export const useNav = () => React.useContext(NavContext)
@@ -42,7 +43,10 @@ export const NavProvider: React.FC<{
const navPrefs = await getPreference('nav') const navPrefs = await getPreference('nav')
const preferredState = navPrefs?.open const preferredState = navPrefs?.open
if (typeof preferredState === 'boolean') { if (typeof preferredState === 'boolean') {
console.log({ navPrefs })
setNavOpen(preferredState) setNavOpen(preferredState)
} else {
setNavOpen(true)
} }
} }
@@ -94,6 +98,6 @@ export const NavProvider: React.FC<{
}, []) }, [])
return ( return (
<NavContext.Provider value={{ navOpen, setNavOpen, navRef }}>{children}</NavContext.Provider> <NavContext.Provider value={{ navOpen, navRef, setNavOpen }}>{children}</NavContext.Provider>
) )
} }

View File

@@ -1,34 +1,36 @@
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useState } from 'react'
import { NavLink } from 'react-router-dom'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useConfig } from '../../utilities/Config' import { NavLink } from 'react-router-dom'
import { useAuth } from '../../utilities/Auth'
import RenderCustomComponent from '../../utilities/RenderCustomComponent' import type { EntityToGroup, Group } from '../../../utilities/groupNavItems'
import Chevron from '../../icons/Chevron'
import Logout from '../Logout'
import { EntityToGroup, EntityType, Group, groupNavItems } from '../../../utilities/groupNavItems'
import { getTranslation } from '../../../../utilities/getTranslation' import { getTranslation } from '../../../../utilities/getTranslation'
import { EntityType, groupNavItems } from '../../../utilities/groupNavItems'
import Chevron from '../../icons/Chevron'
import { useAuth } from '../../utilities/Auth'
import { useConfig } from '../../utilities/Config'
import RenderCustomComponent from '../../utilities/RenderCustomComponent'
import { Hamburger } from '../Hamburger'
import Logout from '../Logout'
import NavGroup from '../NavGroup' import NavGroup from '../NavGroup'
import { useNav } from './context' import { useNav } from './context'
import './index.scss' import './index.scss'
import { Hamburger } from '../Hamburger'
const baseClass = 'nav' const baseClass = 'nav'
const DefaultNav: React.FC = () => { const DefaultNav: React.FC = () => {
const { navOpen, setNavOpen, navRef } = useNav() const { navOpen, navRef, setNavOpen } = useNav()
const { permissions, user } = useAuth() const { permissions, user } = useAuth()
const [groups, setGroups] = useState<Group[]>([]) const [groups, setGroups] = useState<Group[]>([])
const { t, i18n } = useTranslation('general') const { i18n } = useTranslation('general')
const { const {
admin: {
components: { afterNavLinks, beforeNavLinks },
},
collections, collections,
globals, globals,
routes: { admin }, routes: { admin },
admin: {
components: { beforeNavLinks, afterNavLinks },
},
} = useConfig() } = useConfig()
useEffect(() => { useEffect(() => {
@@ -42,8 +44,8 @@ const DefaultNav: React.FC = () => {
) )
.map((collection) => { .map((collection) => {
const entityToGroup: EntityToGroup = { const entityToGroup: EntityToGroup = {
type: EntityType.collection,
entity: collection, entity: collection,
type: EntityType.collection,
} }
return entityToGroup return entityToGroup
@@ -55,8 +57,8 @@ const DefaultNav: React.FC = () => {
) )
.map((global) => { .map((global) => {
const entityToGroup: EntityToGroup = { const entityToGroup: EntityToGroup = {
type: EntityType.global,
entity: global, entity: global,
type: EntityType.global,
} }
return entityToGroup return entityToGroup
@@ -74,7 +76,7 @@ const DefaultNav: React.FC = () => {
<nav className={`${baseClass}__wrap`}> <nav className={`${baseClass}__wrap`}>
{Array.isArray(beforeNavLinks) && {Array.isArray(beforeNavLinks) &&
beforeNavLinks.map((Component, i) => <Component key={i} />)} beforeNavLinks.map((Component, i) => <Component key={i} />)}
{groups.map(({ label, entities }, key) => { {groups.map(({ entities, label }, key) => {
return ( return (
<NavGroup {...{ key, label }}> <NavGroup {...{ key, label }}>
{entities.map(({ entity, type }, i) => { {entities.map(({ entity, type }, i) => {
@@ -96,9 +98,9 @@ const DefaultNav: React.FC = () => {
return ( return (
<NavLink <NavLink
id={id}
className={`${baseClass}__link`}
activeClassName="active" activeClassName="active"
className={`${baseClass}__link`}
id={id}
key={i} key={i}
to={href} to={href}
> >
@@ -126,6 +128,7 @@ const DefaultNav: React.FC = () => {
onClick={() => { onClick={() => {
setNavOpen(false) setNavOpen(false)
}} }}
type="button"
> >
<Hamburger isActive /> <Hamburger isActive />
</button> </button>

View File

@@ -3,16 +3,15 @@ import { useTranslation } from 'react-i18next'
import type { Props } from './types' import type { Props } from './types'
import { Hamburger } from '../../elements/Hamburger'
import { AppHeader } from '../../elements/Header' import { AppHeader } from '../../elements/Header'
import { Nav as DefaultNav } from '../../elements/Nav'
import { NavToggler } from '../../elements/Nav/NavToggler'
import { useNav } from '../../elements/Nav/context'
import { useConfig } from '../../utilities/Config' import { useConfig } from '../../utilities/Config'
import Meta from '../../utilities/Meta' import Meta from '../../utilities/Meta'
import RenderCustomComponent from '../../utilities/RenderCustomComponent' import RenderCustomComponent from '../../utilities/RenderCustomComponent'
import { Nav as DefaultNav } from '../../elements/Nav'
import { useNav } from '../../elements/Nav/context'
import { NavToggler } from '../../elements/Nav/NavToggler'
import './index.scss' import './index.scss'
import { Hamburger } from '../../elements/Hamburger'
const baseClass = 'template-default' const baseClass = 'template-default'
@@ -46,15 +45,15 @@ const Default: React.FC<Props> = ({ children, className }) => {
<AppHeader /> <AppHeader />
{children} {children}
<button <button
type="button" aria-label={t('close')}
className={`${baseClass}__nav-overlay`} className={`${baseClass}__nav-overlay`}
aria-label={t('menu')}
onClick={() => setNavOpen(!navOpen)} onClick={() => setNavOpen(!navOpen)}
type="button"
/> />
</div> </div>
</div> </div>
<NavToggler className={`${baseClass}__nav-toggler`} id="nav-toggler"> <NavToggler className={`${baseClass}__nav-toggler`} id="nav-toggler">
<Hamburger isActive={navOpen} closeIcon="collapse" /> <Hamburger closeIcon="collapse" isActive={navOpen} />
</NavToggler> </NavToggler>
</Fragment> </Fragment>
) )

View File

@@ -25,6 +25,12 @@
padding-left: calc(var(--base) * 2); padding-left: calc(var(--base) * 2);
} }
} }
&__fields {
& > .tabs-field {
margin-right: calc(var(--base) * -2);
}
}
} }
} }
@@ -101,6 +107,12 @@
padding-left: var(--gutter-h); padding-left: var(--gutter-h);
} }
} }
&__fields {
& > .tabs-field {
margin-right: calc(var(--gutter-h) * -1);
}
}
} }
} }

View File

@@ -25,6 +25,12 @@
padding-left: calc(var(--base) * 2); padding-left: calc(var(--base) * 2);
} }
} }
&__fields {
& > .tabs-field {
margin-right: calc(var(--base) * -2);
}
}
} }
} }
@@ -97,6 +103,12 @@
padding-left: var(--gutter-h); padding-left: var(--gutter-h);
} }
} }
&__fields {
& > .tabs-field {
margin-right: calc(var(--gutter-h) * -1);
}
}
} }
} }

View File

@@ -17,7 +17,7 @@ import CustomDefaultView from './components/views/CustomDefault'
import CustomEditView from './components/views/CustomEdit' import CustomEditView from './components/views/CustomEdit'
import CustomVersionsView from './components/views/CustomVersions' import CustomVersionsView from './components/views/CustomVersions'
import CustomView from './components/views/CustomView' import CustomView from './components/views/CustomView'
import { globalSlug, slug, slugPluralLabel, slugSingularLabel } from './shared' import { globalSlug, postsSlug, slugPluralLabel, slugSingularLabel } from './shared'
export interface Post { export interface Post {
createdAt: Date createdAt: Date
@@ -89,7 +89,7 @@ export default buildConfigWithDefaults({
], ],
}, },
{ {
slug, slug: postsSlug,
labels: { labels: {
singular: slugSingularLabel, singular: slugSingularLabel,
plural: slugPluralLabel, plural: slugPluralLabel,
@@ -107,36 +107,46 @@ export default buildConfigWithDefaults({
}, },
fields: [ fields: [
{ {
name: 'title', type: 'tabs',
type: 'text', tabs: [
}, {
{ label: 'Tab 1',
name: 'description', fields: [
type: 'text', {
}, name: 'title',
{ type: 'text',
name: 'number', },
type: 'number', {
}, name: 'description',
{ type: 'text',
name: 'richText', },
type: 'richText', {
editor: slateEditor({ name: 'number',
admin: { type: 'number',
elements: ['relationship'], },
{
name: 'richText',
type: 'richText',
editor: slateEditor({
admin: {
elements: ['relationship'],
},
}),
},
{
type: 'ui',
name: 'demoUIField',
label: 'Demo UI Field',
admin: {
components: {
Field: DemoUIFieldField,
Cell: DemoUIFieldCell,
},
},
},
],
}, },
}), ],
},
{
type: 'ui',
name: 'demoUIField',
label: 'Demo UI Field',
admin: {
components: {
Field: DemoUIFieldField,
Cell: DemoUIFieldCell,
},
},
}, },
{ {
name: 'sidebarField', name: 'sidebarField',
@@ -380,7 +390,7 @@ export default buildConfigWithDefaults({
await mapAsync([...Array(11)], async () => { await mapAsync([...Array(11)], async () => {
await payload.create({ await payload.create({
collection: slug, collection: postsSlug,
data: { data: {
title: 'title', title: 'title',
description: 'description', description: 'description',

View File

@@ -17,7 +17,7 @@ import {
} from '../helpers' } from '../helpers'
import { AdminUrlUtil } from '../helpers/adminUrlUtil' import { AdminUrlUtil } from '../helpers/adminUrlUtil'
import { initPayloadE2E } from '../helpers/configHelpers' import { initPayloadE2E } from '../helpers/configHelpers'
import { globalSlug, slug, slugPluralLabel } from './shared' import { globalSlug, postsSlug, slugPluralLabel } from './shared'
const { afterEach, beforeAll, beforeEach, describe } = test const { afterEach, beforeAll, beforeEach, describe } = test
@@ -33,7 +33,7 @@ describe('admin', () => {
beforeAll(async ({ browser }) => { beforeAll(async ({ browser }) => {
serverURL = (await initPayloadE2E(__dirname)).serverURL serverURL = (await initPayloadE2E(__dirname)).serverURL
await clearDocs() // Clear any seeded data from onInit await clearDocs() // Clear any seeded data from onInit
url = new AdminUrlUtil(serverURL, slug) url = new AdminUrlUtil(serverURL, postsSlug)
const context = await browser.newContext() const context = await browser.newContext()
page = await context.newPage() page = await context.newPage()
@@ -52,7 +52,7 @@ describe('admin', () => {
test('should nav to collection - nav', async () => { test('should nav to collection - nav', async () => {
await page.goto(url.admin) await page.goto(url.admin)
await openNav(page) await openNav(page)
await page.locator(`#nav-${slug}`).click() await page.locator(`#nav-${postsSlug}`).click()
expect(page.url()).toContain(url.list) expect(page.url()).toContain(url.list)
}) })
@@ -66,7 +66,7 @@ describe('admin', () => {
test('should navigate to collection - card', async () => { test('should navigate to collection - card', async () => {
await page.goto(url.admin) await page.goto(url.admin)
await wait(200) await wait(200)
await page.locator(`#card-${slug}`).click() await page.locator(`#card-${postsSlug}`).click()
expect(page.url()).toContain(url.list) expect(page.url()).toContain(url.list)
}) })
@@ -122,7 +122,9 @@ describe('admin', () => {
test('breadcrumbs - from document to collection', async () => { test('breadcrumbs - from document to collection', async () => {
const { id } = await createPost() const { id } = await createPost()
await page.goto(url.edit(id)) await page.goto(url.edit(id))
const collectionBreadcrumb = page.locator(`.step-nav a[href="/admin/collections/${slug}"]`) const collectionBreadcrumb = page.locator(
`.step-nav a[href="/admin/collections/${postsSlug}"]`,
)
await expect(collectionBreadcrumb).toBeVisible() await expect(collectionBreadcrumb).toBeVisible()
await expect(collectionBreadcrumb).toHaveText(slugPluralLabel) await expect(collectionBreadcrumb).toHaveText(slugPluralLabel)
expect(page.url()).toContain(url.list) expect(page.url()).toContain(url.list)
@@ -866,7 +868,7 @@ describe('admin', () => {
async function createPost(overrides?: Partial<Post>): Promise<Post> { async function createPost(overrides?: Partial<Post>): Promise<Post> {
return payload.create({ return payload.create({
collection: slug, collection: postsSlug,
data: { data: {
title, title,
description, description,
@@ -876,9 +878,9 @@ async function createPost(overrides?: Partial<Post>): Promise<Post> {
} }
async function clearDocs(): Promise<void> { async function clearDocs(): Promise<void> {
const allDocs = await payload.find({ collection: slug, limit: 100 }) const allDocs = await payload.find({ collection: postsSlug, limit: 100 })
const ids = allDocs.docs.map((doc) => doc.id) const ids = allDocs.docs.map((doc) => doc.id)
await mapAsync(ids, async (id) => { await mapAsync(ids, async (id) => {
await payload.delete({ collection: slug, id }) await payload.delete({ collection: postsSlug, id })
}) })
} }

View File

@@ -1,4 +1,4 @@
export const slug = 'posts' export const postsSlug = 'posts'
export const slugSingularLabel = 'Post' export const slugSingularLabel = 'Post'