fix(ui): bulk upload losing state when adding additional files (#12946)
Fixes an issue where adding additional upload files would clear the state of the originally uploaded files.
This commit is contained in:
@@ -83,12 +83,13 @@
|
|||||||
background-color: var(--theme-elevation-100);
|
background-color: var(--theme-elevation-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
.thumbnail {
|
.file-selections__thumbnail,
|
||||||
width: base(1.2);
|
.file-selections__thumbnail-shimmer {
|
||||||
height: base(1.2);
|
width: calc(var(--base) * 1.2);
|
||||||
|
height: calc(var(--base) * 1.2);
|
||||||
|
border-radius: var(--style-radius-s);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: var(--style-radius-s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ import { Drawer } from '../../Drawer/index.js'
|
|||||||
import { ErrorPill } from '../../ErrorPill/index.js'
|
import { ErrorPill } from '../../ErrorPill/index.js'
|
||||||
import { Pill } from '../../Pill/index.js'
|
import { Pill } from '../../Pill/index.js'
|
||||||
import { ShimmerEffect } from '../../ShimmerEffect/index.js'
|
import { ShimmerEffect } from '../../ShimmerEffect/index.js'
|
||||||
|
import { createThumbnail } from '../../Thumbnail/createThumbnail.js'
|
||||||
import { Thumbnail } from '../../Thumbnail/index.js'
|
import { Thumbnail } from '../../Thumbnail/index.js'
|
||||||
import { Actions } from '../ActionsBar/index.js'
|
import { Actions } from '../ActionsBar/index.js'
|
||||||
import './index.scss'
|
|
||||||
import { AddFilesView } from '../AddFilesView/index.js'
|
import { AddFilesView } from '../AddFilesView/index.js'
|
||||||
import { useFormsManager } from '../FormsManager/index.js'
|
import { useFormsManager } from '../FormsManager/index.js'
|
||||||
import { useBulkUpload } from '../index.js'
|
import { useBulkUpload } from '../index.js'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
const addMoreFilesDrawerSlug = 'bulk-upload-drawer--add-more-files'
|
const addMoreFilesDrawerSlug = 'bulk-upload-drawer--add-more-files'
|
||||||
|
|
||||||
@@ -33,7 +34,6 @@ export function FileSidebar() {
|
|||||||
isInitializing,
|
isInitializing,
|
||||||
removeFile,
|
removeFile,
|
||||||
setActiveIndex,
|
setActiveIndex,
|
||||||
thumbnailUrls,
|
|
||||||
totalErrorCount,
|
totalErrorCount,
|
||||||
} = useFormsManager()
|
} = useFormsManager()
|
||||||
const { initialFiles, maxFiles } = useBulkUpload()
|
const { initialFiles, maxFiles } = useBulkUpload()
|
||||||
@@ -139,7 +139,7 @@ export function FileSidebar() {
|
|||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
: null}
|
: null}
|
||||||
{forms.map(({ errorCount, formState }, index) => {
|
{forms.map(({ errorCount, formID, formState }, index) => {
|
||||||
const currentFile = (formState?.file?.value as File) || ({} as File)
|
const currentFile = (formState?.file?.value as File) || ({} as File)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -151,17 +151,14 @@ export function FileSidebar() {
|
|||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ')}
|
.join(' ')}
|
||||||
key={index}
|
key={formID}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
className={`${baseClass}__fileRow`}
|
className={`${baseClass}__fileRow`}
|
||||||
onClick={() => setActiveIndex(index)}
|
onClick={() => setActiveIndex(index)}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Thumbnail
|
<SidebarThumbnail file={currentFile} formID={formID} />
|
||||||
className={`${baseClass}__thumbnail`}
|
|
||||||
fileSrc={isImage(currentFile.type) ? thumbnailUrls[index] : null}
|
|
||||||
/>
|
|
||||||
<div className={`${baseClass}__fileDetails`}>
|
<div className={`${baseClass}__fileDetails`}>
|
||||||
<p className={`${baseClass}__fileName`} title={currentFile.name}>
|
<p className={`${baseClass}__fileName`} title={currentFile.name}>
|
||||||
{currentFile.name || t('upload:noFile')}
|
{currentFile.name || t('upload:noFile')}
|
||||||
@@ -200,3 +197,54 @@ export function FileSidebar() {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SidebarThumbnail({ file, formID }: { file: File; formID: string }) {
|
||||||
|
const [thumbnailURL, setThumbnailURL] = React.useState<null | string>(null)
|
||||||
|
const [isLoading, setIsLoading] = React.useState(true)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
let isCancelled = false
|
||||||
|
|
||||||
|
async function generateThumbnail() {
|
||||||
|
setIsLoading(true)
|
||||||
|
setThumbnailURL(null)
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isImage(file.type)) {
|
||||||
|
const url = await createThumbnail(file)
|
||||||
|
if (!isCancelled) {
|
||||||
|
setThumbnailURL(url)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setThumbnailURL(null)
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
if (!isCancelled) {
|
||||||
|
setThumbnailURL(null)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (!isCancelled) {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void generateThumbnail()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isCancelled = true
|
||||||
|
}
|
||||||
|
}, [file])
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <ShimmerEffect className={`${baseClass}__thumbnail-shimmer`} disableInlineStyles />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Thumbnail
|
||||||
|
className={`${baseClass}__thumbnail`}
|
||||||
|
fileSrc={thumbnailURL}
|
||||||
|
key={`${formID}-${thumbnailURL || 'placeholder'}`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import type {
|
|||||||
} from 'payload'
|
} from 'payload'
|
||||||
|
|
||||||
import { useModal } from '@faceless-ui/modal'
|
import { useModal } from '@faceless-ui/modal'
|
||||||
import { isImage } from 'payload/shared'
|
|
||||||
import * as qs from 'qs-esm'
|
import * as qs from 'qs-esm'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
@@ -25,7 +24,6 @@ import { useUploadHandlers } from '../../../providers/UploadHandlers/index.js'
|
|||||||
import { hasSavePermission as getHasSavePermission } from '../../../utilities/hasSavePermission.js'
|
import { hasSavePermission as getHasSavePermission } from '../../../utilities/hasSavePermission.js'
|
||||||
import { LoadingOverlay } from '../../Loading/index.js'
|
import { LoadingOverlay } from '../../Loading/index.js'
|
||||||
import { useLoadingOverlay } from '../../LoadingOverlay/index.js'
|
import { useLoadingOverlay } from '../../LoadingOverlay/index.js'
|
||||||
import { createThumbnail } from '../../Thumbnail/createThumbnail.js'
|
|
||||||
import { useBulkUpload } from '../index.js'
|
import { useBulkUpload } from '../index.js'
|
||||||
import { createFormData } from './createFormData.js'
|
import { createFormData } from './createFormData.js'
|
||||||
import { formsManagementReducer } from './reducer.js'
|
import { formsManagementReducer } from './reducer.js'
|
||||||
@@ -57,7 +55,6 @@ type FormsManagerContext = {
|
|||||||
errorCount: number
|
errorCount: number
|
||||||
index: number
|
index: number
|
||||||
}) => void
|
}) => void
|
||||||
readonly thumbnailUrls: string[]
|
|
||||||
readonly totalErrorCount?: number
|
readonly totalErrorCount?: number
|
||||||
readonly updateUploadEdits: (args: UploadEdits) => void
|
readonly updateUploadEdits: (args: UploadEdits) => void
|
||||||
}
|
}
|
||||||
@@ -79,7 +76,6 @@ const Context = React.createContext<FormsManagerContext>({
|
|||||||
saveAllDocs: () => Promise.resolve(),
|
saveAllDocs: () => Promise.resolve(),
|
||||||
setActiveIndex: () => 0,
|
setActiveIndex: () => 0,
|
||||||
setFormTotalErrorCount: () => {},
|
setFormTotalErrorCount: () => {},
|
||||||
thumbnailUrls: [],
|
|
||||||
totalErrorCount: 0,
|
totalErrorCount: 0,
|
||||||
updateUploadEdits: () => {},
|
updateUploadEdits: () => {},
|
||||||
})
|
})
|
||||||
@@ -119,37 +115,6 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
|
|||||||
|
|
||||||
const formsRef = React.useRef(forms)
|
const formsRef = React.useRef(forms)
|
||||||
formsRef.current = forms
|
formsRef.current = forms
|
||||||
const formsCount = forms.length
|
|
||||||
|
|
||||||
const thumbnailUrlsRef = React.useRef<string[]>([])
|
|
||||||
const processedFiles = React.useRef(new Set()) // Track already-processed files
|
|
||||||
const [renderedThumbnails, setRenderedThumbnails] = React.useState<string[]>([])
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
;(async () => {
|
|
||||||
const newThumbnails = [...thumbnailUrlsRef.current]
|
|
||||||
|
|
||||||
for (let i = 0; i < formsCount; i++) {
|
|
||||||
const file = formsRef.current[i].formState.file.value as File
|
|
||||||
|
|
||||||
// Skip if already processed
|
|
||||||
if (processedFiles.current.has(file) || !file || !isImage(file.type)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
processedFiles.current.add(file)
|
|
||||||
|
|
||||||
// Generate thumbnail and update ref
|
|
||||||
const thumbnailUrl = await createThumbnail(file)
|
|
||||||
newThumbnails[i] = thumbnailUrl
|
|
||||||
thumbnailUrlsRef.current = newThumbnails
|
|
||||||
|
|
||||||
// Trigger re-render in batches
|
|
||||||
setRenderedThumbnails([...newThumbnails])
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
}, [formsCount])
|
|
||||||
|
|
||||||
const { toggleLoadingOverlay } = useLoadingOverlay()
|
const { toggleLoadingOverlay } = useLoadingOverlay()
|
||||||
const { closeModal } = useModal()
|
const { closeModal } = useModal()
|
||||||
@@ -250,6 +215,7 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
|
|||||||
if (i === activeIndex) {
|
if (i === activeIndex) {
|
||||||
return {
|
return {
|
||||||
errorCount: form.errorCount,
|
errorCount: form.errorCount,
|
||||||
|
formID: form.formID,
|
||||||
formState: currentFormsData,
|
formState: currentFormsData,
|
||||||
uploadEdits: form.uploadEdits,
|
uploadEdits: form.uploadEdits,
|
||||||
}
|
}
|
||||||
@@ -264,6 +230,16 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
|
|||||||
|
|
||||||
const addFiles = React.useCallback(
|
const addFiles = React.useCallback(
|
||||||
async (files: FileList) => {
|
async (files: FileList) => {
|
||||||
|
if (forms.length) {
|
||||||
|
// save the state of the current form before adding new files
|
||||||
|
dispatch({
|
||||||
|
type: 'UPDATE_FORM',
|
||||||
|
errorCount: forms[activeIndex].errorCount,
|
||||||
|
formState: getFormDataRef.current(),
|
||||||
|
index: activeIndex,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
toggleLoadingOverlay({ isLoading: true, key: 'addingDocs' })
|
toggleLoadingOverlay({ isLoading: true, key: 'addingDocs' })
|
||||||
if (!hasInitializedState) {
|
if (!hasInitializedState) {
|
||||||
await initializeSharedFormState()
|
await initializeSharedFormState()
|
||||||
@@ -271,22 +247,13 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
|
|||||||
dispatch({ type: 'ADD_FORMS', files, initialState: initialStateRef.current })
|
dispatch({ type: 'ADD_FORMS', files, initialState: initialStateRef.current })
|
||||||
toggleLoadingOverlay({ isLoading: false, key: 'addingDocs' })
|
toggleLoadingOverlay({ isLoading: false, key: 'addingDocs' })
|
||||||
},
|
},
|
||||||
[initializeSharedFormState, hasInitializedState, toggleLoadingOverlay],
|
[initializeSharedFormState, hasInitializedState, toggleLoadingOverlay, activeIndex, forms],
|
||||||
)
|
)
|
||||||
|
|
||||||
const removeThumbnails = React.useCallback((indexes: number[]) => {
|
const removeFile: FormsManagerContext['removeFile'] = React.useCallback((index) => {
|
||||||
thumbnailUrlsRef.current = thumbnailUrlsRef.current.filter((_, i) => !indexes.includes(i))
|
dispatch({ type: 'REMOVE_FORM', index })
|
||||||
setRenderedThumbnails([...thumbnailUrlsRef.current])
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const removeFile: FormsManagerContext['removeFile'] = React.useCallback(
|
|
||||||
(index) => {
|
|
||||||
dispatch({ type: 'REMOVE_FORM', index })
|
|
||||||
removeThumbnails([index])
|
|
||||||
},
|
|
||||||
[removeThumbnails],
|
|
||||||
)
|
|
||||||
|
|
||||||
const setFormTotalErrorCount: FormsManagerContext['setFormTotalErrorCount'] = React.useCallback(
|
const setFormTotalErrorCount: FormsManagerContext['setFormTotalErrorCount'] = React.useCallback(
|
||||||
({ errorCount, index }) => {
|
({ errorCount, index }) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
@@ -304,6 +271,7 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
|
|||||||
const currentForms = [...forms]
|
const currentForms = [...forms]
|
||||||
currentForms[activeIndex] = {
|
currentForms[activeIndex] = {
|
||||||
errorCount: currentForms[activeIndex].errorCount,
|
errorCount: currentForms[activeIndex].errorCount,
|
||||||
|
formID: currentForms[activeIndex].formID,
|
||||||
formState: currentFormsData,
|
formState: currentFormsData,
|
||||||
uploadEdits: currentForms[activeIndex].uploadEdits,
|
uploadEdits: currentForms[activeIndex].uploadEdits,
|
||||||
}
|
}
|
||||||
@@ -372,6 +340,7 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
|
|||||||
|
|
||||||
currentForms[i] = {
|
currentForms[i] = {
|
||||||
errorCount: fieldErrors.length,
|
errorCount: fieldErrors.length,
|
||||||
|
formID: currentForms[i].formID,
|
||||||
formState: fieldReducer(currentForms[i].formState, {
|
formState: fieldReducer(currentForms[i].formState, {
|
||||||
type: 'ADD_SERVER_ERRORS',
|
type: 'ADD_SERVER_ERRORS',
|
||||||
errors: fieldErrors,
|
errors: fieldErrors,
|
||||||
@@ -416,10 +385,6 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
|
|||||||
if (typeof onSuccess === 'function') {
|
if (typeof onSuccess === 'function') {
|
||||||
onSuccess(newDocs, errorCount)
|
onSuccess(newDocs, errorCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remainingForms.length && thumbnailIndexesToRemove.length) {
|
|
||||||
removeThumbnails(thumbnailIndexesToRemove)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorCount) {
|
if (errorCount) {
|
||||||
@@ -439,15 +404,14 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
actionURL,
|
actionURL,
|
||||||
activeIndex,
|
|
||||||
forms,
|
|
||||||
removeThumbnails,
|
|
||||||
onSuccess,
|
|
||||||
collectionSlug,
|
collectionSlug,
|
||||||
getUploadHandler,
|
getUploadHandler,
|
||||||
t,
|
t,
|
||||||
|
forms,
|
||||||
|
activeIndex,
|
||||||
closeModal,
|
closeModal,
|
||||||
drawerSlug,
|
drawerSlug,
|
||||||
|
onSuccess,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -578,7 +542,6 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
|
|||||||
saveAllDocs,
|
saveAllDocs,
|
||||||
setActiveIndex,
|
setActiveIndex,
|
||||||
setFormTotalErrorCount,
|
setFormTotalErrorCount,
|
||||||
thumbnailUrls: renderedThumbnails,
|
|
||||||
totalErrorCount,
|
totalErrorCount,
|
||||||
updateUploadEdits,
|
updateUploadEdits,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export type State = {
|
|||||||
activeIndex: number
|
activeIndex: number
|
||||||
forms: {
|
forms: {
|
||||||
errorCount: number
|
errorCount: number
|
||||||
|
formID: string
|
||||||
formState: FormState
|
formState: FormState
|
||||||
uploadEdits?: UploadEdits
|
uploadEdits?: UploadEdits
|
||||||
}[]
|
}[]
|
||||||
@@ -49,6 +50,7 @@ export function formsManagementReducer(state: State, action: Action): State {
|
|||||||
for (let i = 0; i < action.files.length; i++) {
|
for (let i = 0; i < action.files.length; i++) {
|
||||||
newForms[i] = {
|
newForms[i] = {
|
||||||
errorCount: 0,
|
errorCount: 0,
|
||||||
|
formID: crypto.randomUUID(),
|
||||||
formState: {
|
formState: {
|
||||||
...(action.initialState || {}),
|
...(action.initialState || {}),
|
||||||
file: {
|
file: {
|
||||||
|
|||||||
@@ -6,21 +6,25 @@ import './index.scss'
|
|||||||
|
|
||||||
export type ShimmerEffectProps = {
|
export type ShimmerEffectProps = {
|
||||||
readonly animationDelay?: string
|
readonly animationDelay?: string
|
||||||
|
readonly className?: string
|
||||||
|
readonly disableInlineStyles?: boolean
|
||||||
readonly height?: number | string
|
readonly height?: number | string
|
||||||
readonly width?: number | string
|
readonly width?: number | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ShimmerEffect: React.FC<ShimmerEffectProps> = ({
|
export const ShimmerEffect: React.FC<ShimmerEffectProps> = ({
|
||||||
animationDelay = '0ms',
|
animationDelay = '0ms',
|
||||||
|
className,
|
||||||
|
disableInlineStyles = false,
|
||||||
height = '60px',
|
height = '60px',
|
||||||
width = '100%',
|
width = '100%',
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="shimmer-effect"
|
className={['shimmer-effect', className].filter(Boolean).join(' ')}
|
||||||
style={{
|
style={{
|
||||||
height: typeof height === 'number' ? `${height}px` : height,
|
height: !disableInlineStyles && (typeof height === 'number' ? `${height}px` : height),
|
||||||
width: typeof width === 'number' ? `${width}px` : width,
|
width: !disableInlineStyles && (typeof width === 'number' ? `${width}px` : width),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -27,12 +27,16 @@ export const createThumbnail = (file: File): Promise<string> => {
|
|||||||
const canvas = new OffscreenCanvas(drawWidth, drawHeight) // Create an OffscreenCanvas
|
const canvas = new OffscreenCanvas(drawWidth, drawHeight) // Create an OffscreenCanvas
|
||||||
const ctx = canvas.getContext('2d')
|
const ctx = canvas.getContext('2d')
|
||||||
|
|
||||||
|
// Determine output format based on input file type
|
||||||
|
const outputFormat = file.type === 'image/png' ? 'image/png' : 'image/jpeg'
|
||||||
|
const quality = file.type === 'image/png' ? undefined : 0.8 // PNG doesn't use quality, use higher quality for JPEG
|
||||||
|
|
||||||
// Draw the image onto the OffscreenCanvas with calculated dimensions
|
// Draw the image onto the OffscreenCanvas with calculated dimensions
|
||||||
ctx.drawImage(img, 0, 0, drawWidth, drawHeight)
|
ctx.drawImage(img, 0, 0, drawWidth, drawHeight)
|
||||||
|
|
||||||
// Convert the OffscreenCanvas to a Blob and free up memory
|
// Convert the OffscreenCanvas to a Blob and free up memory
|
||||||
canvas
|
canvas
|
||||||
.convertToBlob({ type: 'image/jpeg', quality: 0.25 })
|
.convertToBlob({ type: outputFormat, ...(quality && { quality }) })
|
||||||
.then((blob) => {
|
.then((blob) => {
|
||||||
URL.revokeObjectURL(img.src) // Release the Object URL
|
URL.revokeObjectURL(img.src) // Release the Object URL
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
|
|||||||
@@ -1145,6 +1145,34 @@ describe('Uploads', () => {
|
|||||||
.first()
|
.first()
|
||||||
await expect(errorCount).toHaveText('1')
|
await expect(errorCount).toHaveText('1')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should preserve state when adding additional files to an existing bulk upload', async () => {
|
||||||
|
await page.goto(uploadsTwo.list)
|
||||||
|
await page.locator('.list-header__title-actions button', { hasText: 'Bulk Upload' }).click()
|
||||||
|
|
||||||
|
await page.setInputFiles('.dropzone input[type="file"]', path.resolve(dirname, './image.png'))
|
||||||
|
|
||||||
|
await page.locator('#field-prefix').fill('should-preserve')
|
||||||
|
|
||||||
|
// add another file
|
||||||
|
await page
|
||||||
|
.locator('.file-selections__header__actions button', { hasText: 'Add File' })
|
||||||
|
.click()
|
||||||
|
await page.setInputFiles('.dropzone input[type="file"]', path.resolve(dirname, './small.png'))
|
||||||
|
|
||||||
|
const originalFileRow = page
|
||||||
|
.locator('.file-selections__filesContainer .file-selections__fileRowContainer')
|
||||||
|
.nth(1)
|
||||||
|
|
||||||
|
// ensure the original file thumbnail is visible (not using default placeholder svg)
|
||||||
|
await expect(originalFileRow.locator('.thumbnail img')).toBeVisible()
|
||||||
|
|
||||||
|
// navigate to the first file added
|
||||||
|
await originalFileRow.locator('button.file-selections__fileRow').click()
|
||||||
|
|
||||||
|
// ensure the prefix field is still filled with the original value
|
||||||
|
await expect(page.locator('#field-prefix')).toHaveValue('should-preserve')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('remote url fetching', () => {
|
describe('remote url fetching', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user