mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #3548 from overleaf/ta-file-tree-init-fix
[ReactFileTree] Fix Initial State When Selected File Doesn't Exist GitOrigin-RevId: 92ee8573203e66abf26c9b3afab2fccd90ec8c2e
This commit is contained in:
parent
759fbe8587
commit
d121b81896
5 changed files with 78 additions and 8 deletions
|
@ -82,13 +82,26 @@ export function FileTreeSelectableProvider({
|
||||||
rootDocId
|
rootDocId
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const { fileTreeData } = useFileTreeMutable()
|
||||||
|
|
||||||
const [selectedEntityIds, dispatch] = useReducer(
|
const [selectedEntityIds, dispatch] = useReducer(
|
||||||
hasWritePermissions
|
hasWritePermissions
|
||||||
? fileTreeSelectableReadWriteReducer
|
? fileTreeSelectableReadWriteReducer
|
||||||
: fileTreeSelectableReadOnlyReducer,
|
: fileTreeSelectableReadOnlyReducer,
|
||||||
initialSelectedEntityId ? new Set([initialSelectedEntityId]) : new Set()
|
null,
|
||||||
|
() => {
|
||||||
|
if (!initialSelectedEntityId) return new Set()
|
||||||
|
|
||||||
|
// the entity with id=initialSelectedEntityId might not exist in the tree
|
||||||
|
// anymore. This checks that it exists before initialising the reducer
|
||||||
|
// with the id.
|
||||||
|
if (findInTree(fileTreeData, initialSelectedEntityId))
|
||||||
|
return new Set([initialSelectedEntityId])
|
||||||
|
|
||||||
|
// the entity doesn't exist anymore; don't select any files
|
||||||
|
return new Set()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
const { fileTreeData } = useFileTreeMutable()
|
|
||||||
|
|
||||||
const [selectedEntityParentIds, setSelectedEntityParentIds] = useState(
|
const [selectedEntityParentIds, setSelectedEntityParentIds] = useState(
|
||||||
new Set()
|
new Set()
|
||||||
|
|
|
@ -65,15 +65,15 @@ describe('<FileTreeFolderList/>', function() {
|
||||||
{ _id: '3', name: '3.tex' }
|
{ _id: '3', name: '3.tex' }
|
||||||
]
|
]
|
||||||
renderWithContext(
|
renderWithContext(
|
||||||
<FileTreeFolderList folders={[]} docs={docs} files={[]} />,
|
<FileTreeFolderList folders={[]} docs={docs} files={[]} />
|
||||||
{ contextProps: { rootDocId: '1' } }
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const treeitem1 = screen.getByRole('treeitem', { name: '1.tex' })
|
const treeitem1 = screen.getByRole('treeitem', { name: '1.tex' })
|
||||||
const treeitem2 = screen.getByRole('treeitem', { name: '2.tex' })
|
const treeitem2 = screen.getByRole('treeitem', { name: '2.tex' })
|
||||||
const treeitem3 = screen.getByRole('treeitem', { name: '3.tex' })
|
const treeitem3 = screen.getByRole('treeitem', { name: '3.tex' })
|
||||||
|
|
||||||
// item 1 i selected by default
|
// click item 1: it gets selected
|
||||||
|
fireEvent.click(treeitem1)
|
||||||
screen.getByRole('treeitem', { name: '1.tex', selected: true })
|
screen.getByRole('treeitem', { name: '1.tex', selected: true })
|
||||||
screen.getByRole('treeitem', { name: '2.tex', selected: false })
|
screen.getByRole('treeitem', { name: '2.tex', selected: false })
|
||||||
screen.getByRole('treeitem', { name: '3.tex', selected: false })
|
screen.getByRole('treeitem', { name: '3.tex', selected: false })
|
||||||
|
|
|
@ -69,7 +69,19 @@ describe('<FileTreeitemInner />', function() {
|
||||||
it('starts rename on menu item click', function() {
|
it('starts rename on menu item click', function() {
|
||||||
renderWithContext(
|
renderWithContext(
|
||||||
<FileTreeitemInner id="123abc" name="bar.tex" isSelected />,
|
<FileTreeitemInner id="123abc" name="bar.tex" isSelected />,
|
||||||
{ contextProps: { rootDocId: '123abc' } }
|
{
|
||||||
|
contextProps: {
|
||||||
|
rootDocId: '123abc',
|
||||||
|
rootFolder: [
|
||||||
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
docs: [{ _id: '123abc', name: 'bar.tex' }],
|
||||||
|
folders: [],
|
||||||
|
fileRefs: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const renameButton = screen.getByRole('menuitem', { name: 'Rename' })
|
const renameButton = screen.getByRole('menuitem', { name: 'Rename' })
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import { screen, render, fireEvent } from '@testing-library/react'
|
import { screen, render, fireEvent, waitFor } from '@testing-library/react'
|
||||||
import fetchMock from 'fetch-mock'
|
import fetchMock from 'fetch-mock'
|
||||||
|
|
||||||
import FileTreeRoot from '../../../../../frontend/js/features/file-tree/components/file-tree-root'
|
import FileTreeRoot from '../../../../../frontend/js/features/file-tree/components/file-tree-root'
|
||||||
|
@ -18,6 +18,7 @@ describe('<FileTreeRoot/>', function() {
|
||||||
fetchMock.restore()
|
fetchMock.restore()
|
||||||
onSelect.reset()
|
onSelect.reset()
|
||||||
onInit.reset()
|
onInit.reset()
|
||||||
|
global.localStorage.clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders', function() {
|
it('renders', function() {
|
||||||
|
@ -45,6 +46,40 @@ describe('<FileTreeRoot/>', function() {
|
||||||
screen.getByRole('treeitem', { name: 'main.tex', selected: true })
|
screen.getByRole('treeitem', { name: 'main.tex', selected: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('renders with invalid selected doc in local storage', async function() {
|
||||||
|
global.localStorage.setItem(
|
||||||
|
'doc.open_id.123abc',
|
||||||
|
JSON.stringify('not-a-valid-id')
|
||||||
|
)
|
||||||
|
const rootFolder = [
|
||||||
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
|
folders: [],
|
||||||
|
fileRefs: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
render(
|
||||||
|
<FileTreeRoot
|
||||||
|
rootFolder={rootFolder}
|
||||||
|
projectId="123abc"
|
||||||
|
hasWritePermissions
|
||||||
|
rootDocId="456def"
|
||||||
|
onSelect={onSelect}
|
||||||
|
onInit={onInit}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
// as a proxy to check that the invalid entity ha not been select 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 treeitemFile = screen.getByRole('treeitem', { name: 'main.tex' })
|
||||||
|
fireEvent.click(treeitemFile, { ctrlKey: true })
|
||||||
|
const deleteButton = screen.getByRole('menuitem', { name: 'Delete' })
|
||||||
|
fireEvent.click(deleteButton)
|
||||||
|
await waitFor(() => screen.getByRole('button', { name: 'Cancel' }))
|
||||||
|
})
|
||||||
|
|
||||||
it('fire onSelect', function() {
|
it('fire onSelect', function() {
|
||||||
const rootFolder = [
|
const rootFolder = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,7 +30,17 @@ describe('<FileTreeToolbar/>', function() {
|
||||||
|
|
||||||
it('with one selected file', function() {
|
it('with one selected file', function() {
|
||||||
renderWithContext(<FileTreeToolbar />, {
|
renderWithContext(<FileTreeToolbar />, {
|
||||||
contextProps: { rootDocId: '123abc' }
|
contextProps: {
|
||||||
|
rootDocId: '456def',
|
||||||
|
rootFolder: [
|
||||||
|
{
|
||||||
|
_id: 'root-folder-id',
|
||||||
|
docs: [{ _id: '456def', name: 'main.tex' }],
|
||||||
|
folders: [],
|
||||||
|
fileRefs: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
screen.getByRole('button', { name: 'New File' })
|
screen.getByRole('button', { name: 'New File' })
|
||||||
|
|
Loading…
Reference in a new issue