Skip to content
Snippets Groups Projects
Commit 949631af authored by Miłosz Grocholewski's avatar Miłosz Grocholewski
Browse files

Merge branch 'bugfix/MIN-112-hiding-context-menu' into 'development'

bugfix(map): close context menu on click outside the menu

Closes MIN-112

See merge request !339
parents eb2236f2 bd8ec059
No related branches found
No related tags found
1 merge request!339bugfix(map): close context menu on click outside the menu
Pipeline #99211 passed
......@@ -14,6 +14,7 @@ import { ClickCoordinates } from '@/services/pluginsManager/pluginContextMenu/pl
import { currentModelSelector } from '@/redux/models/models.selectors';
import { mapDataLastPositionSelector } from '@/redux/map/map.selectors';
import { DEFAULT_ZOOM } from '@/constants/map';
import { OutsideClickWrapper } from '@/shared/OutsideClickWrapper';
export const ContextMenu = (): React.ReactNode => {
const pluginContextMenu = PluginsContextMenu.menuItems;
......@@ -29,15 +30,19 @@ export const ContextMenu = (): React.ReactNode => {
return isUnitProtIdAvailable() ? unitProtId : 'no UnitProt ID available';
};
const closeContextMenuFunction = (): void => {
dispatch(closeContextMenu());
};
const handleOpenMolArtClick = (): void => {
if (isUnitProtIdAvailable()) {
dispatch(closeContextMenu());
closeContextMenuFunction();
dispatch(openMolArtModalById(unitProtId));
}
};
const handleAddCommentClick = (): void => {
dispatch(closeContextMenu());
closeContextMenuFunction();
dispatch(openAddCommentModal());
};
......@@ -47,7 +52,7 @@ export const ContextMenu = (): React.ReactNode => {
callback: (coordinates: ClickCoordinates, element: BioEntity | NewReaction | undefined) => void,
) => {
return () => {
dispatch(closeContextMenu());
closeContextMenuFunction();
return callback(
{
modelId,
......@@ -61,55 +66,57 @@ export const ContextMenu = (): React.ReactNode => {
};
return (
<div
className={twMerge(
'absolute z-10 rounded-lg border border-[#DBD9D9] bg-white p-4',
isOpen ? '' : 'hidden',
)}
style={{
left: `${coordinates[FIRST_ARRAY_ELEMENT]}px`,
top: `${coordinates[SECOND_ARRAY_ELEMENT]}px`,
}}
data-testid="context-modal"
>
<button
<OutsideClickWrapper onOutsideClick={closeContextMenuFunction}>
<div
className={twMerge(
'w-full cursor-pointer text-left text-xs font-normal',
!isUnitProtIdAvailable() ? 'cursor-not-allowed text-greyscale-700' : '',
'absolute z-10 rounded-lg border border-[#DBD9D9] bg-white p-4',
isOpen ? '' : 'hidden',
)}
onClick={handleOpenMolArtClick}
type="button"
data-testid="open-molart"
>
Open MolArt ({getUnitProtId()})
</button>
<hr />
<button
className={twMerge('w-full cursor-pointer text-left text-xs font-normal')}
onClick={handleAddCommentClick}
type="button"
data-testid="add-comment"
style={{
left: `${coordinates[FIRST_ARRAY_ELEMENT]}px`,
top: `${coordinates[SECOND_ARRAY_ELEMENT]}px`,
}}
data-testid="context-modal"
>
Add comment
</button>
{pluginContextMenu.length && <hr />}
{pluginContextMenu.map(contextMenuEntry => (
<button
key={contextMenuEntry.id}
id={contextMenuEntry.id}
className={twMerge(
'cursor-pointer text-xs font-normal',
contextMenuEntry.style,
!contextMenuEntry.enabled ? 'cursor-not-allowed text-greyscale-700' : '',
'w-full cursor-pointer text-left text-xs font-normal',
!isUnitProtIdAvailable() ? 'cursor-not-allowed text-greyscale-700' : '',
)}
onClick={handleCallback(contextMenuEntry.callback)}
onClick={handleOpenMolArtClick}
type="button"
data-testid={contextMenuEntry.id}
data-testid="open-molart"
>
{contextMenuEntry.name}
Open MolArt ({getUnitProtId()})
</button>
))}
</div>
<hr />
<button
className={twMerge('w-full cursor-pointer text-left text-xs font-normal')}
onClick={handleAddCommentClick}
type="button"
data-testid="add-comment"
>
Add comment
</button>
{pluginContextMenu.length && <hr />}
{pluginContextMenu.map(contextMenuEntry => (
<button
key={contextMenuEntry.id}
id={contextMenuEntry.id}
className={twMerge(
'cursor-pointer text-xs font-normal',
contextMenuEntry.style,
!contextMenuEntry.enabled ? 'cursor-not-allowed text-greyscale-700' : '',
)}
onClick={handleCallback(contextMenuEntry.callback)}
type="button"
data-testid={contextMenuEntry.id}
>
{contextMenuEntry.name}
</button>
))}
</div>
</OutsideClickWrapper>
);
};
/* eslint-disable no-magic-numbers */
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { OutsideClickWrapper } from '.';
describe('OutsideClickWrapper', () => {
it('should call onOutsideClick when click outside the component', () => {
const handleOutsideClick = jest.fn();
const { getByText } = render(
<OutsideClickWrapper onOutsideClick={handleOutsideClick}>
<div>Inner element</div>
</OutsideClickWrapper>,
);
const innerElement = getByText('Inner element');
fireEvent.mouseDown(document.body);
expect(handleOutsideClick).toHaveBeenCalledTimes(1);
fireEvent.mouseDown(innerElement);
expect(handleOutsideClick).toHaveBeenCalledTimes(1);
});
it('should not call onOutsideClick when click inside the component', () => {
const handleOutsideClick = jest.fn();
const { getByText } = render(
<OutsideClickWrapper onOutsideClick={handleOutsideClick}>
<div>Inner element</div>
</OutsideClickWrapper>,
);
const innerElement = getByText('Inner element');
fireEvent.mouseDown(innerElement);
expect(handleOutsideClick).not.toHaveBeenCalled();
});
});
import React, { useEffect, useRef, ReactNode } from 'react';
interface OutsideClickHandlerProps {
onOutsideClick: () => void;
children: ReactNode;
}
export const OutsideClickWrapper: React.FC<OutsideClickHandlerProps> = ({
onOutsideClick,
children,
}) => {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent): void => {
if (ref.current && !ref.current.contains(event.target as Node)) {
onOutsideClick();
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [onOutsideClick]);
return <div ref={ref}>{children}</div>;
};
export { OutsideClickWrapper } from './OutsideClickWrapper.component';
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment