Migrating to menuWrapper to functional component

This commit is contained in:
Jesús Espino 2021-03-30 13:53:12 +02:00
parent e441c4ee91
commit 009639b1ea

View File

@ -1,75 +1,49 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react'
import React, {useRef, useState, useEffect} from 'react'
import './menuWrapper.scss'
type Props = {
children?: React.ReactNode;
onToggle?: (open: boolean) => void;
isDisabled?: boolean;
stopPropagationOnToggle?: boolean;
className?: string
disabled?: boolean
}
type State = {
open: boolean;
}
const MenuWrapper = React.memo((props: Props) => {
const node = useRef<HTMLDivElement>(null)
const [open, setOpen] = useState(false)
export default class MenuWrapper extends React.PureComponent<Props, State> {
private node: React.RefObject<HTMLDivElement>
public constructor(props: Props) {
super(props)
if (!Array.isArray(props.children) || props.children.length !== 2) {
throw new Error('MenuWrapper needs exactly 2 children')
}
this.state = {
open: false,
}
this.node = React.createRef()
if (!Array.isArray(props.children) || props.children.length !== 2) {
throw new Error('MenuWrapper needs exactly 2 children')
}
public componentDidMount(): void {
document.addEventListener('menuItemClicked', this.close, true)
document.addEventListener('click', this.closeOnBlur, true)
document.addEventListener('keyup', this.keyboardClose, true)
const close = (): void => {
setOpen(false)
}
public componentWillUnmount(): void {
document.removeEventListener('menuItemClicked', this.close, true)
document.removeEventListener('click', this.closeOnBlur, true)
document.removeEventListener('keyup', this.keyboardClose, true)
}
private keyboardClose = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
this.close()
}
if (e.key === 'Tab') {
this.closeOnBlur(e)
}
}
private closeOnBlur = (e: Event) => {
if (this.node && this.node.current && e.target && this.node.current.contains(e.target as Node)) {
const closeOnBlur = (e: Event) => {
if (e.target && node.current?.contains(e.target as Node)) {
return
}
this.close()
close()
}
public close = (): void => {
if (this.state.open) {
this.setState({open: false})
const keyboardClose = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
close()
}
if (e.key === 'Tab') {
closeOnBlur(e)
}
}
private toggle = (e: React.MouseEvent<HTMLDivElement, MouseEvent>): void => {
if (this.props.disabled) {
const toggle = (e: React.MouseEvent<HTMLDivElement, MouseEvent>): void => {
if (props.disabled) {
return
}
@ -79,33 +53,43 @@ export default class MenuWrapper extends React.PureComponent<Props, State> {
* We need to refactor this so that the modal is explicitly closed on toggle, but for now I am aiming to preserve the existing logic
* so as to not break other things
**/
if (this.props.stopPropagationOnToggle) {
if (props.stopPropagationOnToggle) {
e.preventDefault()
e.stopPropagation()
}
const newState = !this.state.open
this.setState({open: newState})
setOpen(!open)
}
public render(): JSX.Element {
const {children} = this.props
let className = 'MenuWrapper'
if (this.props.disabled) {
className += ' disabled'
}
if (this.props.className) {
className += ' ' + this.props.className
useEffect(() => {
document.addEventListener('menuItemClicked', close, true)
document.addEventListener('click', closeOnBlur, true)
document.addEventListener('keyup', keyboardClose, true)
return () => {
document.removeEventListener('menuItemClicked', close, true)
document.removeEventListener('click', closeOnBlur, true)
document.removeEventListener('keyup', keyboardClose, true)
}
}, [])
return (
<div
className={className}
onClick={this.toggle}
ref={this.node}
>
{children ? Object.values(children)[0] : null}
{children && !this.props.disabled && this.state.open ? Object.values(children)[1] : null}
</div>
)
const {children} = props
let className = 'MenuWrapper'
if (props.disabled) {
className += ' disabled'
}
}
if (props.className) {
className += ' ' + props.className
}
return (
<div
className={className}
onClick={toggle}
ref={node}
>
{children ? Object.values(children)[0] : null}
{children && !props.disabled && open ? Object.values(children)[1] : null}
</div>
)
})
export default MenuWrapper