-
Notifications
You must be signed in to change notification settings - Fork 3k
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
[$250] Firefox - Chat - Highlighted text disappears when selecting a part of the text and opening context menu reported by @daraksha-dk #12521
Comments
Triggered auto assignment to @johncschuster ( |
This issue looks unique and I can reproduce this by doing the following:
I tested this in Safari and the NewDot app on Staging, so I don't think this is specific to Firefox. @kavimuru, did you test this on all platforms? Did you get different results across platforms? |
I chatted this over with @hax, and we really dialed in the nuance of the behavior in Slack. In general, my expectation of highlighted text works something like this:
On Firefox, it seems the highlighted text is dismissed right as the context menu is summoned. On other platforms (Safari, for example), the highlighted text is dismissed when an option from the context menu is selected. Let's achieve parity on all platforms, either by fixing the Firefox-specific behavior to match the behavior in Safari and others or by updating all highlight-dismissing behavior to match what I would consider "system-level" behavior. |
Current assignee @johncschuster is eligible for the External assigner, not assigning anyone new. |
Triggered auto assignment to Contributor-plus team member for initial proposal review - @thesahindia ( |
Triggered auto assignment to @stitesExpensify ( |
Upwork job: https://www.upwork.com/jobs/~01f721fddf8bdf3016 |
Proposalfixing the Firefox-specific behavior to match the behavior in Safari and others --- a/src/components/PopoverWithMeasuredContent.js
+++ b/src/components/PopoverWithMeasuredContent.js
@@ -72,9 +72,66 @@ class PopoverWithMeasuredContent extends Component {
this.state = {
isContentMeasured: this.popoverWidth > 0 && this.popoverHeight > 0,
isVisible: false,
+
+ prevSelection: null,
};
this.measurePopover = this.measurePopover.bind(this);
+
+ this.onSelectionChange = this.onSelectionChange.bind(this);
+ }
+
+ getSnapshotBeforeUpdate(prevProps, prevState) {
+ // Save current selection while the popover is not visible yet
+ if (prevState.isVisible === false) {
+ const selection = window.getSelection();
+ if (selection.rangeCount > 0) {
+ this.setState({
+ prevSelection: {
+ range: selection.getRangeAt(0)
+ }
+ });
+ }
+ }
+
+ return null;
+ }
+
+ componentDidUpdate(prevProps, prevState, snapshot) {
+ /*
+ In FF, the change in document.activeElement will cause a change in the window current selection
+ Using the event "selectionchange" we can reset the selection
+
+ In other browsers, the window current selection will remain unchanged; "selectionchange" event will not be fired
+ for the sake of consistency and to make the behavior identical across all browsers, we trigger that event manually
+ */
+ if (this.state.prevSelection !== null && this.state.isVisible === true) {
+ if (navigator.userAgent.search("Firefox") === -1) { // Not Firefox
+ document.dispatchEvent(new Event("selectionchange"));
+ }
+ }
+ }
+
+ onSelectionChange(e) {
+ // If the selection changed and we have a saved selection, revert to that saved selection
+ if (this.state.prevSelection !== null && this.state.isVisible === true) {
+ const selection = window.getSelection();
+ const range = this.state.prevSelection.range
+ selection.removeAllRanges();
+ selection.addRange(range);
+
+ this.setState({
+ prevSelection: null
+ });
+ }
+ }
+
+ componentDidMount() {
+ document.addEventListener('selectionchange', this.onSelectionChange);
+ }
+
+ componentWillUnmount() {
+ document.removeEventListener('selectionchange', this.onSelectionChange);
}
/**
ExplantionThe workaround it to save the current selection just before the context menu (popover) is displayed Kooha-2022-11-08-15-58-55.mp4 |
@stitesExpensify what are your thoughts on the proposal? |
I'll have @thesahindia take a look first 😄 |
Sorry for the delay but I don't have the bandwidth. Please re-assign. |
Current assignee @johncschuster is eligible for the External assigner, not assigning anyone new. |
Triggered auto assignment to Contributor-plus team member for initial proposal review - @Santhosh-Sellavel ( |
Current assignee @stitesExpensify is eligible for the External assigner, not assigning anyone new. |
Hi @Santhosh-Sellavel can you please check out @s77rt 's proposal? |
Proposal (Updated)diff --git a/src/components/Modal/BaseModal.js b/src/components/Modal/BaseModal.js
index 91b1867a6..f7300cc09 100644
--- a/src/components/Modal/BaseModal.js
+++ b/src/components/Modal/BaseModal.js
@@ -103,8 +103,8 @@ class BaseModal extends PureComponent {
backdropColor={themeColors.modalBackdrop}
backdropOpacity={hideBackdrop ? 0 : 0.5}
backdropTransitionOutTiming={0}
- hasBackdrop={this.props.fullscreen}
- coverScreen={this.props.fullscreen}
+ hasBackdrop={typeof(this.props.hasBackdrop) === "boolean" ? this.props.hasBackdrop : this.props.fullscreen}
+ coverScreen={typeof(this.props.coverScreen) === "boolean" ? this.props.coverScreen : this.props.fullscreen}
style={modalStyle}
deviceHeight={this.props.windowHeight}
deviceWidth={this.props.windowWidth}
diff --git a/src/components/Modal/index.js b/src/components/Modal/index.js
index add8feef0..d7b7a1603 100644
--- a/src/components/Modal/index.js
+++ b/src/components/Modal/index.js
@@ -6,29 +6,52 @@ import {propTypes, defaultProps} from './modalPropTypes';
class Modal extends Component {
constructor(props) {
super(props);
+ this.focus = this.focus.bind(this);
+ this.trapFocus = this.trapFocus.bind(this);
this.closeOnOutsideClick = this.closeOnOutsideClick.bind(this);
}
componentDidMount() {
- if (!this.props.shouldCloseOnOutsideClick) {
- return;
+ if (this.props.shouldTrapFocus) {
+ document.addEventListener('focusin', this.trapFocus);
+ }
+ if (this.props.shouldCloseOnOutsideClick) {
+ document.addEventListener('mousedown', this.closeOnOutsideClick);
}
-
- document.addEventListener('mousedown', this.closeOnOutsideClick);
}
componentWillUnmount() {
- if (!this.props.shouldCloseOnOutsideClick) {
+ if (this.props.shouldTrapFocus) {
+ document.removeEventListener('focusin', this.trapFocus);
+ }
+ if (this.props.shouldCloseOnOutsideClick) {
+ document.removeEventListener('mousedown', this.closeOnOutsideClick);;
+ }
+ }
+
+ focus() {
+ if (!this.props.isVisible || !this.baseModalRef) {
return;
}
+ this.baseModalRef.focus();
+ }
- document.removeEventListener('mousedown', this.closeOnOutsideClick);
+ trapFocus(event) {
+ if (!this.props.isVisible
+ || !this.baseModalRef
+ || (this.baseModalRef && this.baseModalRef.contains(event.target))
+ || !this.props.shouldTrapFocus) {
+ return;
+ }
+
+ event.preventDefault();
+ this.baseModalRef.focus();
}
closeOnOutsideClick(event) {
if (!this.props.isVisible
|| !this.baseModalRef
- || this.baseModalRef.contains(event.target)
+ || (this.baseModalRef && this.baseModalRef.contains(event.target))
|| !this.props.shouldCloseOnOutsideClick) {
return;
}
@@ -39,9 +62,13 @@ class Modal extends Component {
render() {
return (
<BaseModal
- ref={el => this.baseModalRef = el}
// eslint-disable-next-line react/jsx-props-no-spreading
{...this.props}
+ ref={el => this.baseModalRef = el}
+ onModalShow={() => {
+ this.baseModalRef.setAttribute("tabindex", "-1");
+ this.props.onModalShow();
+ }}
>
{this.props.children}
</BaseModal>
diff --git a/src/components/Modal/modalPropTypes.js b/src/components/Modal/modalPropTypes.js
index 8f09588e3..14b20b78b 100644
--- a/src/components/Modal/modalPropTypes.js
+++ b/src/components/Modal/modalPropTypes.js
@@ -5,9 +5,18 @@ import {windowDimensionsPropTypes} from '../withWindowDimensions';
import stylePropTypes from '../../styles/stylePropTypes';
const propTypes = {
- /** Decides whether the modal should cover fullscreen. FullScreen modal has backdrop */
+ /** Decides whether the modal should cover the screen (can be overridden by specifying coverScreen explicitly). FullScreen modal has backdrop (can be overridden by specifying hasBackdrop explicitly) */
fullscreen: PropTypes.bool,
+ /** Decides whether the modal should cover screen */
+ coverScreen: PropTypes.bool,
+
+ /** Decides whether the modal should have a backdrop */
+ hasBackdrop: PropTypes.bool,
+
+ /** Decides whether the modal should trap focus */
+ shouldTrapFocus: PropTypes.bool,
+
/** Should we close modal on outside click */
shouldCloseOnOutsideClick: PropTypes.bool,
@@ -66,7 +75,8 @@ const propTypes = {
const defaultProps = {
fullscreen: true,
- shouldCloseOnOutsideClick: false,
+ shouldTrapFocus: true,
+ shouldCloseOnOutsideClick: true,
shouldSetModalVisibility: true,
onSubmit: null,
type: '',
diff --git a/src/components/Popover/index.js b/src/components/Popover/index.js
index 9a2e20ed3..354275282 100644
--- a/src/components/Popover/index.js
+++ b/src/components/Popover/index.js
@@ -31,6 +31,7 @@ const Popover = (props) => {
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
fullscreen={props.isSmallScreenWidth ? true : props.fullscreen}
+ hasBackdrop={props.isSmallScreenWidth ? true : props.hasBackdrop}
animationInTiming={props.disableAnimation && !props.isSmallScreenWidth ? 1 : props.animationInTiming}
animationOutTiming={props.disableAnimation && !props.isSmallScreenWidth ? 1 : props.animationOutTiming}
/>
diff --git a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.js b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.js
index d28e7db1e..0843f0918 100644
--- a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.js
+++ b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.js
@@ -288,7 +288,8 @@ class PopoverReportActionContextMenu extends React.Component {
animationOutTiming={1}
measureContent={this.measureContent}
shouldSetModalVisibility={false}
- fullscreen
+ hasBackdrop={true}
+ coverScreen={false}
>
<BaseReportActionContextMenu
isVisible
diff --git a/src/styles/getModalStyles/getBaseModalStyles.js b/src/styles/getModalStyles/getBaseModalStyles.js
index 7346a4951..0466a2c33 100644
--- a/src/styles/getModalStyles/getBaseModalStyles.js
+++ b/src/styles/getModalStyles/getBaseModalStyles.js
@@ -151,7 +151,7 @@ export default (type, windowDimensions, popoverAnchorPosition = {}, containerSty
...modalStyle,
...popoverAnchorPosition,
...{
- position: 'absolute',
+ position: 'fixed',
alignItems: 'center',
justifyContent: 'flex-end',
}, diff --git a/node_modules/react-native-modal/dist/modal.style.d.ts b/node_modules/react-native-modal/dist/modal.style.d.ts
index 1734c97..b9d09ad 100644
--- a/node_modules/react-native-modal/dist/modal.style.d.ts
+++ b/node_modules/react-native-modal/dist/modal.style.d.ts
@@ -1,6 +1,6 @@
declare const _default: {
backdrop: {
- position: "absolute";
+ position: "fixed";
top: number;
bottom: number;
left: number;
diff --git a/node_modules/react-native-modal/dist/modal.style.js b/node_modules/react-native-modal/dist/modal.style.js
index 191ea2f..b6aa1ca 100644
--- a/node_modules/react-native-modal/dist/modal.style.js
+++ b/node_modules/react-native-modal/dist/modal.style.js
@@ -1,7 +1,7 @@
import { StyleSheet } from 'react-native';
export default StyleSheet.create({
backdrop: {
- position: 'absolute',
+ position: 'fixed',
top: 0,
bottom: 0,
left: 0, DetailsI have implemented a simple trap focus for accessibility. I have also enabled Pros:
Cons:
ThoughtsI think with this proposal we are somewhere in the middle or above, providing the most important features / accessibility with the least drawbacks. Let me know what do you think about this. PS: If you apply the react-native-modal patch, make sure to clear the cache ( |
Thanks again @s77rt for the update and the details.
Do you mind details about the bugs? Btw per this thread we freeze reports for accessibility issues, so I don't think we want to log another tab accessibility from the CONS either. I'm bringing this issue to the team for opinions. Sorry for the back and forth. cc @srikarparsi |
@mollfpr
I have stated this behaviour somewhere else in the GH issues (where I proposed same modal modification) |
@johncschuster, @mollfpr, @srikarparsi Uh oh! This issue is overdue by 2 days. Don't forget to update your issues! |
I feel this proposal is over-engineered and too risky causing a regression. I'll deep dive into the root cause while waiting for others' proposals. Thanks! cc @srikarparsi |
The issue is related to the modals. Currently I only see that this can be fixed by not using modals in the first place as we can't force browsers behaviours. The change may seem too much but that's only due to the existing code that does the coupling of two separated prop into one ( I would say to close this as not-to-be-fixed or at least hold, but the more I investigate accessibility issues it's always about modals. Not to mention this bug |
Thanks, @s77rt for the investigation. Yes, currently we know that the problem is from the RNW modal. But the fact that it only works in Chrome is still not clear to me. I'll wait a few more days for a new proposal. |
AFAIK we said we would not be focusing on supporting firefox, so I think we should close this issue. |
@srikarparsi @johncschuster Do we want to close this issue? |
@srikarparsi @johncschuster @mollfpr |
@mollfpr No, the bug you linked is about the keyboard staying open. Kooha-2022-11-27-17-26-53.mp4 |
Whops my bad.
I've seen that bug log before, but I couldn't find it now. |
@johncschuster, @mollfpr, @srikarparsi Huh... This is 4 days overdue. Who can take care of this? |
@srikarparsi @johncschuster Should we close this because we not focusing on Firefox support? |
I'm in favor of closing the issue since it's Firefox-specific. @srikarparsi, what are your thoughts? |
@srikarparsi No it has not been reported anywhere. I was hoping to get it silently fixed here or here Should I report this to be handled separately? |
Yes, can you please do that @s77rt and I'm good to close out this issue as well @johncschuster. Most of the changes you proposed in this issue would probably still apply but it would be better to put it under the new issue since that's the problem we're solving. |
Thanks, @srikarparsi! I'll go ahead and close this issue. |
If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!
Action Performed:
Expected Result:
Highlighting should remain as it as until an action is taken
Actual Result :
It gets disappear as soon as the menu appears (working fine for other devices)
Workaround:
unknown
Platform:
Where is this issue occurring?
Version Number: 1.2.24-1
Reproducible in staging?: y
Reproducible in production?: y
Email or phone of affected tester (no customers):
Logs: https://stackoverflow.com/c/expensify/questions/4856
Notes/Photos/Videos:
Recording.864.mp4
hightlighting.issue.mp4
Expensify/Expensify Issue URL:
Issue reported by: @daraksha-dk
Slack conversation: https://expensify.slack.com/archives/C01GTK53T8Q/p1667833686198119
View all open jobs on GitHub
The text was updated successfully, but these errors were encountered: