diff --git a/src/components/structures/ContextMenu.tsx b/src/components/structures/ContextMenu.tsx index 9a13d624248..b62c4b6d7be 100644 --- a/src/components/structures/ContextMenu.tsx +++ b/src/components/structures/ContextMenu.tsx @@ -565,8 +565,13 @@ type ContextMenuTuple = [ (val: boolean) => void, ]; // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint -export const useContextMenu = (): ContextMenuTuple => { - const button = useRef(null); +export const useContextMenu = (inputRef?: RefObject): ContextMenuTuple => { + let button = useRef(null); + if (inputRef) { + // if we are given a ref, use it instead of ours + button = inputRef; + } + const [isOpen, setIsOpen] = useState(false); const open = (ev?: SyntheticEvent) => { ev?.preventDefault(); @@ -579,7 +584,7 @@ export const useContextMenu = (): ContextMenuTuple< setIsOpen(false); }; - return [isOpen, button, open, close, setIsOpen]; + return [button.current ? isOpen : false, button, open, close, setIsOpen]; }; // XXX: Deprecated, used only for dynamic Tooltips. Avoid using at all costs. diff --git a/src/components/views/location/LocationButton.tsx b/src/components/views/location/LocationButton.tsx index 7efd350b6b9..6d9885cf915 100644 --- a/src/components/views/location/LocationButton.tsx +++ b/src/components/views/location/LocationButton.tsx @@ -69,6 +69,7 @@ export const LocationButton: React.FC = ({ roomId, sender, menuPosition, iconClassName="mx_MessageComposer_location" onClick={openMenu} title={_t("Location")} + inputRef={button} /> { contextMenu } diff --git a/src/components/views/rooms/ReadReceiptGroup.tsx b/src/components/views/rooms/ReadReceiptGroup.tsx index 34bcb431e29..4e4b4c8a9dd 100644 --- a/src/components/views/rooms/ReadReceiptGroup.tsx +++ b/src/components/views/rooms/ReadReceiptGroup.tsx @@ -209,7 +209,8 @@ export function ReadReceiptGroup( onMouseOver={showTooltip} onMouseLeave={hideTooltip} onFocus={showTooltip} - onBlur={hideTooltip}> + onBlur={hideTooltip} + > { remText } ) => { - // We don't need the handle as we position the menu in a constant location - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [menuDisplayed, _handle, openMenu, closeMenu] = useContextMenu(); + const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu(); useEffect(() => { if (!isPanelCollapsed && menuDisplayed) { @@ -231,13 +229,14 @@ const CreateSpaceButton = ({ role="treeitem" > { contextMenu } diff --git a/src/components/views/spaces/SpaceTreeLevel.tsx b/src/components/views/spaces/SpaceTreeLevel.tsx index cab7bc3c76b..5952e877d9f 100644 --- a/src/components/views/spaces/SpaceTreeLevel.tsx +++ b/src/components/views/spaces/SpaceTreeLevel.tsx @@ -14,7 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { MouseEvent, ComponentProps, ComponentType, createRef, InputHTMLAttributes, LegacyRef } from "react"; +import React, { + MouseEvent, + ComponentProps, + ComponentType, + createRef, + InputHTMLAttributes, + LegacyRef, + forwardRef, + RefObject, +} from "react"; import classNames from "classnames"; import { Room, RoomEvent } from "matrix-js-sdk/src/models/room"; import { DraggableProvidedDragHandleProps } from "react-beautiful-dnd"; @@ -54,7 +63,7 @@ interface IButtonProps extends Omit = ({ +export const SpaceButton = forwardRef(({ space, spaceKey, className, @@ -67,9 +76,9 @@ export const SpaceButton: React.FC = ({ children, ContextMenuComponent, ...props -}) => { - const [menuDisplayed, ref, openMenu, closeMenu] = useContextMenu(); - const [onFocus, isActive, handle] = useRovingTabIndex(ref); +}, ref: RefObject) => { + const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu(ref); + const [onFocus, isActive] = useRovingTabIndex(handle); const tabIndex = isActive ? 0 : -1; let avatar =
; @@ -150,7 +159,7 @@ export const SpaceButton: React.FC = ({
); -}; +}); interface IItemProps extends InputHTMLAttributes { space: Room; diff --git a/test/components/views/spaces/SpacePanel-test.tsx b/test/components/views/spaces/SpacePanel-test.tsx index 25ce4c3b6a0..7e922b13991 100644 --- a/test/components/views/spaces/SpacePanel-test.tsx +++ b/test/components/views/spaces/SpacePanel-test.tsx @@ -15,16 +15,13 @@ limitations under the License. */ import React from 'react'; -// eslint-disable-next-line deprecate/import -import { mount } from 'enzyme'; +import { render, screen, fireEvent } from "@testing-library/react"; import { mocked } from 'jest-mock'; import { MatrixClient } from 'matrix-js-sdk/src/matrix'; -import { act } from "react-dom/test-utils"; import SpacePanel from '../../../../src/components/views/spaces/SpacePanel'; import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; import { SpaceKey } from '../../../../src/stores/spaces'; -import { findByTestId } from '../../../test-utils'; import { shouldShowComponent } from '../../../../src/customisations/helpers/UIComponents'; import { UIComponent } from '../../../../src/settings/UIFeature'; @@ -47,10 +44,6 @@ jest.mock('../../../../src/customisations/helpers/UIComponents', () => ({ })); describe('', () => { - const defaultProps = {}; - const getComponent = (props = {}) => - mount(); - const mockClient = { getUserId: jest.fn().mockReturnValue('@test:test'), isGuest: jest.fn(), @@ -67,26 +60,21 @@ describe('', () => { describe('create new space button', () => { it('renders create space button when UIComponent.CreateSpaces component should be shown', () => { - const component = getComponent(); - expect(findByTestId(component, 'create-space-button').length).toBeTruthy(); + render(); + screen.getByTestId("create-space-button"); }); it('does not render create space button when UIComponent.CreateSpaces component should not be shown', () => { mocked(shouldShowComponent).mockReturnValue(false); - const component = getComponent(); + render(); expect(shouldShowComponent).toHaveBeenCalledWith(UIComponent.CreateSpaces); - expect(findByTestId(component, 'create-space-button').length).toBeFalsy(); + expect(screen.queryByTestId("create-space-button")).toBeFalsy(); }); - it('opens context menu on create space button click', async () => { - const component = getComponent(); - - await act(async () => { - findByTestId(component, 'create-space-button').at(0).simulate('click'); - component.setProps({}); - }); - - expect(component.find('SpaceCreateMenu').length).toBeTruthy(); + it('opens context menu on create space button click', () => { + render(); + fireEvent.click(screen.getByTestId("create-space-button")); + screen.getByTestId("create-space-button"); }); }); });