feat(ui): hides lock icon when locked by current user (#8309)

This commit is contained in:
Patrik
2024-09-19 14:13:14 -04:00
committed by GitHub
parent 405a6c3447
commit 879f690161
5 changed files with 80 additions and 33 deletions

View File

@@ -140,7 +140,7 @@ export const DefaultDashboard: React.FC<DashboardProps> = (props) => {
<li key={entityIndex}>
<Card
actions={
lockStatus ? (
lockStatus && user?.id !== userEditing?.id ? (
<Locked className={`${baseClass}__locked`} user={userEditing} />
) : hasCreatePermission && type === EntityType.collection ? (
<Button

View File

@@ -21,6 +21,7 @@ import {
StaggeredShimmers,
Table,
UnpublishMany,
useAuth,
useBulkUpload,
useConfig,
useEditDepth,
@@ -43,6 +44,7 @@ const baseClass = 'collection-list'
const Link = (LinkImport.default || LinkImport) as unknown as typeof LinkImport.default
export const DefaultListView: React.FC = () => {
const { user } = useAuth()
const {
beforeActions,
collectionSlug,
@@ -125,7 +127,7 @@ export const DefaultListView: React.FC = () => {
return (
<div className={`${baseClass} ${baseClass}--${collectionSlug}`}>
<SetViewActions actions={actions} />
<SelectionProvider docs={data.docs} totalDocs={data.totalDocs}>
<SelectionProvider docs={data.docs} totalDocs={data.totalDocs} user={user}>
<RenderComponent mappedComponent={beforeList} />
<Gutter className={`${baseClass}__wrap`}>
{Header || (

View File

@@ -3,6 +3,7 @@ import React from 'react'
import { useTableCell } from '../../elements/Table/TableCellProvider/index.js'
import { CheckboxInput } from '../../fields/Checkbox/Input.js'
import { useAuth } from '../../providers/Auth/index.js'
import { useSelection } from '../../providers/Selection/index.js'
import { Locked } from '../Locked/index.js'
import './index.scss'
@@ -10,13 +11,14 @@ import './index.scss'
const baseClass = 'select-row'
export const SelectRow: React.FC = () => {
const { user } = useAuth()
const { selected, setSelection } = useSelection()
const { rowData } = useTableCell()
const { _isLocked, _userEditing } = rowData || {}
const documentIsLocked = _isLocked && _userEditing
if (documentIsLocked) {
if (documentIsLocked && _userEditing.id !== user?.id) {
return <Locked user={_userEditing} />
}

View File

@@ -1,5 +1,5 @@
'use client'
import type { Where } from 'payload'
import type { ClientUser, Where } from 'payload'
import * as qs from 'qs-esm'
import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'
@@ -33,9 +33,10 @@ type Props = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
readonly docs: any[]
readonly totalDocs: number
user: ClientUser
}
export const SelectionProvider: React.FC<Props> = ({ children, docs = [], totalDocs }) => {
export const SelectionProvider: React.FC<Props> = ({ children, docs = [], totalDocs, user }) => {
const contextRef = useRef({} as SelectionContext)
const { code: locale } = useLocale()
@@ -56,8 +57,8 @@ export const SelectionProvider: React.FC<Props> = ({ children, docs = [], totalD
const rows = new Map()
if (allAvailable) {
setSelectAll(SelectAllStatus.AllAvailable)
docs.forEach(({ id, _isLocked }) => {
if (!_isLocked) {
docs.forEach(({ id, _isLocked, _userEditing }) => {
if (!_isLocked || _userEditing?.id === user?.id) {
rows.set(id, true)
}
})
@@ -67,22 +68,22 @@ export const SelectionProvider: React.FC<Props> = ({ children, docs = [], totalD
) {
setSelectAll(SelectAllStatus.None)
} else {
docs.forEach(({ id, _isLocked }) => {
if (!_isLocked) {
docs.forEach(({ id, _isLocked, _userEditing }) => {
if (!_isLocked || _userEditing?.id === user?.id) {
rows.set(id, selectAll !== SelectAllStatus.Some)
}
})
}
setSelected(rows)
},
[docs, selectAll],
[docs, selectAll, user?.id],
)
const setSelection = useCallback(
(id) => {
const doc = docs.find((doc) => doc.id === id)
if (doc?._isLocked) {
if (doc?._isLocked && user?.id !== doc?._userEditing.id) {
return // Prevent selection if the document is locked
}
@@ -99,7 +100,7 @@ export const SelectionProvider: React.FC<Props> = ({ children, docs = [], totalD
setSelected(newMap)
},
[selected, docs],
[selected, docs, user?.id],
)
const getQueryParams = useCallback(

View File

@@ -70,6 +70,7 @@ describe('locked documents', () => {
describe('list view - collections', () => {
let postDoc
let anotherPostDoc
let user2
let lockedDoc
@@ -78,6 +79,10 @@ describe('locked documents', () => {
text: 'hello',
})
anotherPostDoc = await createPostDoc({
text: 'another post',
})
user2 = await payload.create({
collection: 'users',
data: {
@@ -114,6 +119,11 @@ describe('locked documents', () => {
id: postDoc.id,
})
await payload.delete({
collection: 'posts',
id: anotherPostDoc.id,
})
await payload.delete({
collection: 'users',
id: user2.id,
@@ -124,14 +134,29 @@ describe('locked documents', () => {
await page.goto(postsUrl.list)
await page.waitForURL(postsUrl.list)
await expect(page.locator('.table .row-1 .locked svg')).toBeVisible()
await expect(page.locator('.table .row-2 .locked svg')).toBeVisible()
})
test('should show no lock icon on document row if unlocked', async () => {
test('should not show lock icon on document row if unlocked', async () => {
await page.goto(postsUrl.list)
await page.waitForURL(postsUrl.list)
await expect(page.locator('.table .row-2 .checkbox-input__input')).toBeVisible()
await expect(page.locator('.table .row-3 .checkbox-input__input')).toBeVisible()
})
test('should not show lock icon on document row if locked by current user', async () => {
await page.goto(postsUrl.edit(anotherPostDoc.id))
await page.waitForURL(postsUrl.edit(anotherPostDoc.id))
const textInput = page.locator('#field-text')
await textInput.fill('testing')
await page.reload()
await page.goto(postsUrl.list)
await page.waitForURL(postsUrl.list)
await expect(page.locator('.table .row-1 .checkbox-input__input')).toBeVisible()
})
test('should only allow bulk delete on unlocked documents', async () => {
@@ -139,7 +164,7 @@ describe('locked documents', () => {
await page.locator('input#select-all').check()
await page.locator('.delete-documents__toggle').click()
await expect(page.locator('.delete-documents__content p')).toHaveText(
'You are about to delete 1 Posts',
'You are about to delete 2 Posts',
)
})
})
@@ -744,6 +769,7 @@ describe('locked documents', () => {
describe('dashboard - globals', () => {
let user2
let lockedGlobal
beforeAll(async () => {
user2 = await payload.create({
@@ -753,6 +779,19 @@ describe('locked documents', () => {
password: '1234',
},
})
lockedGlobal = await payload.create({
collection: lockedDocumentCollection,
data: {
document: undefined,
editedAt: new Date().toISOString(),
globalSlug: 'menu',
user: {
relationTo: 'users',
value: user2.id,
},
},
})
})
afterAll(async () => {
@@ -763,17 +802,17 @@ describe('locked documents', () => {
})
test('should show lock on document card in dashboard view if locked', async () => {
const lockedGlobal = await payload.create({
await page.goto(postsUrl.admin)
await page.waitForURL(postsUrl.admin)
const globalCardList = page.locator('.dashboard__group').nth(1)
await expect(globalCardList.locator('#card-menu .locked svg')).toBeVisible()
})
test('should not show lock on document card in dashboard view if unlocked', async () => {
await payload.delete({
collection: lockedDocumentCollection,
data: {
document: undefined,
editedAt: new Date().toISOString(),
globalSlug: 'menu',
user: {
relationTo: 'users',
value: user2.id,
},
},
id: lockedGlobal.id,
})
// eslint-disable-next-line payload/no-wait-function
@@ -783,15 +822,18 @@ describe('locked documents', () => {
await page.waitForURL(postsUrl.admin)
const globalCardList = page.locator('.dashboard__group').nth(1)
await expect(globalCardList.locator('#card-menu .locked svg')).toBeVisible()
await payload.delete({
collection: lockedDocumentCollection,
id: lockedGlobal.id,
})
await expect(globalCardList.locator('#card-menu .locked')).toBeHidden()
})
test('should show no lock on document card in dashboard view if unlocked', async () => {
test('should not show lock on document card in dashboard view if locked by current user', async () => {
await page.goto(postsUrl.edit('menu'))
await page.waitForURL(postsUrl.edit('menu'))
const textInput = page.locator('#field-text')
await textInput.fill('this is a global menu text field')
await page.reload()
await page.goto(postsUrl.admin)
await page.waitForURL(postsUrl.admin)