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

Add popovers without overlay #19011

Merged
merged 60 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
2eda49d
feat: refactored popover component to work without overlay
allroundexperts Mar 6, 2023
6c39e70
fix: added onShowModal and onHideModal callbacks
allroundexperts Mar 6, 2023
2e69592
feat: added more listeners
allroundexperts Mar 10, 2023
9fca908
Merge branch 'main' into fix-15289
allroundexperts Mar 10, 2023
5d48e5b
Merge branch 'main' into fix-15289
allroundexperts Apr 12, 2023
41226e3
feat: updated popover without overlay implementation
allroundexperts Apr 13, 2023
3b6871f
Merge branch 'main' into fix-15289
allroundexperts Apr 30, 2023
872ad78
fix: comments
allroundexperts May 1, 2023
4288309
Merge branch 'main' into fix-15289
allroundexperts May 5, 2023
2010c6f
feat: handled popover migration changes
allroundexperts May 6, 2023
39457e7
Merge branch 'main' into fix-15289
allroundexperts May 13, 2023
11ca1bd
feat: lint changes"
allroundexperts May 13, 2023
d5330f5
feat: fix emoji popover consistency
allroundexperts May 13, 2023
d023e16
Merge branch 'main' into fix-15289
allroundexperts May 16, 2023
138dfcf
feat: use refs for popover anchor identification
allroundexperts May 16, 2023
2a9c74b
Merge branch 'main' into fix-15289
allroundexperts May 17, 2023
256bb31
feat: added extra line in const file
allroundexperts May 17, 2023
06282c3
feat: fix lint error for useEffect hook
allroundexperts May 17, 2023
653f295
feat: more cleaning
allroundexperts May 18, 2023
41ab963
Merge branch 'main' into fix-15289
allroundexperts May 19, 2023
cd9b7d3
feat: add popover to more items
allroundexperts May 19, 2023
be09f48
Merge branch 'main' into fix-15289
allroundexperts May 30, 2023
f8b9e31
Merge branch 'main' into fix-15289
allroundexperts Jun 8, 2023
53e2de6
fix: lint errors
allroundexperts Jun 8, 2023
07628c6
fix: more popover fixes
allroundexperts Jun 9, 2023
e15c660
fix: NAB comments
allroundexperts Jun 9, 2023
cb1daff
Merge branch 'main' into fix-15289
allroundexperts Jun 9, 2023
9a1fd90
fix: handled more comments
allroundexperts Jun 9, 2023
1c3bfaa
fix: lint errors
allroundexperts Jun 9, 2023
289e030
fix: more lint errors
allroundexperts Jun 9, 2023
ed9fb14
fix: remove popoverwithoutoverlay const
allroundexperts Jun 9, 2023
3546860
fix: report action item hover stuck fix
allroundexperts Jun 10, 2023
4a7abaf
fix: lint errors
allroundexperts Jun 10, 2023
c538b02
merge with main and resolve conflicts
allroundexperts Jun 13, 2023
f033a3e
Merge branch 'main' into fix-15289
allroundexperts Jun 16, 2023
8655c02
feat: lint errors
allroundexperts Jun 16, 2023
28d8f5a
feat: add reason for disabling exhaustive-deps rule in PopoverWithout…
allroundexperts Jun 19, 2023
c526124
Merge branch 'main' into fix-15289
allroundexperts Jun 19, 2023
bf2120b
feat: lint errors
allroundexperts Jun 19, 2023
58ea8a0
Merge branch 'main' into fix-15289
allroundexperts Jun 27, 2023
7efadff
fix: handle more code style changes
allroundexperts Jun 28, 2023
362f5d5
Merge branch 'main' into fix-15289
allroundexperts Jun 30, 2023
75f5ab4
fix post merge errors
allroundexperts Jun 30, 2023
d866919
feat: merge with main
allroundexperts Jul 6, 2023
eaacce0
feat: merge with main
allroundexperts Jul 10, 2023
37da8c4
Merge branch 'main' into fix-15289
allroundexperts Jul 17, 2023
1c2502d
fix: lint fixes
allroundexperts Jul 17, 2023
5cc78c8
fix: merge with main
allroundexperts Jul 17, 2023
d71eff1
fix: console errors
allroundexperts Jul 17, 2023
80af7e8
fix: safari image picker issues
allroundexperts Jul 17, 2023
5c23bf5
Merge branch 'main' into fix-15289
allroundexperts Jul 19, 2023
c494ff4
Merge branch 'main' into fix-15289
allroundexperts Jul 19, 2023
24045e4
fix: lint errors
allroundexperts Jul 19, 2023
ffc460e
Merge branch 'main' into fix-15289
allroundexperts Jul 20, 2023
d9c9191
Merge branch 'main' into fix-15289
allroundexperts Jul 25, 2023
d35b4d8
lint fix
allroundexperts Jul 25, 2023
41ba45b
merge with main
allroundexperts Jul 28, 2023
27fcc40
Merge branch 'main' into fix-15289
allroundexperts Aug 1, 2023
edcc556
Merge branch 'main' into fix-15289
allroundexperts Aug 2, 2023
74f8e0d
Merge branch 'main' into fix-15289
allroundexperts Aug 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Expensify from './Expensify';
import {LocaleContextProvider} from './components/withLocalize';
import OnyxProvider from './components/OnyxProvider';
import HTMLEngineProvider from './components/HTMLEngineProvider';
import PopoverContextProvider from './components/PopoverProvider';
import ComposeProviders from './components/ComposeProviders';
import SafeArea from './components/SafeArea';
import * as Environment from './libs/Environment/Environment';
Expand Down Expand Up @@ -44,6 +45,7 @@ const App = () => (
HTMLEngineProvider,
WindowDimensionsProvider,
KeyboardStateProvider,
PopoverContextProvider,
PickerStateProvider,
]}
>
Expand Down
2 changes: 1 addition & 1 deletion src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ const CONST = {
CENTERED_SMALL: 'centered_small',
BOTTOM_DOCKED: 'bottom_docked',
POPOVER: 'popover',
POPOVER_WITHOUT_OVERLAY: 'popover_without_overlay',
RIGHT_DOCKED: 'right_docked',
},
ANCHOR_ORIGIN_VERTICAL: {
Expand Down Expand Up @@ -664,7 +665,6 @@ const CONST = {
VALIDATE_CODE_FAILED: 'Validate code failed',
},
},

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why line removed? unnecessary change

