diff --git a/webapp/src/widgets/menuWrapper.tsx b/webapp/src/widgets/menuWrapper.tsx index f3f7c9b12..9d66dead9 100644 --- a/webapp/src/widgets/menuWrapper.tsx +++ b/webapp/src/widgets/menuWrapper.tsx @@ -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(null) + const [open, setOpen] = useState(false) -export default class MenuWrapper extends React.PureComponent { - private node: React.RefObject - - 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): void => { - if (this.props.disabled) { + const toggle = (e: React.MouseEvent): void => { + if (props.disabled) { return } @@ -79,33 +53,43 @@ export default class MenuWrapper extends React.PureComponent { * 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 ( -
- {children ? Object.values(children)[0] : null} - {children && !this.props.disabled && this.state.open ? Object.values(children)[1] : null} -
- ) + const {children} = props + let className = 'MenuWrapper' + if (props.disabled) { + className += ' disabled' } -} + if (props.className) { + className += ' ' + props.className + } + + return ( +
+ {children ? Object.values(children)[0] : null} + {children && !props.disabled && open ? Object.values(children)[1] : null} +
+ ) +}) + +export default MenuWrapper