fix: image previews getting stuck in list view when paginating (#12062)
### What? In the List View, row data related to images and relationships gets stuck when you go from one page to another. ### Why? The `key` we are providing is not unique and not triggering the DOM to update. ### How? Uses the `row id` as a unique key prop to each table row to ensure proper re-rendering of rows during pagination. #### Testing Adds e2e test to `upload` test suite. You can recreate the issue using the `upload` test suite and new `list view preview` collection.
This commit is contained in:
committed by
GitHub
parent
101f7658f7
commit
a0fb3353c6
@@ -40,7 +40,14 @@ export const Table: React.FC<Props> = ({ appearance, columns, data }) => {
|
|||||||
<tbody>
|
<tbody>
|
||||||
{data &&
|
{data &&
|
||||||
data.map((row, rowIndex) => (
|
data.map((row, rowIndex) => (
|
||||||
<tr className={`row-${rowIndex + 1}`} key={rowIndex}>
|
<tr
|
||||||
|
className={`row-${rowIndex + 1}`}
|
||||||
|
key={
|
||||||
|
typeof row.id === 'string' || typeof row.id === 'number'
|
||||||
|
? String(row.id)
|
||||||
|
: rowIndex
|
||||||
|
}
|
||||||
|
>
|
||||||
{activeColumns.map((col, colIndex) => {
|
{activeColumns.map((col, colIndex) => {
|
||||||
const { accessor } = col
|
const { accessor } = col
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { CollectionSlug } from 'payload'
|
||||||
|
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { getFileByPath } from 'payload'
|
import { getFileByPath } from 'payload'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
@@ -18,6 +20,7 @@ import {
|
|||||||
enlargeSlug,
|
enlargeSlug,
|
||||||
focalNoSizesSlug,
|
focalNoSizesSlug,
|
||||||
hideFileInputOnCreateSlug,
|
hideFileInputOnCreateSlug,
|
||||||
|
listViewPreviewSlug,
|
||||||
mediaSlug,
|
mediaSlug,
|
||||||
mediaWithoutCacheTagsSlug,
|
mediaWithoutCacheTagsSlug,
|
||||||
mediaWithoutRelationPreviewSlug,
|
mediaWithoutRelationPreviewSlug,
|
||||||
@@ -760,6 +763,26 @@ export default buildConfigWithDefaults({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
slug: listViewPreviewSlug,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'imageUpload',
|
||||||
|
type: 'upload',
|
||||||
|
relationTo: mediaWithRelationPreviewSlug,
|
||||||
|
displayPreview: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'imageRelationship',
|
||||||
|
type: 'relationship',
|
||||||
|
relationTo: mediaWithRelationPreviewSlug,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
onInit: async (payload) => {
|
onInit: async (payload) => {
|
||||||
const uploadsDir = path.resolve(dirname, './media')
|
const uploadsDir = path.resolve(dirname, './media')
|
||||||
@@ -932,6 +955,22 @@ export default buildConfigWithDefaults({
|
|||||||
},
|
},
|
||||||
file: imageFile,
|
file: imageFile,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
const data = {
|
||||||
|
title: `List View Preview ${i + 1}`,
|
||||||
|
imageUpload: uploadedImageWithPreview,
|
||||||
|
imageRelationship: uploadedImageWithPreview,
|
||||||
|
}
|
||||||
|
if (i > 15) {
|
||||||
|
data.imageUpload = ''
|
||||||
|
data.imageRelationship = ''
|
||||||
|
}
|
||||||
|
await payload.create({
|
||||||
|
collection: listViewPreviewSlug as CollectionSlug,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
serverURL: undefined,
|
serverURL: undefined,
|
||||||
upload: {
|
upload: {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
customUploadFieldSlug,
|
customUploadFieldSlug,
|
||||||
focalOnlySlug,
|
focalOnlySlug,
|
||||||
hideFileInputOnCreateSlug,
|
hideFileInputOnCreateSlug,
|
||||||
|
listViewPreviewSlug,
|
||||||
mediaSlug,
|
mediaSlug,
|
||||||
mediaWithoutCacheTagsSlug,
|
mediaWithoutCacheTagsSlug,
|
||||||
relationPreviewSlug,
|
relationPreviewSlug,
|
||||||
@@ -55,6 +56,7 @@ let relationURL: AdminUrlUtil
|
|||||||
let adminThumbnailSizeURL: AdminUrlUtil
|
let adminThumbnailSizeURL: AdminUrlUtil
|
||||||
let adminThumbnailFunctionURL: AdminUrlUtil
|
let adminThumbnailFunctionURL: AdminUrlUtil
|
||||||
let adminThumbnailWithSearchQueriesURL: AdminUrlUtil
|
let adminThumbnailWithSearchQueriesURL: AdminUrlUtil
|
||||||
|
let listViewPreviewURL: AdminUrlUtil
|
||||||
let mediaWithoutCacheTagsSlugURL: AdminUrlUtil
|
let mediaWithoutCacheTagsSlugURL: AdminUrlUtil
|
||||||
let focalOnlyURL: AdminUrlUtil
|
let focalOnlyURL: AdminUrlUtil
|
||||||
let withMetadataURL: AdminUrlUtil
|
let withMetadataURL: AdminUrlUtil
|
||||||
@@ -89,6 +91,7 @@ describe('Uploads', () => {
|
|||||||
serverURL,
|
serverURL,
|
||||||
adminThumbnailWithSearchQueries,
|
adminThumbnailWithSearchQueries,
|
||||||
)
|
)
|
||||||
|
listViewPreviewURL = new AdminUrlUtil(serverURL, listViewPreviewSlug)
|
||||||
mediaWithoutCacheTagsSlugURL = new AdminUrlUtil(serverURL, mediaWithoutCacheTagsSlug)
|
mediaWithoutCacheTagsSlugURL = new AdminUrlUtil(serverURL, mediaWithoutCacheTagsSlug)
|
||||||
focalOnlyURL = new AdminUrlUtil(serverURL, focalOnlySlug)
|
focalOnlyURL = new AdminUrlUtil(serverURL, focalOnlySlug)
|
||||||
withMetadataURL = new AdminUrlUtil(serverURL, withMetadataSlug)
|
withMetadataURL = new AdminUrlUtil(serverURL, withMetadataSlug)
|
||||||
@@ -1400,4 +1403,30 @@ describe('Uploads', () => {
|
|||||||
await expect(thumbnail).toHaveAttribute('src', '/api/focal-only/file/small.png')
|
await expect(thumbnail).toHaveAttribute('src', '/api/focal-only/file/small.png')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should show correct image preview or placeholder after paginating', async () => {
|
||||||
|
await page.goto(listViewPreviewURL.list)
|
||||||
|
const firstRow = page.locator('.row-1')
|
||||||
|
|
||||||
|
const imageUploadCell = firstRow.locator('.cell-imageUpload .relationship-cell')
|
||||||
|
await expect(imageUploadCell).toHaveText('<No Image Upload>')
|
||||||
|
|
||||||
|
const imageRelationshipCell = firstRow.locator('.cell-imageRelationship .relationship-cell')
|
||||||
|
await expect(imageRelationshipCell).toHaveText('<No Image Relationship>')
|
||||||
|
|
||||||
|
const pageTwoButton = page.locator('.paginator__page', { hasText: '2' })
|
||||||
|
await expect(pageTwoButton).toBeVisible()
|
||||||
|
await pageTwoButton.click()
|
||||||
|
|
||||||
|
const imageUploadImg = imageUploadCell.locator('.thumbnail')
|
||||||
|
await expect(imageUploadImg).toBeVisible()
|
||||||
|
await expect(imageRelationshipCell).toHaveText('image-1.png')
|
||||||
|
|
||||||
|
const pageOneButton = page.locator('.paginator__page', { hasText: '1' })
|
||||||
|
await expect(pageOneButton).toBeVisible()
|
||||||
|
await pageOneButton.click()
|
||||||
|
|
||||||
|
await expect(imageUploadCell).toHaveText('<No Image Upload>')
|
||||||
|
await expect(imageRelationshipCell).toHaveText('<No Image Relationship>')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -22,3 +22,5 @@ export const withMetadataSlug = 'with-meta-data'
|
|||||||
export const withoutMetadataSlug = 'without-meta-data'
|
export const withoutMetadataSlug = 'without-meta-data'
|
||||||
export const withOnlyJPEGMetadataSlug = 'with-only-jpeg-meta-data'
|
export const withOnlyJPEGMetadataSlug = 'with-only-jpeg-meta-data'
|
||||||
export const customFileNameMediaSlug = 'custom-file-name-media'
|
export const customFileNameMediaSlug = 'custom-file-name-media'
|
||||||
|
|
||||||
|
export const listViewPreviewSlug = 'list-view-preview'
|
||||||
|
|||||||
Reference in New Issue
Block a user