PUSHER: {
PRIVATE_USER_CHANNEL_PREFIX: 'private-encrypted-user-accountID-',
PRIVATE_REPORT_CHANNEL_PREFIX: 'private-report-reportID-',
Expand Down
6 changes: 6 additions & 0 deletions src/components/AddPaymentMethodMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ const propTypes = {
/** List of betas available to current user */
betas: PropTypes.arrayOf(PropTypes.string),

/** Popover anchor ref */
anchorRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use /components/refPropTypes everywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Replaced all instances that I could find.


...withLocalizePropTypes,
};

Expand All @@ -36,13 +39,15 @@ const defaultProps = {
payPalMeData: {},
shouldShowPaypal: true,
betas: [],
anchorRef: () => {},
};

const AddPaymentMethodMenu = (props) => (
<PopoverMenu
isVisible={props.isVisible}
onClose={props.onClose}
anchorPosition={props.anchorPosition}
anchorRef={props.anchorRef}
onItemSelected={() => props.onClose()}
menuItems={[
{
Expand Down Expand Up @@ -71,6 +76,7 @@ const AddPaymentMethodMenu = (props) => (
]
: []),
]}
withoutOverlay
/>
);

Expand Down
14 changes: 13 additions & 1 deletion src/components/AvatarWithImagePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class AvatarWithImagePicker extends React.Component {
imageUri: '',
imageType: '',
};
this.anchorRef = React.createRef();
}

