Merge pull request #5636 from overleaf/hb-single-context-menu-for-file-tree

Use context menu for all file tree dropdowns

GitOrigin-RevId: 8283093b428b4cb53bc1ed7795e398ffe4bd3496
This commit is contained in:
Hugh O'Brien 2021-11-09 11:01:08 +00:00 committed by Copybot
parent 05c1ecdde0
commit a5323293e0
5 changed files with 32 additions and 98 deletions

View file

@ -42,6 +42,7 @@ function FileTreeItemInner({ id, name, isSelected, icons }) {
function handleContextMenu(ev) {
ev.preventDefault()
setContextMenuCoords({
top: ev.pageY,
left: ev.pageX,

View file

@ -4,26 +4,30 @@ import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next'
import withoutPropagation from '../../../../infrastructure/without-propagation'
import { Dropdown, Overlay } from 'react-bootstrap'
import { Button } from 'react-bootstrap'
import Icon from '../../../../shared/components/icon'
import FileTreeItemMenuItems from './file-tree-item-menu-items'
import { useFileTreeMainContext } from '../../contexts/file-tree-main'
function FileTreeItemMenu({ id }) {
const { t } = useTranslation()
const [dropdownOpen, setDropdownOpen] = useState(false)
const { contextMenuCoords, setContextMenuCoords } = useFileTreeMainContext()
const [dropdownTarget, setDropdownTarget] = useState()
function handleToggle(wantOpen) {
setDropdownOpen(wantOpen)
function handleClick(_ev) {
const target = dropdownTarget.getBoundingClientRect()
if (!contextMenuCoords) {
setContextMenuCoords({
top: target.top + target.height / 2,
left: target.right,
})
} else {
setContextMenuCoords(null)
}
}
function handleClick() {
handleToggle(false)
}
const toggleRef = component => {
const menuButtonRef = component => {
if (component) {
// eslint-disable-next-line react/no-find-dom-node
setDropdownTarget(findDOMNode(component))
@ -31,30 +35,16 @@ function FileTreeItemMenu({ id }) {
}
return (
<Dropdown
onClick={withoutPropagation(handleClick)}
pullRight
open={dropdownOpen}
id={`dropdown-${id}`}
onToggle={handleToggle}
>
<Dropdown.Toggle
noCaret
className="dropdown-toggle-no-background entity-menu-toggle"
onClick={withoutPropagation()}
ref={toggleRef}
<div className="menu-button btn-group">
<Button
className="entity-menu-toggle btn btn-default"
id={`menu-button-${id}`}
onClick={withoutPropagation(handleClick)}
ref={menuButtonRef}
>
<Icon type="ellipsis-v" accessibilityLabel={t('menu')} />
</Dropdown.Toggle>
<Overlay
bsRole="menu"
show={dropdownOpen}
target={dropdownTarget}
container={document.body}
>
<Menu dropdownId={`dropdown-${id}`} />
</Overlay>
</Dropdown>
</Button>
</div>
)
}
@ -62,20 +52,4 @@ FileTreeItemMenu.propTypes = {
id: PropTypes.string.isRequired,
}
function Menu({ dropdownId, style, className }) {
return (
<div className={`dropdown open ${className}`} style={style}>
<ul className="dropdown-menu" role="menu" aria-labelledby={dropdownId}>
<FileTreeItemMenuItems />
</ul>
</div>
)
}
Menu.propTypes = {
dropdownId: PropTypes.string.isRequired,
style: PropTypes.object,
className: PropTypes.string,
}
export default FileTreeItemMenu

View file

@ -155,7 +155,7 @@
input {
line-height: 1.6;
}
.dropdown-toggle > i {
.entity-menu-toggle > i {
color: white;
font-size: 18px;
}
@ -242,7 +242,7 @@
}
}
.dropdown {
.menu-button {
position: absolute;
right: 0;
}
@ -306,6 +306,9 @@
.entity-menu-toggle {
display: inline-block;
background-color: transparent;
box-shadow: none;
border: 0;
}
}
}

View file

@ -72,7 +72,10 @@ describe('<FileTreeitemInner />', function () {
it('starts rename on menu item click', function () {
renderWithContext(
<FileTreeitemInner id="123abc" name="bar.tex" isSelected />,
<>
<FileTreeitemInner id="123abc" name="bar.tex" isSelected />
<FileTreeContextMenu />
</>,
{
contextProps: {
rootDocId: '123abc',

View file

@ -1,47 +0,0 @@
import sinon from 'sinon'
import { expect } from 'chai'
import { screen, fireEvent } from '@testing-library/react'
import renderWithContext from '../../helpers/render-with-context'
import FileTreeitemMenu from '../../../../../../frontend/js/features/file-tree/components/file-tree-item/file-tree-item-menu'
describe('<FileTreeitemMenu />', function () {
const setContextMenuCoords = sinon.stub()
afterEach(function () {
setContextMenuCoords.reset()
})
it('renders dropdown', function () {
renderWithContext(
<FileTreeitemMenu
id="123abc"
setContextMenuCoords={setContextMenuCoords}
/>
)
const toggleButton = screen.getByRole('button', { name: 'Menu' })
fireEvent.click(toggleButton)
screen.getByRole('menu')
})
it('open / close', function () {
renderWithContext(
<FileTreeitemMenu
id="123abc"
setContextMenuCoords={setContextMenuCoords}
/>
)
expect(screen.queryByRole('menu')).to.be.null
const toggleButton = screen.getByRole('button', { name: 'Menu' })
fireEvent.click(toggleButton)
screen.getByRole('menu', { visible: true })
fireEvent.click(toggleButton)
screen.getByRole('menu', { visible: false })
})
})