mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-07 16:02:18 +00:00
Merge pull request #3535 from overleaf/spd-ta-bugfix-crash-on-selected-child
[ReactFileTree] Unselect Children when Deleting Folder GitOrigin-RevId: 410183f5af596ec70f9c739503944966c68677fc
This commit is contained in:
parent
c78a2f1809
commit
759fbe8587
2 changed files with 85 additions and 3 deletions
|
@ -2,6 +2,7 @@ import { useCallback, useEffect } from 'react'
|
|||
|
||||
import { useFileTreeMutable } from '../contexts/file-tree-mutable'
|
||||
import { useFileTreeSelectable } from '../contexts/file-tree-selectable'
|
||||
import { findInTreeOrThrow } from '../util/find-in-tree'
|
||||
|
||||
export function useFileTreeSocketListener() {
|
||||
const {
|
||||
|
@ -10,9 +11,15 @@ export function useFileTreeSocketListener() {
|
|||
dispatchMove,
|
||||
dispatchCreateFolder,
|
||||
dispatchCreateDoc,
|
||||
dispatchCreateFile
|
||||
dispatchCreateFile,
|
||||
fileTreeData
|
||||
} = useFileTreeMutable()
|
||||
const { select, unselect } = useFileTreeSelectable()
|
||||
const {
|
||||
selectedEntityIds,
|
||||
selectedEntityParentIds,
|
||||
select,
|
||||
unselect
|
||||
} = useFileTreeSelectable()
|
||||
const socket = window._ide && window._ide.socket
|
||||
|
||||
const selectEntityIfCreatedByUser = useCallback(
|
||||
|
@ -38,13 +45,33 @@ export function useFileTreeSocketListener() {
|
|||
useEffect(() => {
|
||||
function handleDispatchDelete(entityId) {
|
||||
unselect(entityId)
|
||||
if (selectedEntityParentIds.has(entityId)) {
|
||||
// we're deleting a folder with a selected children so we need to
|
||||
// unselect its selected children first
|
||||
for (const selectedEntityId of selectedEntityIds) {
|
||||
if (
|
||||
findInTreeOrThrow(fileTreeData, selectedEntityId).path.includes(
|
||||
entityId
|
||||
)
|
||||
) {
|
||||
unselect(selectedEntityId)
|
||||
}
|
||||
}
|
||||
}
|
||||
dispatchDelete(entityId)
|
||||
}
|
||||
if (socket) socket.on('removeEntity', handleDispatchDelete)
|
||||
return () => {
|
||||
if (socket) socket.removeListener('removeEntity', handleDispatchDelete)
|
||||
}
|
||||
}, [socket, unselect, dispatchDelete])
|
||||
}, [
|
||||
socket,
|
||||
unselect,
|
||||
dispatchDelete,
|
||||
fileTreeData,
|
||||
selectedEntityIds,
|
||||
selectedEntityParentIds
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
function handleDispatchMove(entityId, toFolderId) {
|
||||
|
|
|
@ -115,6 +115,61 @@ describe('FileTree Delete Entity Flow', function() {
|
|||
})
|
||||
})
|
||||
|
||||
describe('folders', function() {
|
||||
beforeEach(function() {
|
||||
const rootFolder = [
|
||||
{
|
||||
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||
folders: [
|
||||
{
|
||||
_id: '123abc',
|
||||
name: 'folder',
|
||||
docs: [],
|
||||
folders: [],
|
||||
fileRefs: [{ _id: '789ghi', name: 'my.bib' }]
|
||||
}
|
||||
],
|
||||
fileRefs: []
|
||||
}
|
||||
]
|
||||
render(
|
||||
<FileTreeRoot
|
||||
rootFolder={rootFolder}
|
||||
projectId="123abc"
|
||||
hasWritePermissions
|
||||
onSelect={onSelect}
|
||||
onInit={onInit}
|
||||
/>
|
||||
)
|
||||
|
||||
const expandButton = screen.queryByRole('button', { name: 'Expand' })
|
||||
if (expandButton) fireEvent.click(expandButton)
|
||||
const treeitemDoc = screen.getByRole('treeitem', { name: 'main.tex' })
|
||||
fireEvent.click(treeitemDoc)
|
||||
const treeitemFile = screen.getByRole('treeitem', { name: 'my.bib' })
|
||||
fireEvent.click(treeitemFile, { ctrlKey: true })
|
||||
|
||||
window._ide.socket.socketClient.emit('removeEntity', '123abc')
|
||||
})
|
||||
|
||||
it('removes the folder', function() {
|
||||
expect(screen.queryByRole('treeitem', { name: 'folder' })).to.not.exist
|
||||
})
|
||||
|
||||
it('leaves the main file selected', function() {
|
||||
screen.getByRole('treeitem', { name: 'main.tex', selected: true })
|
||||
})
|
||||
|
||||
it('unselect the child entity', async function() {
|
||||
// as a proxy to check that the child entity has been unselect we start
|
||||
// a delete and ensure the modal is displayed (the cancel button can be
|
||||
// selected) This is needed to make sure the test fail.
|
||||
const deleteButton = screen.getByRole('menuitem', { name: 'Delete' })
|
||||
fireEvent.click(deleteButton)
|
||||
await waitFor(() => screen.getByRole('button', { name: 'Cancel' }))
|
||||
})
|
||||
})
|
||||
|
||||
describe('multiple entities', function() {
|
||||
beforeEach(function() {
|
||||
const rootFolder = [
|
||||
|
|
Loading…
Add table
Reference in a new issue