Răsfoiți Sursa

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

Use context menu for all file tree dropdowns

GitOrigin-RevId: 8283093b428b4cb53bc1ed7795e398ffe4bd3496
Hugh O'Brien 4 ani în urmă
părinte
comite
a5323293e0

+ 1 - 0
services/web/frontend/js/features/file-tree/components/file-tree-item/file-tree-item-inner.js

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

+ 22 - 48
services/web/frontend/js/features/file-tree/components/file-tree-item/file-tree-item-menu.js

@@ -4,26 +4,30 @@ import PropTypes from 'prop-types'
 import { useTranslation } from 'react-i18next'
 import { useTranslation } from 'react-i18next'
 import withoutPropagation from '../../../../infrastructure/without-propagation'
 import withoutPropagation from '../../../../infrastructure/without-propagation'
 
 
-import { Dropdown, Overlay } from 'react-bootstrap'
+import { Button } from 'react-bootstrap'
 import Icon from '../../../../shared/components/icon'
 import Icon from '../../../../shared/components/icon'
 
 
-import FileTreeItemMenuItems from './file-tree-item-menu-items'
+import { useFileTreeMainContext } from '../../contexts/file-tree-main'
 
 
 function FileTreeItemMenu({ id }) {
 function FileTreeItemMenu({ id }) {
   const { t } = useTranslation()
   const { t } = useTranslation()
 
 
-  const [dropdownOpen, setDropdownOpen] = useState(false)
+  const { contextMenuCoords, setContextMenuCoords } = useFileTreeMainContext()
   const [dropdownTarget, setDropdownTarget] = useState()
   const [dropdownTarget, setDropdownTarget] = useState()
 
 
-  function handleToggle(wantOpen) {
-    setDropdownOpen(wantOpen)
-  }
-
-  function handleClick() {
-    handleToggle(false)
+  function handleClick(_ev) {
+    const target = dropdownTarget.getBoundingClientRect()
+    if (!contextMenuCoords) {
+      setContextMenuCoords({
+        top: target.top + target.height / 2,
+        left: target.right,
+      })
+    } else {
+      setContextMenuCoords(null)
+    }
   }
   }
 
 
-  const toggleRef = component => {
+  const menuButtonRef = component => {
     if (component) {
     if (component) {
       // eslint-disable-next-line react/no-find-dom-node
       // eslint-disable-next-line react/no-find-dom-node
       setDropdownTarget(findDOMNode(component))
       setDropdownTarget(findDOMNode(component))
@@ -31,30 +35,16 @@ function FileTreeItemMenu({ id }) {
   }
   }
 
 
   return (
   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')} />
         <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,
   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
 export default FileTreeItemMenu

+ 5 - 2
services/web/frontend/stylesheets/app/editor/file-tree.less

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

+ 4 - 1
services/web/test/frontend/features/file-tree/components/file-tree-item/file-tree-item-inner.test.js

@@ -72,7 +72,10 @@ 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 />
+          <FileTreeContextMenu />
+        </>,
         {
         {
           contextProps: {
           contextProps: {
             rootDocId: '123abc',
             rootDocId: '123abc',

+ 0 - 47
services/web/test/frontend/features/file-tree/components/file-tree-item/file-tree-item-menu.test.js

@@ -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 })
-  })
-})