componentDidMount() {
Expand Down Expand Up @@ -256,7 +257,16 @@ class AvatarWithImagePicker extends React.Component {

return (
<View style={[styles.alignItemsCenter, ...additionalStyles]}>
<Pressable onPress={() => this.setState({isMenuVisible: true})}>
<Pressable
ref={this.anchorRef}
onPress={(e) => {
if (e.nativeEvent.anchorRef && e.nativeEvent.anchorRef.current === this.anchorRef.current) {
return;
}

this.setState((prev) => ({isMenuVisible: !prev.isMenuVisible}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this change for?

Screen.Recording.2023-06-05.at.3.32.52.PM.mov

}}
>
<View style={[styles.pRelative, styles.avatarLarge]}>
<Tooltip text={this.props.translate('avatarWithImagePicker.editImage')}>
{this.props.source ? (
Expand Down Expand Up @@ -294,6 +304,8 @@ class AvatarWithImagePicker extends React.Component {
onItemSelected={() => this.setState({isMenuVisible: false})}
menuItems={this.createMenuItems(openPicker)}
anchorPosition={this.props.anchorPosition}
withoutOverlay
anchorRef={this.anchorRef}
/>
</>
)}
Expand Down
15 changes: 14 additions & 1 deletion src/components/Button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ const propTypes = {

/** Accessibility label for the component */
accessibilityLabel: PropTypes.string,

/** React ref being forwarded to the Button */
forwardedRef: PropTypes.func,
};

const defaultProps = {
Expand Down Expand Up @@ -141,6 +144,7 @@ const defaultProps = {
shouldEnableHapticFeedback: false,
nativeID: '',
accessibilityLabel: '',
forwardedRef: () => {},
};

class Button extends Component {
Expand Down Expand Up @@ -240,6 +244,7 @@ class Button extends Component {
render() {
return (
<PressableWithFeedback
ref={this.props.forwardedRef}
onPress={(e) => {
if (e && e.type === 'click') {
e.currentTarget.blur();
Expand Down Expand Up @@ -303,4 +308,12 @@ class Button extends Component {
Button.propTypes = propTypes;
Button.defaultProps = defaultProps;

export default compose(withNavigationFallback, withNavigationFocus)(Button);
const ComposedButton = compose(withNavigationFallback, withNavigationFocus)(Button);

export default React.forwardRef((props, ref) => (
<ComposedButton
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));
5 changes: 5 additions & 0 deletions src/components/ButtonWithDropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@ const propTypes = {

/** Should the button be disabled */
isDisabled: PropTypes.bool,

/** Ref to the dropdown button */
dropdownButtonRef: PropTypes.func,
};

const defaultProps = {
onButtonPress: () => {},
onDropdownPress: () => {},
isDisabled: false,
isLoading: false,
dropdownButtonRef: () => {},
};

const ButtonWithDropdown = (props) => (
Expand All @@ -50,6 +54,7 @@ const ButtonWithDropdown = (props) => (
style={[styles.pl0]}
onPress={props.onDropdownPress}
shouldRemoveLeftBorderRadius
ref={props.dropdownButtonRef}
>
<Icon
src={Expensicons.DownArrow}
Expand Down
12 changes: 10 additions & 2 deletions src/components/ButtonWithMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class ButtonWithMenu extends PureComponent {
selectedItemIndex: 0,
isMenuVisible: false,
};

this.anchorRef = React.createRef();
}

setMenuVisibility(isMenuVisible) {
Expand All @@ -64,9 +66,13 @@ class ButtonWithMenu extends PureComponent {
isLoading={this.props.isLoading}
isDisabled={this.props.isDisabled}
onButtonPress={(event) => this.props.onPress(event, selectedItem.value)}
onDropdownPress={() => {
this.setMenuVisibility(true);
onDropdownPress={(e) => {
if (e.nativeEvent.anchorRef && e.nativeEvent.anchorRef.current === this.anchorRef.current) {
return;
}
this.setMenuVisibility(!this.state.isMenuVisible);
}}
dropdownButtonRef={this.anchorRef}
/>
) : (
<Button
Expand All @@ -92,6 +98,8 @@ class ButtonWithMenu extends PureComponent {
this.setState({selectedItemIndex: index});
},
}))}
withoutOverlay
anchorRef={this.anchorRef}
/>
)}
</View>
Expand Down
15 changes: 9 additions & 6 deletions src/components/EmojiPicker/EmojiPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class EmojiPicker extends React.Component {

emojiPopoverAnchorOrigin: DEFAULT_ANCHOR_ORIGIN,
};

this.emojiPopoverAnchor = React.createRef();
}

componentDidMount() {
Expand Down Expand Up @@ -68,7 +70,7 @@ class EmojiPicker extends React.Component {
// The first click will hide the emoji picker by calling the hideEmojiPicker() function
// and in that function the emojiPopoverAnchor prop to will be set to null (synchronously)
// thus we rely on that prop to prevent fast click / multiple emoji selection
if (!this.emojiPopoverAnchor) {
if (!this.emojiPopoverAnchor.current) {
return;
}

Expand All @@ -87,7 +89,6 @@ class EmojiPicker extends React.Component {
if (isNavigating) {
this.onModalHide = () => {};
}
this.emojiPopoverAnchor = null;
this.setState({isEmojiPickerVisible: false});
}

Expand All @@ -103,9 +104,9 @@ class EmojiPicker extends React.Component {
showEmojiPicker(onModalHide, onEmojiSelected, emojiPopoverAnchor, anchorOrigin, onWillShow = () => {}) {
this.onModalHide = onModalHide;
this.onEmojiSelected = onEmojiSelected;
this.emojiPopoverAnchor = emojiPopoverAnchor;
this.emojiPopoverAnchor.current = emojiPopoverAnchor;

if (this.emojiPopoverAnchor) {
if (this.emojiPopoverAnchor.current) {
// Drop focus to avoid blue focus ring.
emojiPopoverAnchor.blur();
}
Expand All @@ -118,10 +119,10 @@ class EmojiPicker extends React.Component {

measureEmojiPopoverAnchorPosition() {
return new Promise((resolve) => {
if (!this.emojiPopoverAnchor) {
if (!this.emojiPopoverAnchor.current) {
return resolve({horizontal: 0, vertical: 0});
}
this.emojiPopoverAnchor.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y}));
this.emojiPopoverAnchor.current.measureInWindow((x, y, width) => resolve({horizontal: x + width, vertical: y}));
});
}

Expand Down Expand Up @@ -177,7 +178,9 @@ class EmojiPicker extends React.Component {
height: CONST.EMOJI_PICKER_SIZE.HEIGHT,
}}
anchorOrigin={this.state.emojiPopoverAnchorOrigin}
anchorRef={this.emojiPopoverAnchor}
measureContent={this.measureContent}
withoutOverlay
>
<EmojiPickerMenu
onEmojiSelected={this.selectEmoji}
Expand Down
18 changes: 14 additions & 4 deletions src/components/EmojiPicker/EmojiPickerButton.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useRef} from 'react';
import {Pressable} from 'react-native';
import PropTypes from 'prop-types';
import styles from '../../styles/styles';
Expand Down Expand Up @@ -26,17 +26,27 @@ const defaultProps = {
};

const EmojiPickerButton = (props) => {
let emojiPopoverAnchor = null;
const emojiPopoverAnchor = useRef(null);
return (
<Tooltip
containerStyles={[styles.alignSelfEnd]}
text={props.translate('reportActionCompose.emoji')}
>
<Pressable
ref={(el) => (emojiPopoverAnchor = el)}
ref={emojiPopoverAnchor}
style={({hovered, pressed}) => [styles.chatItemEmojiButton, StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed))]}
disabled={props.isDisabled}
onPress={() => EmojiPickerAction.showEmojiPicker(props.onModalHide, props.onEmojiSelected, emojiPopoverAnchor)}
onPress={(e) => {
if (e.nativeEvent.anchorRef && e.nativeEvent.anchorRef.current === emojiPopoverAnchor.current) {
return;
}

if (!EmojiPickerAction.emojiPickerRef.current.state.isEmojiPickerVisible) {
EmojiPickerAction.showEmojiPicker(props.onModalHide, props.onEmojiSelected, emojiPopoverAnchor.current);
} else {
EmojiPickerAction.emojiPickerRef.current.hideEmojiPicker();
}
}}
nativeID={props.nativeID}
>
{({hovered, pressed}) => (
Expand Down
7 changes: 7 additions & 0 deletions src/components/Popover/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {propTypes, defaultProps} from './popoverPropTypes';
import CONST from '../../CONST';
import Modal from '../Modal';
import withWindowDimensions from '../withWindowDimensions';
import PopoverWithoutOverlay from '../PopoverWithoutOverlay';

/*
* This is a convenience wrapper around the Modal component for a responsive Popover.
Expand All @@ -24,6 +25,12 @@ const Popover = (props) => {
document.body,
);
}

if (props.withoutOverlay && !props.isSmallScreenWidth) {
// eslint-disable-next-line react/jsx-props-no-spreading
return createPortal(<PopoverWithoutOverlay {...props} />, document.body);
}

return (
<Modal
// eslint-disable-next-line react/jsx-props-no-spreading
Expand Down
4 changes: 4 additions & 0 deletions src/components/Popover/popoverPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const propTypes = {
left: PropTypes.number,
}),

/** The anchor ref of the popover */
anchorRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),

/** A react-native-animatable animation timing for the modal display animation. */
animationInTiming: PropTypes.number,

Expand All @@ -30,6 +33,7 @@ const defaultProps = {

// Anchor position is optional only because it is not relevant on mobile
anchorPosition: {},
anchorRef: () => {},
disableAnimation: true,
};

Expand Down
2 changes: 2 additions & 0 deletions src/components/PopoverMenu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class PopoverMenu extends PureComponent {
return (
<Popover
anchorPosition={this.props.anchorPosition}
anchorRef={this.props.anchorRef}
onClose={this.props.onClose}
isVisible={this.props.isVisible}
onModalHide={this.resetFocusAndHideModal}
Expand All @@ -98,6 +99,7 @@ class PopoverMenu extends PureComponent {
animationInTiming={this.props.animationInTiming}
disableAnimation={this.props.disableAnimation}
fromSidebarMediumScreen={this.props.fromSidebarMediumScreen}
withoutOverlay={this.props.withoutOverlay}
>
<View style={this.props.isSmallScreenWidth ? {} : styles.createMenuContainer}>
{!_.isEmpty(this.props.headerText) && (
Expand Down
3 changes: 3 additions & 0 deletions src/components/PopoverMenu/popoverMenuPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ const propTypes = {
left: PropTypes.number,
}).isRequired,

/** The anchor reference of the CreateMenu popover */
anchorRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired,

/** A react-native-animatable animation definition for the modal display animation. */
animationIn: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),

Expand Down
Loading