Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix "Closing split bill doesn't refocus on composer" on web/desktop #12031

Merged
merged 7 commits into from
Dec 7, 2022
139 changes: 0 additions & 139 deletions src/components/PopoverMenu/BasePopoverMenu.js

This file was deleted.

156 changes: 131 additions & 25 deletions src/components/PopoverMenu/index.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,136 @@
import React from 'react';
import BasePopoverMenu from './BasePopoverMenu';
import {propTypes, defaultProps} from './popoverMenuPropTypes';

/**
* The web implementation of the menu needs to trigger actions before the popup closes
* When the modal is closed it's elements are destroyed
* Some browser will ignore interactions on elements that were removed or if the
* the action is not triggered immediately after a click
* This is a precaution against malicious scripts
*
* @param {Object} props
* @returns {React.ReactElement}
*/
const PopoverMenu = (props) => {
// Trigger the item's `onSelect` action as soon as clicked
const selectItem = (item) => {
item.onSelected();
props.onItemSelected(item);
};

// eslint-disable-next-line react/jsx-props-no-spreading
return <BasePopoverMenu {...props} onItemSelected={selectItem} />;
import _ from 'underscore';
import React, {PureComponent} from 'react';
import {View} from 'react-native';
import Popover from '../Popover';
import styles from '../../styles/styles';
import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions';
import MenuItem from '../MenuItem';
import {
propTypes as createMenuPropTypes,
defaultProps,
} from './popoverMenuPropTypes';
import ArrowKeyFocusManager from '../ArrowKeyFocusManager';
import Text from '../Text';
import KeyboardShortcut from '../../libs/KeyboardShortcut';
import CONST from '../../CONST';

const propTypes = {
...createMenuPropTypes,
...windowDimensionsPropTypes,
};

class PopoverMenu extends PureComponent {
constructor(props) {
super(props);
this.state = {
focusedIndex: -1,
};
this.resetFocusAndHideModal = this.resetFocusAndHideModal.bind(this);
this.removeKeyboardListener = this.removeKeyboardListener.bind(this);
this.attachKeyboardListener = this.attachKeyboardListener.bind(this);
this.selectedItem = null;
}

componentDidUpdate(prevProps) {
if (this.props.isVisible === prevProps.isVisible) {
return;
}

if (this.props.isVisible) {
this.attachKeyboardListener();
} else {
this.removeKeyboardListener();
}
}

componentWillUnmount() {
this.removeKeyboardListener();
}

/**
* Set item to local variable to fire `onSelected` action after the menu popup closes
* @param {Object} item
*/
selectItem(item) {
this.selectedItem = item;
this.props.onItemSelected(item);
}

attachKeyboardListener() {
const shortcutConfig = CONST.KEYBOARD_SHORTCUTS.ENTER;
this.unsubscribeEnterKey = KeyboardShortcut.subscribe(shortcutConfig.shortcutKey, () => {
if (this.state.focusedIndex === -1) {
return;
}
this.selectItem(this.props.menuItems[this.state.focusedIndex]);
this.setState({focusedIndex: -1}); // Reset the focusedIndex on selecting any menu
}, shortcutConfig.descriptionKey, shortcutConfig.modifiers, true);
}

removeKeyboardListener() {
if (!this.unsubscribeEnterKey) {
return;
}
this.unsubscribeEnterKey();
}

resetFocusAndHideModal() {
this.setState({focusedIndex: -1}); // Reset the focusedIndex on modal hide
this.removeKeyboardListener();
if (this.selectedItem) {
this.selectedItem.onSelected();
this.selectedItem = null;
}
}

render() {
return (
<Popover
anchorPosition={this.props.anchorPosition}
onClose={this.props.onClose}
isVisible={this.props.isVisible}
onModalHide={this.resetFocusAndHideModal}
animationIn={this.props.animationIn}
animationOut={this.props.animationOut}
animationInTiming={this.props.animationInTiming}
disableAnimation={this.props.disableAnimation}
fromSidebarMediumScreen={this.props.fromSidebarMediumScreen}
>
<View style={this.props.isSmallScreenWidth ? {} : styles.createMenuContainer}>
{!_.isEmpty(this.props.headerText) && (
<View style={styles.createMenuItem}>
<Text
style={[styles.createMenuHeaderText, styles.ml3]}
>
{this.props.headerText}
</Text>
</View>
)}
<ArrowKeyFocusManager
focusedIndex={this.state.focusedIndex}
maxIndex={this.props.menuItems.length - 1}
onFocusedIndexChanged={index => this.setState({focusedIndex: index})}
>
{_.map(this.props.menuItems, (item, menuIndex) => (
<MenuItem
key={item.text}
icon={item.icon}
iconWidth={item.iconWidth}
iconHeight={item.iconHeight}
title={item.text}
description={item.description}
onPress={() => this.selectItem(item)}
focused={this.state.focusedIndex === menuIndex}
/>
))}
</ArrowKeyFocusManager>
</View>
</Popover>
);
}
}

PopoverMenu.propTypes = propTypes;
PopoverMenu.defaultProps = defaultProps;
PopoverMenu.displayName = 'PopoverMenu';

export default PopoverMenu;
export default withWindowDimensions(PopoverMenu);
41 changes: 0 additions & 41 deletions src/components/PopoverMenu/index.native.js

This file was deleted.