Skip to content

Commit

Permalink
PortalMenu now uses a portal (#363)
Browse files Browse the repository at this point in the history
  • Loading branch information
dalefukami committed Feb 9, 2023
1 parent 8ed3166 commit ad1da2a
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 67 deletions.
30 changes: 23 additions & 7 deletions src/platform-apps/channels/messages-menu/portal-menu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,49 @@ import React from 'react';
import { shallow } from 'enzyme';
import PortalMenu from './portal-menu';

jest.mock('react-dom', () => ({
createPortal: (node, _portalLocation) => {
return node;
},
}));

describe('Portal menu', () => {
const subject = (props: any = {}) => {
const subject = (props: any = {}, boundingRectangle = { x: 100, y: 250 }) => {
const allProps = {
className: '',
isOpen: false,
onClose: undefined,
...props,
};

return shallow(<PortalMenu {...allProps} />);
const wrapper = shallow(<PortalMenu {...allProps} />);
(wrapper.childAt(0).getElement() as any).ref({
getBoundingClientRect: () => boundingRectangle,
});
wrapper.update();
return wrapper;
};

it('adds className', () => {
const wrapper = subject({ className: 'portal-menu' });

expect(wrapper.hasClass('portal-menu')).toBe(true);
wrapper.setProps({ isOpen: true });

expect(wrapper.find('.portal-menu').exists()).toBe(true);
});

it('should portal menu be active when isOpen true', () => {
const wrapper = subject({ className: 'portal-menu', isOpen: true });
it('positions popup', () => {
const wrapper = subject({ className: 'portal-menu' }, { x: 50, y: 99 });

wrapper.setProps({ isOpen: true });

expect(wrapper.find('.portal-menu').hasClass('active')).toBe(true);
expect(wrapper.find('.portal-menu').prop('style')).toEqual({ top: 99, left: 50 });
});

it('should call onClose when exit portal', () => {
const onClose = jest.fn();
const wrapper = subject({ className: 'portal-menu', isOpen: true, onClose });
const wrapper = subject({ className: 'portal-menu', onClose });
wrapper.setProps({ isOpen: true });

wrapper.find('.portal-menu').simulate('click');

Expand Down
33 changes: 24 additions & 9 deletions src/platform-apps/channels/messages-menu/portal-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { createPortal } from 'react-dom';
import classNames from 'classnames';

export interface Properties {
Expand All @@ -7,20 +8,21 @@ export interface Properties {
onClose: () => void;
}

interface State {
isMenuOpen: boolean;
}

export default class PortalMenu extends React.Component<Properties, State> {
state = { isMenuOpen: false };

export default class PortalMenu extends React.Component<Properties> {
close = () => this.props.onClose();
ref: HTMLDivElement = null;

render() {
renderMenu() {
if (!this.ref) {
return null;
}

const { x, y } = this.ref.getBoundingClientRect();
return (
<div
className={classNames(this.props.className, { active: this.props.isOpen })}
className={classNames(this.props.className)}
onClick={this.close}
style={{ top: y, left: x }}
>
<div className='portal-menu__content'>
<ul>{this.props.children}</ul>
Expand All @@ -32,4 +34,17 @@ export default class PortalMenu extends React.Component<Properties, State> {
</div>
);
}

setPositionRef = (el) => {
this.ref = el;
};

render() {
return (
<>
<div ref={this.setPositionRef}></div>
{this.props.isOpen && createPortal(this.renderMenu(), document.body)}
</>
);
}
}
95 changes: 44 additions & 51 deletions src/platform-apps/channels/messages-menu/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,60 +6,53 @@
visibility: visible;
}
}
}

.portal-menu {
position: absolute;
visibility: hidden;
top: 12px;
right: 30px;
&__underlay {
z-index: 100;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
opacity: 0;
background-color: theme.$underlay-tooltip-background;
}
.portal-menu {
position: absolute;
z-index: 1000;

&.active {
visibility: visible;
.portal-menu__underlay {
display: block;
animation: partial-fadein 0.3s ease-out normal forwards;
}
}
&__underlay {
z-index: 100;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
opacity: 0;
background-color: theme.$underlay-tooltip-background;
display: block;
animation: partial-fadein 0.3s ease-out normal forwards;
}

&__content {
position: relative;
background: theme.$background-color-tertiary;
border-radius: 6px;
cursor: pointer;
z-index: 200;
ul {
padding: 10px 0;
margin: 0;
.menu-button {
height: 30px;
align-items: center;
display: flex;
overflow: hidden;
padding: 0 20px;
cursor: pointer;
color: theme.$actionable-inactive-color;
&:hover {
background-color: theme.$actionable-hover-background-tooltip;
color: theme.$actionable-color;
}
span {
font-family: Inter Regular;
font-size: 14px;
font-weight: 400;
line-height: 16px;
position: relative;
top: 1px;
}
&__content {
position: relative;
background: theme.$background-color-tertiary;
border-radius: 6px;
cursor: pointer;
z-index: 200;
ul {
padding: 10px 0;
margin: 0;
.menu-button {
height: 30px;
align-items: center;
display: flex;
overflow: hidden;
padding: 0 20px;
cursor: pointer;
color: theme.$actionable-inactive-color;
&:hover {
background-color: theme.$actionable-hover-background-tooltip;
color: theme.$actionable-color;
}
span {
font-family: Inter Regular;
font-size: 14px;
font-weight: 400;
line-height: 16px;
position: relative;
top: 1px;
}
}
}
Expand Down

0 comments on commit ad1da2a

Please sign in to comment.