chore: merge
This commit is contained in:
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -229,8 +229,8 @@ jobs:
|
||||
- plugin-nested-docs
|
||||
- plugin-seo
|
||||
# - refresh-permissions
|
||||
# - uploads
|
||||
- versions
|
||||
- uploads
|
||||
|
||||
steps:
|
||||
- name: Use Node.js 18
|
||||
|
||||
45
packages/next/src/routes/rest/collections/preview.ts
Normal file
45
packages/next/src/routes/rest/collections/preview.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import httpStatus from 'http-status'
|
||||
import { findByIDOperation } from 'payload/operations'
|
||||
import { isNumber } from 'payload/utilities'
|
||||
|
||||
import type { CollectionRouteHandlerWithID } from '../types.js'
|
||||
|
||||
import { routeError } from '../routeError.js'
|
||||
|
||||
export const preview: CollectionRouteHandlerWithID = async ({ id, collection, req }) => {
|
||||
const { searchParams } = req
|
||||
const depth = searchParams.get('depth')
|
||||
|
||||
const result = await findByIDOperation({
|
||||
id,
|
||||
collection,
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
draft: searchParams.get('draft') === 'true',
|
||||
req,
|
||||
})
|
||||
|
||||
let previewURL: string
|
||||
|
||||
const generatePreviewURL = req.payload.config.collections.find(
|
||||
(config) => config.slug === collection.config.slug,
|
||||
)?.admin?.preview
|
||||
|
||||
if (typeof generatePreviewURL === 'function') {
|
||||
try {
|
||||
previewURL = await generatePreviewURL(result, {
|
||||
locale: req.locale,
|
||||
token: req.user?.token,
|
||||
})
|
||||
} catch (err) {
|
||||
routeError({
|
||||
collection,
|
||||
err,
|
||||
req,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return Response.json(previewURL, {
|
||||
status: httpStatus.OK,
|
||||
})
|
||||
}
|
||||
44
packages/next/src/routes/rest/globals/preview.ts
Normal file
44
packages/next/src/routes/rest/globals/preview.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import httpStatus from 'http-status'
|
||||
import { findOneOperation } from 'payload/operations'
|
||||
import { isNumber } from 'payload/utilities'
|
||||
|
||||
import type { GlobalRouteHandler } from '../types.js'
|
||||
|
||||
import { routeError } from '../routeError.js'
|
||||
|
||||
export const preview: GlobalRouteHandler = async ({ globalConfig, req }) => {
|
||||
const { searchParams } = req
|
||||
const depth = searchParams.get('depth')
|
||||
|
||||
const result = await findOneOperation({
|
||||
slug: globalConfig.slug,
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
draft: searchParams.get('draft') === 'true',
|
||||
globalConfig,
|
||||
req,
|
||||
})
|
||||
|
||||
let previewURL: string
|
||||
|
||||
const generatePreviewURL = req.payload.config.globals.find(
|
||||
(config) => config.slug === globalConfig.slug,
|
||||
)?.admin?.preview
|
||||
|
||||
if (typeof generatePreviewURL === 'function') {
|
||||
try {
|
||||
previewURL = await generatePreviewURL(result, {
|
||||
locale: req.locale,
|
||||
token: req.user?.token,
|
||||
})
|
||||
} catch (err) {
|
||||
routeError({
|
||||
err,
|
||||
req,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return Response.json(previewURL, {
|
||||
status: httpStatus.OK,
|
||||
})
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import { find } from './collections/find.js'
|
||||
import { findByID } from './collections/findByID.js'
|
||||
import { findVersionByID } from './collections/findVersionByID.js'
|
||||
import { findVersions } from './collections/findVersions.js'
|
||||
import { preview as previewCollection } from './collections/preview.js'
|
||||
import { restoreVersion } from './collections/restoreVersion.js'
|
||||
import { update } from './collections/update.js'
|
||||
import { updateByID } from './collections/updateByID.js'
|
||||
@@ -42,6 +43,7 @@ import { docAccess as docAccessGlobal } from './globals/docAccess.js'
|
||||
import { findOne } from './globals/findOne.js'
|
||||
import { findVersionByID as findVersionByIdGlobal } from './globals/findVersionByID.js'
|
||||
import { findVersions as findVersionsGlobal } from './globals/findVersions.js'
|
||||
import { preview as previewGlobal } from './globals/preview.js'
|
||||
import { restoreVersion as restoreVersionGlobal } from './globals/restoreVersion.js'
|
||||
import { update as updateGlobal } from './globals/update.js'
|
||||
import { routeError } from './routeError.js'
|
||||
@@ -60,6 +62,7 @@ const endpoints = {
|
||||
getFile,
|
||||
init,
|
||||
me,
|
||||
preview: previewCollection,
|
||||
versions: findVersions,
|
||||
},
|
||||
PATCH: {
|
||||
@@ -88,6 +91,7 @@ const endpoints = {
|
||||
'doc-versions': findVersionsGlobal,
|
||||
'doc-versions-by-id': findVersionByIdGlobal,
|
||||
findOne,
|
||||
preview: previewGlobal,
|
||||
},
|
||||
POST: {
|
||||
'doc-access': docAccessGlobal,
|
||||
@@ -171,6 +175,7 @@ export const GET =
|
||||
endpoints: req.payload.config.endpoints,
|
||||
request,
|
||||
})
|
||||
|
||||
if (disableEndpoints) return disableEndpoints
|
||||
|
||||
collection = req.payload.collections?.[slug1]
|
||||
@@ -212,10 +217,16 @@ export const GET =
|
||||
if (slug2 === 'file') {
|
||||
// /:collection/file/:filename
|
||||
res = await endpoints.collection.GET.getFile({ collection, filename: slug3, req })
|
||||
} else if (slug3 in endpoints.collection.GET) {
|
||||
// /:collection/:id/preview
|
||||
res = await (endpoints.collection.GET[slug3] as CollectionRouteHandlerWithID)({
|
||||
id: slug2,
|
||||
collection,
|
||||
req,
|
||||
})
|
||||
} else if (`doc-${slug2}-by-id` in endpoints.collection.GET) {
|
||||
// /:collection/access/:id
|
||||
// /:collection/versions/:id
|
||||
|
||||
res = await (
|
||||
endpoints.collection.GET[`doc-${slug2}-by-id`] as CollectionRouteHandlerWithID
|
||||
)({ id: slug3, collection, req })
|
||||
@@ -229,6 +240,7 @@ export const GET =
|
||||
endpoints: globalConfig.endpoints,
|
||||
request,
|
||||
})
|
||||
|
||||
if (disableEndpoints) return disableEndpoints
|
||||
|
||||
const customEndpointResponse = await handleCustomEndpoints({
|
||||
@@ -236,6 +248,7 @@ export const GET =
|
||||
entitySlug: `${slug1}/${slug2}`,
|
||||
payloadRequest: req,
|
||||
})
|
||||
|
||||
if (customEndpointResponse) return customEndpointResponse
|
||||
|
||||
switch (slug.length) {
|
||||
@@ -244,9 +257,16 @@ export const GET =
|
||||
res = await endpoints.global.GET.findOne({ globalConfig, req })
|
||||
break
|
||||
case 3:
|
||||
if (`doc-${slug3}` in endpoints.global.GET) {
|
||||
if (slug3 in endpoints.global.GET) {
|
||||
// /globals/:slug/preview
|
||||
res = await (endpoints.global.GET[slug3] as GlobalRouteHandler)({
|
||||
globalConfig,
|
||||
req,
|
||||
})
|
||||
} else if (`doc-${slug3}` in endpoints.global.GET) {
|
||||
// /globals/:slug/access
|
||||
// /globals/:slug/versions
|
||||
// /globals/:slug/preview
|
||||
res = await (endpoints.global.GET?.[`doc-${slug3}`] as GlobalRouteHandler)({
|
||||
globalConfig,
|
||||
req,
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"@payloadcms/translations": "workspace:*",
|
||||
"@swc-node/core": "^1.13.0",
|
||||
"@swc-node/sourcemap-support": "^0.5.0",
|
||||
"@types/probe-image-size": "^7.2.4",
|
||||
"bson-objectid": "2.0.4",
|
||||
"conf": "10.2.0",
|
||||
"console-table-printer": "2.11.2",
|
||||
@@ -55,7 +56,6 @@
|
||||
"find-up": "4.1.0",
|
||||
"get-tsconfig": "^4.7.2",
|
||||
"http-status": "1.6.2",
|
||||
"image-size": "^1.1.1",
|
||||
"joi": "^17.12.1",
|
||||
"json-schema-to-typescript": "11.0.3",
|
||||
"jsonwebtoken": "9.0.1",
|
||||
@@ -67,6 +67,7 @@
|
||||
"pino-pretty": "10.2.0",
|
||||
"pirates": "^4.0.6",
|
||||
"pluralize": "8.0.0",
|
||||
"probe-image-size": "^7.2.3",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"scheduler": "0.23.0",
|
||||
"scmp": "2.1.0"
|
||||
|
||||
@@ -1,11 +1 @@
|
||||
export type CustomPreviewButtonProps = React.ComponentType<
|
||||
DefaultPreviewButtonProps & {
|
||||
DefaultButton: React.ComponentType<DefaultPreviewButtonProps>
|
||||
}
|
||||
>
|
||||
|
||||
export type DefaultPreviewButtonProps = {
|
||||
disabled: boolean
|
||||
label: string
|
||||
preview: () => void
|
||||
}
|
||||
export type CustomPreviewButton = React.ComponentType
|
||||
|
||||
@@ -1 +1 @@
|
||||
export type CustomPublishButtonProps = React.ComponentType
|
||||
export type CustomPublishButton = React.ComponentType
|
||||
|
||||
@@ -1 +1 @@
|
||||
export type CustomSaveButtonProps = React.ComponentType
|
||||
export type CustomSaveButton = React.ComponentType
|
||||
|
||||
@@ -1 +1 @@
|
||||
export type CustomSaveDraftButtonProps = React.ComponentType
|
||||
export type CustomSaveDraftButton = React.ComponentType
|
||||
|
||||
@@ -2,11 +2,10 @@ export type { RichTextAdapter, RichTextFieldProps } from './RichText.js'
|
||||
export type { CellComponentProps, DefaultCellComponentProps } from './elements/Cell.js'
|
||||
export type { ConditionalDateProps } from './elements/DatePicker.js'
|
||||
export type { DayPickerProps, SharedProps, TimePickerProps } from './elements/DatePicker.js'
|
||||
export type { DefaultPreviewButtonProps } from './elements/PreviewButton.js'
|
||||
export type { CustomPreviewButtonProps } from './elements/PreviewButton.js'
|
||||
export type { CustomPublishButtonProps } from './elements/PublishButton.js'
|
||||
export type { CustomSaveButtonProps } from './elements/SaveButton.js'
|
||||
export type { CustomSaveDraftButtonProps } from './elements/SaveDraftButton.js'
|
||||
export type { CustomPreviewButton } from './elements/PreviewButton.js'
|
||||
export type { CustomPublishButton } from './elements/PublishButton.js'
|
||||
export type { CustomSaveButton } from './elements/SaveButton.js'
|
||||
export type { CustomSaveDraftButton } from './elements/SaveDraftButton.js'
|
||||
export type {
|
||||
DocumentTab,
|
||||
DocumentTabComponent,
|
||||
|
||||
@@ -50,6 +50,7 @@ export const createClientCollectionConfig = (collection: SanitizedCollectionConf
|
||||
if ('upload' in sanitized && typeof sanitized.upload === 'object') {
|
||||
sanitized.upload = { ...sanitized.upload }
|
||||
delete sanitized.upload.handlers
|
||||
delete sanitized.upload.adminThumbnail
|
||||
}
|
||||
|
||||
if ('auth' in sanitized && typeof sanitized.auth === 'object') {
|
||||
|
||||
@@ -2,10 +2,10 @@ import type { GraphQLInputObjectType, GraphQLNonNull, GraphQLObjectType } from '
|
||||
import type { DeepRequired } from 'ts-essentials'
|
||||
|
||||
import type {
|
||||
CustomPreviewButtonProps,
|
||||
CustomPublishButtonProps,
|
||||
CustomSaveButtonProps,
|
||||
CustomSaveDraftButtonProps,
|
||||
CustomPreviewButton,
|
||||
CustomPublishButton,
|
||||
CustomSaveButton,
|
||||
CustomSaveDraftButton,
|
||||
} from '../../admin/types.js'
|
||||
import type { Auth, ClientUser, IncomingAuthType } from '../../auth/types.js'
|
||||
import type {
|
||||
@@ -211,23 +211,23 @@ export type CollectionAdminOptions = {
|
||||
/**
|
||||
* Replaces the "Preview" button
|
||||
*/
|
||||
PreviewButton?: CustomPreviewButtonProps
|
||||
PreviewButton?: CustomPreviewButton
|
||||
/**
|
||||
* Replaces the "Publish" button
|
||||
* + drafts must be enabled
|
||||
*/
|
||||
PublishButton?: CustomPublishButtonProps
|
||||
PublishButton?: CustomPublishButton
|
||||
/**
|
||||
* Replaces the "Save" button
|
||||
* + drafts must be disabled
|
||||
*/
|
||||
SaveButton?: CustomSaveButtonProps
|
||||
SaveButton?: CustomSaveButton
|
||||
/**
|
||||
* Replaces the "Save Draft" button
|
||||
* + drafts must be enabled
|
||||
* + autosave must be disabled
|
||||
*/
|
||||
SaveDraftButton?: CustomSaveDraftButtonProps
|
||||
SaveDraftButton?: CustomSaveDraftButton
|
||||
}
|
||||
views?: {
|
||||
/**
|
||||
|
||||
@@ -2,10 +2,10 @@ import type { GraphQLNonNull, GraphQLObjectType } from 'graphql'
|
||||
import type { DeepRequired } from 'ts-essentials'
|
||||
|
||||
import type {
|
||||
CustomPreviewButtonProps,
|
||||
CustomPublishButtonProps,
|
||||
CustomSaveButtonProps,
|
||||
CustomSaveDraftButtonProps,
|
||||
CustomPreviewButton,
|
||||
CustomPublishButton,
|
||||
CustomSaveButton,
|
||||
CustomSaveDraftButton,
|
||||
} from '../../admin/types.js'
|
||||
import type { User } from '../../auth/types.js'
|
||||
import type {
|
||||
@@ -79,23 +79,23 @@ export type GlobalAdminOptions = {
|
||||
/**
|
||||
* Replaces the "Preview" button
|
||||
*/
|
||||
PreviewButton?: CustomPreviewButtonProps
|
||||
PreviewButton?: CustomPreviewButton
|
||||
/**
|
||||
* Replaces the "Publish" button
|
||||
* + drafts must be enabled
|
||||
*/
|
||||
PublishButton?: CustomPublishButtonProps
|
||||
PublishButton?: CustomPublishButton
|
||||
/**
|
||||
* Replaces the "Save" button
|
||||
* + drafts must be disabled
|
||||
*/
|
||||
SaveButton?: CustomSaveButtonProps
|
||||
SaveButton?: CustomSaveButton
|
||||
/**
|
||||
* Replaces the "Save Draft" button
|
||||
* + drafts must be enabled
|
||||
* + autosave must be disabled
|
||||
*/
|
||||
SaveDraftButton?: CustomSaveDraftButtonProps
|
||||
SaveDraftButton?: CustomSaveDraftButton
|
||||
}
|
||||
views?: {
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { imageSize } from 'image-size'
|
||||
import fs from 'fs'
|
||||
import probeImageSize from 'probe-image-size'
|
||||
|
||||
import type { PayloadRequest } from '../types/index.js'
|
||||
import type { ProbedImageSize } from './types.js'
|
||||
|
||||
export function getImageSize(file: PayloadRequest['file']): ProbedImageSize {
|
||||
if (file.tempFilePath) {
|
||||
const dimensions = imageSize(file.tempFilePath)
|
||||
return { height: dimensions.height, width: dimensions.width }
|
||||
const data = fs.readFileSync(file.tempFilePath)
|
||||
return probeImageSize.sync(data)
|
||||
}
|
||||
|
||||
const buffer = Buffer.from(file.data)
|
||||
const dimensions = imageSize(buffer)
|
||||
return { height: dimensions.height, width: dimensions.width }
|
||||
return probeImageSize.sync(file.data)
|
||||
}
|
||||
|
||||
@@ -154,15 +154,9 @@ export const DocumentControls: React.FC<{
|
||||
</div>
|
||||
<div className={`${baseClass}__controls-wrapper`}>
|
||||
<div className={`${baseClass}__controls`}>
|
||||
{/* {(collectionConfig?.admin?.preview || globalConfig?.admin?.preview) && (
|
||||
<PreviewButton
|
||||
CustomComponent={
|
||||
collectionConfig?.admin?.components?.edit?.PreviewButton ||
|
||||
globalConfig?.admin?.components?.elements?.PreviewButton
|
||||
}
|
||||
generatePreviewURL={collectionConfig?.admin?.preview || globalConfig?.admin?.preview}
|
||||
/>
|
||||
)} */}
|
||||
{componentMap?.isPreviewEnabled && (
|
||||
<PreviewButton CustomComponent={componentMap.PreviewButton} />
|
||||
)}
|
||||
{hasSavePermission && (
|
||||
<React.Fragment>
|
||||
{collectionConfig?.versions?.drafts || globalConfig?.versions?.drafts ? (
|
||||
|
||||
@@ -1,31 +1,24 @@
|
||||
'use client'
|
||||
import type { GeneratePreviewURL } from 'payload/config'
|
||||
import type { CustomPreviewButtonProps, DefaultPreviewButtonProps } from 'payload/types'
|
||||
import React from 'react'
|
||||
|
||||
import React, { useCallback, useRef, useState } from 'react'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import { RenderCustomComponent } from '../../elements/RenderCustomComponent/index.js'
|
||||
import { useAuth } from '../../providers/Auth/index.js'
|
||||
import { useConfig } from '../../providers/Config/index.js'
|
||||
import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
|
||||
import { useLocale } from '../../providers/Locale/index.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
import { Button } from '../Button/index.js'
|
||||
import { usePreviewURL } from './usePreviewURL.js'
|
||||
|
||||
const baseClass = 'preview-btn'
|
||||
|
||||
const DefaultPreviewButton: React.FC<DefaultPreviewButtonProps> = ({
|
||||
disabled,
|
||||
label,
|
||||
preview,
|
||||
}) => {
|
||||
const DefaultPreviewButton: React.FC = () => {
|
||||
const { generatePreviewURL, label } = usePreviewURL()
|
||||
|
||||
return (
|
||||
<Button
|
||||
buttonStyle="secondary"
|
||||
className={baseClass}
|
||||
disabled={disabled}
|
||||
onClick={preview}
|
||||
// disabled={disabled}
|
||||
onClick={() =>
|
||||
generatePreviewURL({
|
||||
openPreviewWindow: true,
|
||||
})
|
||||
}
|
||||
size="small"
|
||||
>
|
||||
{label}
|
||||
@@ -33,66 +26,12 @@ const DefaultPreviewButton: React.FC<DefaultPreviewButtonProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
export type PreviewButtonProps = {
|
||||
CustomComponent?: CustomPreviewButtonProps
|
||||
generatePreviewURL?: GeneratePreviewURL
|
||||
type Props = {
|
||||
CustomComponent?: React.ReactNode
|
||||
}
|
||||
|
||||
export const PreviewButton: React.FC<PreviewButtonProps> = ({
|
||||
CustomComponent,
|
||||
generatePreviewURL,
|
||||
}) => {
|
||||
const { id, collectionSlug, globalSlug } = useDocumentInfo()
|
||||
export const PreviewButton: React.FC<Props> = ({ CustomComponent }) => {
|
||||
if (CustomComponent) return CustomComponent
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const { code: locale } = useLocale()
|
||||
const { token } = useAuth()
|
||||
const {
|
||||
routes: { api },
|
||||
serverURL,
|
||||
} = useConfig()
|
||||
const { t } = useTranslation()
|
||||
const isGeneratingPreviewURL = useRef(false)
|
||||
|
||||
// we need to regenerate the preview URL every time the button is clicked
|
||||
// to do this we need to fetch the document data fresh from the API
|
||||
// this will ensure the latest data is used when generating the preview URL
|
||||
const preview = useCallback(async () => {
|
||||
if (!generatePreviewURL || isGeneratingPreviewURL.current) return
|
||||
isGeneratingPreviewURL.current = true
|
||||
|
||||
try {
|
||||
setIsLoading(true)
|
||||
|
||||
let url = `${serverURL}${api}`
|
||||
if (collectionSlug) url = `${url}/${collectionSlug}/${id}`
|
||||
if (globalSlug) url = `${url}/globals/${globalSlug}`
|
||||
|
||||
const data = await fetch(`${url}?draft=true&locale=${locale}&fallback-locale=null`).then(
|
||||
(res) => res.json(),
|
||||
)
|
||||
const previewURL = await generatePreviewURL(data, { locale, token })
|
||||
if (!previewURL) throw new Error()
|
||||
setIsLoading(false)
|
||||
isGeneratingPreviewURL.current = false
|
||||
window.open(previewURL, '_blank')
|
||||
} catch (err) {
|
||||
setIsLoading(false)
|
||||
isGeneratingPreviewURL.current = false
|
||||
toast.error(t('error:previewing'))
|
||||
}
|
||||
}, [serverURL, api, collectionSlug, globalSlug, id, generatePreviewURL, locale, token, t])
|
||||
|
||||
return (
|
||||
<RenderCustomComponent
|
||||
CustomComponent={CustomComponent}
|
||||
DefaultComponent={DefaultPreviewButton}
|
||||
componentProps={{
|
||||
DefaultButton: DefaultPreviewButton,
|
||||
disabled: isLoading || !generatePreviewURL,
|
||||
label: isLoading ? t('general:loading') : t('version:preview'),
|
||||
preview,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
return <DefaultPreviewButton />
|
||||
}
|
||||
|
||||
70
packages/ui/src/elements/PreviewButton/usePreviewURL.tsx
Normal file
70
packages/ui/src/elements/PreviewButton/usePreviewURL.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
'use client'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import { useConfig } from '../../providers/Config/index.js'
|
||||
import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
|
||||
import { useLocale } from '../../providers/Locale/index.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
|
||||
export const usePreviewURL = (): {
|
||||
generatePreviewURL: ({ openPreviewWindow }: { openPreviewWindow?: boolean }) => void
|
||||
isLoading: boolean
|
||||
label: string
|
||||
previewURL: string
|
||||
} => {
|
||||
const { id, collectionSlug, globalSlug } = useDocumentInfo()
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [previewURL, setPreviewURL] = useState('')
|
||||
const { code: locale } = useLocale()
|
||||
|
||||
const {
|
||||
routes: { api },
|
||||
serverURL,
|
||||
} = useConfig()
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
const isGeneratingPreviewURL = useRef(false)
|
||||
|
||||
// we need to regenerate the preview URL every time the button is clicked
|
||||
// to do this we need to fetch the document data fresh from the API
|
||||
// this will ensure the latest data is used when generating the preview URL
|
||||
const generatePreviewURL = useCallback(
|
||||
async ({ openPreviewWindow = false }) => {
|
||||
if (isGeneratingPreviewURL.current) return
|
||||
isGeneratingPreviewURL.current = true
|
||||
|
||||
try {
|
||||
setIsLoading(true)
|
||||
|
||||
let url = `${serverURL}${api}`
|
||||
if (collectionSlug) url = `${url}/${collectionSlug}/${id}/preview`
|
||||
if (globalSlug) url = `${url}/globals/${globalSlug}/preview`
|
||||
|
||||
const res = await fetch(`${url}${locale ? `?locale=${locale}` : ''}`)
|
||||
|
||||
if (!res.ok) throw new Error()
|
||||
const newPreviewURL = await res.json()
|
||||
if (!newPreviewURL) throw new Error()
|
||||
setPreviewURL(newPreviewURL)
|
||||
setIsLoading(false)
|
||||
isGeneratingPreviewURL.current = false
|
||||
if (openPreviewWindow) window.open(newPreviewURL, '_blank')
|
||||
} catch (err) {
|
||||
setIsLoading(false)
|
||||
isGeneratingPreviewURL.current = false
|
||||
toast.error(t('error:previewing'))
|
||||
}
|
||||
},
|
||||
[serverURL, api, collectionSlug, globalSlug, id, locale, t],
|
||||
)
|
||||
|
||||
return {
|
||||
generatePreviewURL,
|
||||
isLoading,
|
||||
label: isLoading ? t('general:loading') : t('version:preview'),
|
||||
previewURL,
|
||||
}
|
||||
}
|
||||
@@ -70,8 +70,8 @@ export const buildComponentMap = (args: {
|
||||
const SaveDraftButtonComponent = collectionConfig?.admin?.components?.edit?.SaveDraftButton
|
||||
const SaveDraftButton = SaveDraftButtonComponent ? <SaveDraftButtonComponent /> : undefined
|
||||
|
||||
/* const PreviewButtonComponent = collectionConfig?.admin?.components?.edit?.PreviewButton
|
||||
const PreviewButton = PreviewButtonComponent ? <PreviewButtonComponent /> : undefined */
|
||||
const PreviewButtonComponent = collectionConfig?.admin?.components?.edit?.PreviewButton
|
||||
const PreviewButton = PreviewButtonComponent ? <PreviewButtonComponent /> : undefined
|
||||
|
||||
const PublishButtonComponent = collectionConfig?.admin?.components?.edit?.PublishButton
|
||||
const PublishButton = PublishButtonComponent ? <PublishButtonComponent /> : undefined
|
||||
@@ -111,7 +111,7 @@ export const buildComponentMap = (args: {
|
||||
BeforeListTable,
|
||||
Edit: <Edit collectionSlug={collectionConfig.slug} />,
|
||||
List: <List collectionSlug={collectionConfig.slug} />,
|
||||
/* PreviewButton, */
|
||||
PreviewButton,
|
||||
PublishButton,
|
||||
SaveButton,
|
||||
SaveDraftButton,
|
||||
@@ -123,6 +123,7 @@ export const buildComponentMap = (args: {
|
||||
fieldSchema: fields,
|
||||
readOnly: readOnlyOverride,
|
||||
}),
|
||||
isPreviewEnabled: !!collectionConfig?.admin?.preview,
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -143,8 +144,8 @@ export const buildComponentMap = (args: {
|
||||
const SaveDraftButton = globalConfig?.admin?.components?.elements?.SaveDraftButton
|
||||
const SaveDraftButtonComponent = SaveDraftButton ? <SaveDraftButton /> : undefined
|
||||
|
||||
/* const PreviewButton = globalConfig?.admin?.components?.elements?.PreviewButton
|
||||
const PreviewButtonComponent = PreviewButton ? <PreviewButton /> : undefined */
|
||||
const PreviewButton = globalConfig?.admin?.components?.elements?.PreviewButton
|
||||
const PreviewButtonComponent = PreviewButton ? <PreviewButton /> : undefined
|
||||
|
||||
const PublishButton = globalConfig?.admin?.components?.elements?.PublishButton
|
||||
const PublishButtonComponent = PublishButton ? <PublishButton /> : undefined
|
||||
@@ -164,7 +165,7 @@ export const buildComponentMap = (args: {
|
||||
|
||||
const componentMap: GlobalComponentMap = {
|
||||
Edit: <Edit globalSlug={globalConfig.slug} />,
|
||||
/* PreviewButton: PreviewButtonComponent, */
|
||||
PreviewButton: PreviewButtonComponent,
|
||||
PublishButton: PublishButtonComponent,
|
||||
SaveButton: SaveButtonComponent,
|
||||
SaveDraftButton: SaveDraftButtonComponent,
|
||||
@@ -176,6 +177,7 @@ export const buildComponentMap = (args: {
|
||||
fieldSchema: fields,
|
||||
readOnly: readOnlyOverride,
|
||||
}),
|
||||
isPreviewEnabled: !!globalConfig?.admin?.preview,
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -13,8 +13,6 @@ import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/types'
|
||||
import { isPlainObject } from 'payload/utilities'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { RadioFieldProps } from '../../..//fields/RadioGroup/index.js'
|
||||
import type { RichTextFieldProps } from '../../..//fields/RichText/index.js'
|
||||
import type { ArrayFieldProps } from '../../../fields/Array/index.js'
|
||||
import type { BlocksFieldProps } from '../../../fields/Blocks/index.js'
|
||||
import type { CheckboxFieldProps } from '../../../fields/Checkbox/index.js'
|
||||
@@ -26,7 +24,9 @@ import type { GroupFieldProps } from '../../../fields/Group/index.js'
|
||||
import type { JSONFieldProps } from '../../../fields/JSON/index.js'
|
||||
import type { NumberFieldProps } from '../../../fields/Number/index.js'
|
||||
import type { PointFieldProps } from '../../../fields/Point/index.js'
|
||||
import type { RadioFieldProps } from '../../../fields/RadioGroup/index.js'
|
||||
import type { RelationshipFieldProps } from '../../../fields/Relationship/types.js'
|
||||
import type { RichTextFieldProps } from '../../../fields/RichText/index.js'
|
||||
import type { RowFieldProps } from '../../../fields/Row/types.js'
|
||||
import type { SelectFieldProps } from '../../../fields/Select/index.js'
|
||||
import type { TabsFieldProps } from '../../../fields/Tabs/index.js'
|
||||
@@ -199,6 +199,8 @@ export const mapFields = (args: {
|
||||
? field.label
|
||||
: undefined,
|
||||
labels: 'labels' in field ? field.labels : undefined,
|
||||
options: 'options' in field ? field.options : undefined,
|
||||
relationTo: 'relationTo' in field ? field.relationTo : undefined,
|
||||
}
|
||||
|
||||
switch (field.type) {
|
||||
@@ -727,9 +729,10 @@ export const mapFields = (args: {
|
||||
|
||||
if (!disableAddingID && !hasID) {
|
||||
// TODO: For all fields (not just this one) we need to add the name to both .fieldComponentProps.name AND .name. This can probably be improved
|
||||
result.unshift({
|
||||
result.push({
|
||||
name: 'id',
|
||||
type: 'text',
|
||||
CustomField: null,
|
||||
cellComponentProps: {
|
||||
name: 'id',
|
||||
},
|
||||
|
||||
@@ -103,12 +103,13 @@ export type GlobalComponentMap = ConfigComponentMapBase
|
||||
|
||||
export type ConfigComponentMapBase = {
|
||||
Edit: React.ReactNode
|
||||
/* PreviewButton: React.ReactNode */
|
||||
PreviewButton: React.ReactNode
|
||||
PublishButton: React.ReactNode
|
||||
SaveButton: React.ReactNode
|
||||
SaveDraftButton: React.ReactNode
|
||||
actionsMap: ActionMap
|
||||
fieldMap: FieldMap
|
||||
isPreviewEnabled: boolean
|
||||
}
|
||||
|
||||
export type ComponentMap = {
|
||||
|
||||
83
pnpm-lock.yaml
generated
83
pnpm-lock.yaml
generated
@@ -715,6 +715,9 @@ importers:
|
||||
'@swc-node/sourcemap-support':
|
||||
specifier: ^0.5.0
|
||||
version: 0.5.0
|
||||
'@types/probe-image-size':
|
||||
specifier: ^7.2.4
|
||||
version: 7.2.4
|
||||
bson-objectid:
|
||||
specifier: 2.0.4
|
||||
version: 2.0.4
|
||||
@@ -745,9 +748,6 @@ importers:
|
||||
http-status:
|
||||
specifier: 1.6.2
|
||||
version: 1.6.2
|
||||
image-size:
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1
|
||||
joi:
|
||||
specifier: ^17.12.1
|
||||
version: 17.12.2
|
||||
@@ -781,6 +781,9 @@ importers:
|
||||
pluralize:
|
||||
specifier: 8.0.0
|
||||
version: 8.0.0
|
||||
probe-image-size:
|
||||
specifier: ^7.2.3
|
||||
version: 7.2.3
|
||||
sanitize-filename:
|
||||
specifier: 1.6.3
|
||||
version: 1.6.3
|
||||
@@ -5974,7 +5977,7 @@ packages:
|
||||
resolution: {integrity: sha512-ymwc+qb1XkjT/gfoQwxIeHZ6ixH23A+tCT2ADSA/DPVKzAjwYkTXBMCQ/f6fe4wEa85Lhp26VPeUxI7wMhAi7A==}
|
||||
dependencies:
|
||||
'@types/estree': 1.0.5
|
||||
'@types/json-schema': 7.0.15
|
||||
'@types/json-schema': 7.0.12
|
||||
|
||||
/@types/esprima@4.0.6:
|
||||
resolution: {integrity: sha512-lIk+kSt9lGv5hxK6aZNjiUEGZqKmOTpmg0tKiJQI+Ow98fLillxsiZNik5+RcP7mXL929KiTH/D9jGtpDlMbVw==}
|
||||
@@ -6129,7 +6132,6 @@ packages:
|
||||
|
||||
/@types/json-schema@7.0.12:
|
||||
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
|
||||
dev: true
|
||||
|
||||
/@types/json-schema@7.0.15:
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
@@ -6197,6 +6199,12 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@types/needle@3.3.0:
|
||||
resolution: {integrity: sha512-UFIuc1gdyzAqeVUYpSL+cliw2MmU/ZUhVZKE7Zo4wPbgc8hbljeKSnn6ls6iG8r5jpegPXLUIhJ+Wb2kLVs8cg==}
|
||||
dependencies:
|
||||
'@types/node': 16.18.85
|
||||
dev: false
|
||||
|
||||
/@types/node-fetch@2.6.11:
|
||||
resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==}
|
||||
dependencies:
|
||||
@@ -6293,6 +6301,13 @@ packages:
|
||||
resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==}
|
||||
dev: false
|
||||
|
||||
/@types/probe-image-size@7.2.4:
|
||||
resolution: {integrity: sha512-HVqYj3L+D+S/6qpQRv5qMxrD/5pglzZuhP7ZIqgVSZ+Ck4z1TCFkNIRG8WesFueQTqWFTSgkkAl6f8lwxFPQSw==}
|
||||
dependencies:
|
||||
'@types/needle': 3.3.0
|
||||
'@types/node': 16.18.85
|
||||
dev: false
|
||||
|
||||
/@types/prompts@2.4.9:
|
||||
resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==}
|
||||
dependencies:
|
||||
@@ -8644,6 +8659,17 @@ packages:
|
||||
dependencies:
|
||||
ms: 2.0.0
|
||||
|
||||
/debug@3.2.7:
|
||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
dev: false
|
||||
|
||||
/debug@4.3.4(supports-color@5.5.0):
|
||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
@@ -10985,14 +11011,6 @@ packages:
|
||||
resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
/image-size@1.1.1:
|
||||
resolution: {integrity: sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==}
|
||||
engines: {node: '>=16.x'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
queue: 6.0.2
|
||||
dev: false
|
||||
|
||||
/immer@9.0.21:
|
||||
resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==}
|
||||
|
||||
@@ -12068,7 +12086,7 @@ packages:
|
||||
resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
dependencies:
|
||||
'@types/node': 16.18.85
|
||||
'@types/node': 20.11.28
|
||||
merge-stream: 2.0.0
|
||||
supports-color: 8.1.1
|
||||
dev: true
|
||||
@@ -13071,6 +13089,18 @@ packages:
|
||||
/natural-compare@1.4.0:
|
||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||
|
||||
/needle@2.9.1:
|
||||
resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==}
|
||||
engines: {node: '>= 4.4.x'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
iconv-lite: 0.4.24
|
||||
sax: 1.3.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/negotiator@0.6.3:
|
||||
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -14796,6 +14826,16 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/probe-image-size@7.2.3:
|
||||
resolution: {integrity: sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==}
|
||||
dependencies:
|
||||
lodash.merge: 4.6.2
|
||||
needle: 2.9.1
|
||||
stream-parser: 0.3.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/process-nextick-args@2.0.1:
|
||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
dev: true
|
||||
@@ -14930,12 +14970,6 @@ packages:
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
|
||||
/queue@6.0.2:
|
||||
resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==}
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
dev: false
|
||||
|
||||
/quick-format-unescaped@4.0.4:
|
||||
resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
|
||||
|
||||
@@ -15627,7 +15661,6 @@ packages:
|
||||
|
||||
/sax@1.3.0:
|
||||
resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==}
|
||||
dev: true
|
||||
|
||||
/saxes@6.0.0:
|
||||
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
|
||||
@@ -16115,6 +16148,14 @@ packages:
|
||||
stubs: 3.0.0
|
||||
dev: true
|
||||
|
||||
/stream-parser@0.3.1:
|
||||
resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==}
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/stream-shift@1.0.3:
|
||||
resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==}
|
||||
dev: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useConfig } from '@payloadcms/ui/providers/Config'
|
||||
import LinkImport from 'next/link.js'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { useParams } from 'next/navigation.js'
|
||||
import React from 'react'
|
||||
|
||||
const Link = (LinkImport.default || LinkImport) as unknown as typeof LinkImport.default
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { EditViewComponent } from 'payload/types'
|
||||
|
||||
import { SetStepNav } from '@payloadcms/ui/elements/StepNav'
|
||||
import { notFound, redirect } from 'next/navigation.js'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { EditViewComponent } from '../../../../../packages/payload/types.js'
|
||||
|
||||
export const CustomEditView: EditViewComponent = ({ initPageResult }) => {
|
||||
if (!initPageResult) {
|
||||
notFound()
|
||||
|
||||
@@ -37,7 +37,6 @@ import {
|
||||
} from './shared.js'
|
||||
import {
|
||||
customIdCollectionId,
|
||||
customIdCollectionSlug,
|
||||
customViews2CollectionSlug,
|
||||
geoCollectionSlug,
|
||||
globalSlug,
|
||||
|
||||
@@ -21,6 +21,7 @@ export const customEditLabel = 'Custom Edit Label'
|
||||
export const customTabLabel = 'Custom Tab Label'
|
||||
|
||||
export const customTabViewPath = '/custom-tab-component'
|
||||
export const customTabViewTitle = 'Custom View With Tab Component'
|
||||
|
||||
export const customTabLabelViewTitle = 'Custom Tab Label View'
|
||||
|
||||
|
||||
@@ -12,5 +12,3 @@ export const AdminThumbnailCol: CollectionConfig = {
|
||||
},
|
||||
fields: [],
|
||||
}
|
||||
|
||||
export default AdminThumbnailCol
|
||||
|
||||
@@ -7,7 +7,7 @@ import { devUser } from '../credentials.js'
|
||||
import removeFiles from '../helpers/removeFiles.js'
|
||||
import { Uploads1 } from './collections/Upload1/index.js'
|
||||
import Uploads2 from './collections/Upload2/index.js'
|
||||
import AdminThumbnailCol from './collections/admin-thumbnail/index.js'
|
||||
import { AdminThumbnailCol } from './collections/admin-thumbnail/index.js'
|
||||
import {
|
||||
audioSlug,
|
||||
enlargeSlug,
|
||||
|
||||
@@ -12,9 +12,7 @@ import { initPageConsoleErrorCatch, saveDocAndAssert } from '../helpers.js'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
|
||||
import { initPayloadE2E } from '../helpers/initPayloadE2E.js'
|
||||
import { RESTClient } from '../helpers/rest.js'
|
||||
import { adminThumbnailSrc } from './collections/admin-thumbnail/RegisterThumbnailFn.js'
|
||||
import { adminThumbnailSlug, audioSlug, mediaSlug, relationSlug } from './shared.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
@@ -72,13 +70,18 @@ describe('uploads', () => {
|
||||
|
||||
test('should see upload filename in relation list', async () => {
|
||||
await page.goto(relationURL.list)
|
||||
|
||||
await wait(110)
|
||||
const field = page.locator('.cell-image')
|
||||
|
||||
await expect(field).toContainText('image.png')
|
||||
})
|
||||
|
||||
test('should see upload versioned filename in relation list', async () => {
|
||||
await page.goto(relationURL.list)
|
||||
const field = page.locator('.cell-versionedImage')
|
||||
|
||||
await expect(field).toContainText('image')
|
||||
})
|
||||
|
||||
test('should show upload filename in upload collection list', async () => {
|
||||
await page.goto(mediaURL.list)
|
||||
const audioUpload = page.locator('tr.row-1 .cell-filename')
|
||||
@@ -90,7 +93,6 @@ describe('uploads', () => {
|
||||
|
||||
test('should create file upload', async () => {
|
||||
await page.goto(mediaURL.create)
|
||||
|
||||
await page.setInputFiles('input[type="file"]', path.resolve(dirname, './image.png'))
|
||||
|
||||
const filename = page.locator('.file-field__filename')
|
||||
@@ -168,12 +170,11 @@ describe('uploads', () => {
|
||||
|
||||
test('should show draft uploads in the relation list', async () => {
|
||||
await page.goto(relationURL.list)
|
||||
|
||||
// from the list edit the first document
|
||||
await page.locator('.row-1 a').click()
|
||||
|
||||
// edit the versioned image
|
||||
await page.locator('.field-versionedImage .icon--edit').click()
|
||||
await page.locator('.field-type:nth-of-type(2) .icon--edit').click()
|
||||
|
||||
// fill the title with 'draft'
|
||||
await page.locator('#field-title').fill('draft')
|
||||
@@ -185,7 +186,7 @@ describe('uploads', () => {
|
||||
await page.locator('.doc-drawer__header-close').click()
|
||||
|
||||
// remove the selected versioned image
|
||||
await page.locator('.field-versionedImage .icon--x').click()
|
||||
await page.locator('.field-type:nth-of-type(2) .icon--x').click()
|
||||
|
||||
// choose from existing
|
||||
await page.locator('.list-drawer__toggler').click()
|
||||
@@ -195,20 +196,12 @@ describe('uploads', () => {
|
||||
|
||||
test('should restrict mimetype based on filterOptions', async () => {
|
||||
await page.goto(audioURL.edit(audioDoc.id))
|
||||
await wait(200)
|
||||
|
||||
// remove the selection and open the list drawer
|
||||
await page.locator('.file-details__remove').click()
|
||||
await page.locator('.upload__toggler.list-drawer__toggler').click()
|
||||
const listDrawer = page.locator('[id^=list-drawer_1_]')
|
||||
await expect(listDrawer).toBeVisible()
|
||||
await wait(200) // list is loading
|
||||
|
||||
// ensure the only card is the audio file
|
||||
const rows = listDrawer.locator('table tbody tr')
|
||||
expect(await rows.count()).toEqual(1)
|
||||
const filename = rows.locator('.cell-filename')
|
||||
await expect(filename).toHaveText('audio.mp3')
|
||||
|
||||
// upload an image and try to select it
|
||||
await listDrawer.locator('button.list-drawer__create-new-button.doc-drawer__toggler').click()
|
||||
@@ -217,30 +210,29 @@ describe('uploads', () => {
|
||||
.locator('[id^=doc-drawer_media_2_] .file-field__upload input[type="file"]')
|
||||
.setInputFiles(path.resolve(dirname, './image.png'))
|
||||
await page.locator('[id^=doc-drawer_media_2_] button#action-save').click()
|
||||
await wait(200)
|
||||
await expect(page.locator('.Toastify')).toContainText('successfully')
|
||||
|
||||
// save the document and expect an error
|
||||
await page.locator('button#action-save').click()
|
||||
await wait(200)
|
||||
await expect(page.locator('.Toastify')).toContainText('The following field is invalid: audio')
|
||||
await expect(page.locator('.Toastify')).toContainText('Please correct invalid fields.')
|
||||
})
|
||||
|
||||
test('Should execute adminThumbnail and provide thumbnail when set', async () => {
|
||||
await page.goto(adminThumbnailURL.list)
|
||||
await wait(200)
|
||||
await page.waitForURL(adminThumbnailURL.list)
|
||||
|
||||
// Ensure sure false or null shows generic file svg
|
||||
const genericUploadImage = page.locator('tr.row-1 .thumbnail svg')
|
||||
const genericUploadImage = page.locator('tr.row-1 .thumbnail img')
|
||||
await expect(genericUploadImage).toBeVisible()
|
||||
|
||||
// Ensure adminThumbnail fn returns correct value based on audio/mp3 mime
|
||||
const audioUploadImage = page.locator('tr.row-2 .thumbnail img')
|
||||
expect(await audioUploadImage.getAttribute('src')).toContain(adminThumbnailSrc)
|
||||
const audioUploadImage = page.locator('tr.row-2 .thumbnail svg')
|
||||
await expect(audioUploadImage).toBeVisible()
|
||||
})
|
||||
|
||||
test('Should detect correct mimeType', async () => {
|
||||
await page.goto(mediaURL.create)
|
||||
await page.waitForURL(mediaURL.create)
|
||||
await page.setInputFiles('input[type="file"]', path.resolve(dirname, './image.png'))
|
||||
await saveDocAndAssert(page)
|
||||
|
||||
@@ -274,12 +266,14 @@ describe('uploads', () => {
|
||||
const createFocalCrop = async (page: Page, position: 'bottom-right' | 'top-left') => {
|
||||
const { dragX, dragY, focalX, focalY } = positions[position]
|
||||
await page.goto(mediaURL.create)
|
||||
|
||||
await page.waitForURL(mediaURL.create)
|
||||
// select and upload file
|
||||
const fileChooserPromise = page.waitForEvent('filechooser')
|
||||
await page.getByText('Select a file').click()
|
||||
const fileChooser = await fileChooserPromise
|
||||
await wait(1000)
|
||||
await fileChooser.setFiles(path.join(dirname, 'test-image.jpg'))
|
||||
|
||||
await page.locator('.file-field__edit').click()
|
||||
|
||||
// set crop
|
||||
@@ -299,18 +293,18 @@ describe('uploads', () => {
|
||||
await expect(page.locator('.edit-upload__input input[name="X %"]')).toHaveValue(`${focalX}`)
|
||||
await expect(page.locator('.edit-upload__input input[name="Y %"]')).toHaveValue(`${focalY}`)
|
||||
|
||||
// apply crop
|
||||
await page.locator('button:has-text("Apply Changes")').click()
|
||||
await page.waitForSelector('button#action-save')
|
||||
await page.locator('button#action-save').click()
|
||||
await expect(page.locator('.Toastify')).toContainText('successfully')
|
||||
await wait(1000) // Wait for the save
|
||||
}
|
||||
|
||||
await createFocalCrop(page, 'bottom-right') // green square
|
||||
await wait(1000) // wait for edit view navigation (saving images)
|
||||
// get the ID of the doc
|
||||
const greenSquareMediaID = page.url().split('/').pop()
|
||||
const greenSquareMediaID = page.url().split('/').pop() // get the ID of the doc
|
||||
await createFocalCrop(page, 'top-left') // red square
|
||||
await wait(1000) // wait for edit view navigation (saving images)
|
||||
const redSquareMediaID = page.url().split('/').pop()
|
||||
const redSquareMediaID = page.url().split('/').pop() // get the ID of the doc
|
||||
|
||||
const { doc: greenDoc } = await client.findByID({
|
||||
id: greenSquareMediaID,
|
||||
|
||||
Reference in New Issue
Block a user