diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-item/file-tree-item-menu.js b/services/web/frontend/js/features/file-tree/components/file-tree-item/file-tree-item-menu.js
index a1f6d45c6b..33dab9a510 100644
--- a/services/web/frontend/js/features/file-tree/components/file-tree-item/file-tree-item-menu.js
+++ b/services/web/frontend/js/features/file-tree/components/file-tree-item/file-tree-item-menu.js
@@ -1,9 +1,10 @@
import React, { useState } from 'react'
+import { findDOMNode } from 'react-dom'
import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next'
import withoutPropagation from '../../../../infrastructure/without-propagation'
-import { Dropdown } from 'react-bootstrap'
+import { Dropdown, Overlay } from 'react-bootstrap'
import Icon from '../../../../shared/components/icon'
import FileTreeItemMenuItems from './file-tree-item-menu-items'
@@ -12,6 +13,7 @@ function FileTreeItemMenu({ id }) {
const { t } = useTranslation()
const [dropdownOpen, setDropdownOpen] = useState(false)
+ const [dropdownTarget, setDropdownTarget] = useState()
function handleToggle(wantOpen) {
setDropdownOpen(wantOpen)
@@ -21,6 +23,13 @@ function FileTreeItemMenu({ id }) {
handleToggle(false)
}
+ const toggleRef = component => {
+ if (component) {
+ // eslint-disable-next-line react/no-find-dom-node
+ setDropdownTarget(findDOMNode(component))
+ }
+ }
+
return (
-
-
-
+
+
+
)
}
@@ -47,4 +62,20 @@ FileTreeItemMenu.propTypes = {
id: PropTypes.string.isRequired,
}
+function Menu({ dropdownId, style, className }) {
+ return (
+
+ )
+}
+
+Menu.propTypes = {
+ dropdownId: PropTypes.string.isRequired,
+ style: PropTypes.object,
+ className: PropTypes.string,
+}
+
export default FileTreeItemMenu
diff --git a/services/web/test/frontend/features/file-tree/components/file-tree-doc.test.js b/services/web/test/frontend/features/file-tree/components/file-tree-doc.test.js
index 8909ac88be..80b8efa51f 100644
--- a/services/web/test/frontend/features/file-tree/components/file-tree-doc.test.js
+++ b/services/web/test/frontend/features/file-tree/components/file-tree-doc.test.js
@@ -35,11 +35,6 @@ describe('', function () {
fireEvent.click(treeitem)
screen.getByRole('treeitem', { selected: true })
- screen.getByRole('menuitem', { name: 'Rename' })
- screen.getByRole('menuitem', { name: 'Delete' })
- screen.getByRole('menuitem', { name: 'New File' })
- screen.getByRole('menuitem', { name: 'New Folder' })
- screen.getByRole('menuitem', { name: 'Upload' })
})
it('renders as linked file', function () {
diff --git a/services/web/test/frontend/features/file-tree/components/file-tree-folder.test.js b/services/web/test/frontend/features/file-tree/components/file-tree-folder.test.js
index 416bb4e921..c9a94fbef3 100644
--- a/services/web/test/frontend/features/file-tree/components/file-tree-folder.test.js
+++ b/services/web/test/frontend/features/file-tree/components/file-tree-folder.test.js
@@ -50,13 +50,6 @@ describe('', function () {
const treeitem = screen.getByRole('treeitem', { selected: false })
fireEvent.click(treeitem)
- screen.getByRole('treeitem', { selected: true })
- screen.getByRole('menuitem', { name: 'Rename' })
- screen.getByRole('menuitem', { name: 'Delete' })
- screen.getByRole('menuitem', { name: 'New File' })
- screen.getByRole('menuitem', { name: 'New Folder' })
- screen.getByRole('menuitem', { name: 'Upload' })
-
screen.getByRole('treeitem', { selected: true })
expect(screen.queryByRole('tree')).to.not.exist
})
diff --git a/services/web/test/frontend/features/file-tree/components/file-tree-item/file-tree-item-inner.test.js b/services/web/test/frontend/features/file-tree/components/file-tree-item/file-tree-item-inner.test.js
index d4aea62ad1..289cba4850 100644
--- a/services/web/test/frontend/features/file-tree/components/file-tree-item/file-tree-item-inner.test.js
+++ b/services/web/test/frontend/features/file-tree/components/file-tree-item/file-tree-item-inner.test.js
@@ -5,6 +5,7 @@ import { screen, fireEvent } from '@testing-library/react'
import renderWithContext from '../../helpers/render-with-context'
import FileTreeitemInner from '../../../../../../frontend/js/features/file-tree/components/file-tree-item/file-tree-item-inner'
+import FileTreeContextMenu from '../../../../../../frontend/js/features/file-tree/components/file-tree-context-menu'
describe('', function () {
const setContextMenuCoords = sinon.stub()
@@ -41,18 +42,22 @@ describe('', function () {
it('open / close', function () {
const { container } = renderWithContext(
-
+ <>
+
+
+ >
)
+ expect(screen.queryByRole('menu')).to.be.null
+
+ // open the context menu
const entityElement = container.querySelector('div.entity')
-
- screen.getByRole('menu', { visible: false })
-
fireEvent.contextMenu(entityElement)
screen.getByRole('menu', { visible: true })
- fireEvent.contextMenu(entityElement)
- screen.getByRole('menu', { visible: false })
+ // close the context menu
+ fireEvent.click(entityElement)
+ expect(screen.queryByRole('menu')).to.be.null
})
})
@@ -83,7 +88,8 @@ describe('', function () {
},
}
)
-
+ const toggleButton = screen.getByRole('button', { name: 'Menu' })
+ fireEvent.click(toggleButton)
const renameButton = screen.getByRole('menuitem', { name: 'Rename' })
fireEvent.click(renameButton)
expect(screen.queryByRole('button', { name: 'bar.tex' })).to.not.exist
diff --git a/services/web/test/frontend/features/file-tree/components/file-tree-item/file-tree-item-menu.test.js b/services/web/test/frontend/features/file-tree/components/file-tree-item/file-tree-item-menu.test.js
index 76caaa9be9..d92918d630 100644
--- a/services/web/test/frontend/features/file-tree/components/file-tree-item/file-tree-item-menu.test.js
+++ b/services/web/test/frontend/features/file-tree/components/file-tree-item/file-tree-item-menu.test.js
@@ -1,5 +1,6 @@
import React from 'react'
import sinon from 'sinon'
+import { expect } from 'chai'
import { screen, fireEvent } from '@testing-library/react'
import renderWithContext from '../../helpers/render-with-context'
@@ -20,7 +21,9 @@ describe('', function () {
/>
)
- screen.getByRole('button', { name: 'Menu' })
+ const toggleButton = screen.getByRole('button', { name: 'Menu' })
+ fireEvent.click(toggleButton)
+
screen.getByRole('menu')
})
@@ -32,9 +35,9 @@ describe('', function () {
/>
)
- const toggleButton = screen.getByRole('button', { name: 'Menu' })
+ expect(screen.queryByRole('menu')).to.be.null
- screen.getByRole('menu', { visible: false })
+ const toggleButton = screen.getByRole('button', { name: 'Menu' })
fireEvent.click(toggleButton)
screen.getByRole('menu', { visible: true })
diff --git a/services/web/test/frontend/features/file-tree/components/file-tree-root.test.js b/services/web/test/frontend/features/file-tree/components/file-tree-root.test.js
index 727154e290..70dda86a79 100644
--- a/services/web/test/frontend/features/file-tree/components/file-tree-root.test.js
+++ b/services/web/test/frontend/features/file-tree/components/file-tree-root.test.js
@@ -89,6 +89,8 @@ describe('', function () {
// selected) This is needed to make sure the test fail.
const treeitemFile = screen.getByRole('treeitem', { name: 'main.tex' })
fireEvent.click(treeitemFile, { ctrlKey: true })
+ const toggleButton = screen.getByRole('button', { name: 'Menu' })
+ fireEvent.click(toggleButton)
const deleteButton = screen.getByRole('menuitem', { name: 'Delete' })
fireEvent.click(deleteButton)
await waitFor(() => screen.getByRole('button', { name: 'Cancel' }))
diff --git a/services/web/test/frontend/features/file-tree/flows/context-menu.test.js b/services/web/test/frontend/features/file-tree/flows/context-menu.test.js
index 16d151b567..0b8dddaf5e 100644
--- a/services/web/test/frontend/features/file-tree/flows/context-menu.test.js
+++ b/services/web/test/frontend/features/file-tree/flows/context-menu.test.js
@@ -36,11 +36,11 @@ describe('FileTree Context Menu Flow', function () {
)
const treeitem = screen.getByRole('button', { name: 'main.tex' })
- expect(screen.getAllByRole('menu').length).to.equal(1) // toolbar
+ expect(screen.queryByRole('menu')).to.be.null
fireEvent.contextMenu(treeitem)
- expect(screen.getAllByRole('menu').length).to.equal(2) // toolbar + menu
+ screen.getByRole('menu')
})
it("doesn't open in read only mode", async function () {
diff --git a/services/web/test/frontend/features/file-tree/flows/delete-entity.test.js b/services/web/test/frontend/features/file-tree/flows/delete-entity.test.js
index c7d5218a90..7e3ec37cda 100644
--- a/services/web/test/frontend/features/file-tree/flows/delete-entity.test.js
+++ b/services/web/test/frontend/features/file-tree/flows/delete-entity.test.js
@@ -53,6 +53,9 @@ describe('FileTree Delete Entity Flow', function () {
const treeitem = screen.getByRole('treeitem', { name: 'main.tex' })
fireEvent.click(treeitem)
+ const toggleButton = screen.getByRole('button', { name: 'Menu' })
+ fireEvent.click(toggleButton)
+
const deleteButton = screen.getByRole('menuitem', { name: 'Delete' })
fireEvent.click(deleteButton)
})
@@ -186,6 +189,8 @@ describe('FileTree Delete Entity Flow', 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 toggleButton = screen.getByRole('button', { name: 'Menu' })
+ fireEvent.click(toggleButton)
const deleteButton = screen.getByRole('menuitem', { name: 'Delete' })
fireEvent.click(deleteButton)
await waitFor(() => screen.getByRole('button', { name: 'Cancel' }))
@@ -193,7 +198,7 @@ describe('FileTree Delete Entity Flow', function () {
})
describe('multiple entities', function () {
- beforeEach(function () {
+ beforeEach(async function () {
const rootFolder = [
{
_id: 'root-folder-id',
@@ -202,6 +207,7 @@ describe('FileTree Delete Entity Flow', function () {
fileRefs: [{ _id: '789ghi', name: 'my.bib' }],
},
]
+
render(
)
+ // select two files
const treeitemDoc = screen.getByRole('treeitem', { name: 'main.tex' })
fireEvent.click(treeitemDoc)
const treeitemFile = screen.getByRole('treeitem', { name: 'my.bib' })
fireEvent.click(treeitemFile, { ctrlKey: true })
- const deleteButton = screen.getAllByRole('menuitem', {
- name: 'Delete',
- })[0]
+ // open the context menu
+ const treeitemButton = screen.getByRole('button', { name: 'my.bib' })
+ fireEvent.contextMenu(treeitemButton)
+
+ // make sure the menu has opened, with only a "Delete" item (as multiple files are selected)
+ screen.getByRole('menu')
+ const menuItems = await screen.findAllByRole('menuitem')
+ expect(menuItems.length).to.equal(1)
+
+ // select the Delete menu item
+ const deleteButton = screen.getByRole('menuitem', { name: 'Delete' })
fireEvent.click(deleteButton)
})
diff --git a/services/web/test/frontend/features/file-tree/flows/rename-entity.test.js b/services/web/test/frontend/features/file-tree/flows/rename-entity.test.js
index f83999c345..ba55d549b1 100644
--- a/services/web/test/frontend/features/file-tree/flows/rename-entity.test.js
+++ b/services/web/test/frontend/features/file-tree/flows/rename-entity.test.js
@@ -185,6 +185,9 @@ describe('FileTree Rename Entity Flow', function () {
const treeitem = screen.getByRole('treeitem', { name: treeitemName })
fireEvent.click(treeitem)
+ const toggleButton = screen.getByRole('button', { name: 'Menu' })
+ fireEvent.click(toggleButton)
+
const renameButton = screen.getByRole('menuitem', { name: 'Rename' })
fireEvent.click(renameButton)