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(@react-aria/overlays): prevent exception on Cypress #2341
fix(@react-aria/overlays): prevent exception on Cypress #2341
Conversation
let target = e.target as HTMLElement; | ||
if (!triggerRef.current || !target.contains(triggerRef.current)) { | ||
let target = e.target; | ||
if (!triggerRef.current || !(target instanceof HTMLElement) || !target.contains(triggerRef.current)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this prevent our overlays from automatically closing when the user scrolls the window?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. If target is an instance of HTMLElement
then it behaves as before, and if it's not, it just prevents a runtime error which would happen by accessing .contains
On something that doesn't have it.
Technically, the check could be on Node
rather than HTMLElement
but in IE, it's defined only on HTMLElement instances, and I thought there ia no harm in checking on HTMLElement, even though IE is not supported by react-aria
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I tested this out locally in our storybook via our DialogTrigger "type: popover" story + making the body scrollable and it no longer closes the overlay on scroll.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps
if (!triggerRef.current || !(target instanceof HTMLElement) || !target.contains(triggerRef.current)) { | |
if (!triggerRef.current || ((target instanceof HTMLElement) && !target.contains(triggerRef.current))) { |
since window.scroll is a valid call. Thanks @snowystinger for pointing the above out
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. The reason that's broken is that target can be document
which is not an instance of HTMLElement
. .contains
is actually on Node
's prototype and document is an instance of Node
. It's only in IE9 that .contains
is on HTMLElement
's prototype. So I think The right change would be to check if the target is an instance of Node
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pushed some update that should fix both the issue in cypress and the regression.
Note that while the suggested change here fix the regression, and also fixes the runtime error on cypress, it doesn't completely solve the issue on cypress. For some reason, the screenshot
command in cypress triggers an artificial scroll event where the target is set to window
. Checking with instanceof Node
and bailing out otherwise would skip unwanted closure in cypress and works in other cases too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the regression BTW, and thanks for catching it. I shouldn't have considered IE9 in the first place :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha, no worries, thanks for the fix!
Cypress screenshot command triggers a scroll event where the event target is window, not a DOM element. closes adobe#2340
dafc3ce
to
8be7d91
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, though we should probably have a test to prevent that regression found in the future
would you mind leaving a copy of the code that caused the regression so we can write a test against it?
Good point @snowystinger |
related to adobe#2340. The first test case is related to the regression mentioned here: adobe#2341 (comment) The second test case captures the fix for the cypress issue.
c5f5266
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, thanks again for submitting this. We had a team talk about the issue and we'd like for window scroll events to behave the same as the document scroll. I know the previous implementation was blowing up due to the window scroll, and that is still something that should be fixed.
See #1852 (comment) and #900 for more information on why closing overlays on scroll is important for us.
There are cases other than Cypress where the window may be the target of the scroll event.
Thanks again for your patience, if you don't have time to make the change, I'm happy to do it. Just let me know
expect(onClose).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should not throw or close when target is window in a scroll event', function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so we'll want this test to be the same as the document scroll test, still good to have both tests
@snowystinger No problem. So if we want to close on window scroll events too, considering that |
I'll be honest, I'm having trouble figuring out if window.scroll is valid. It's certainly available to JS, which is what Cypress is taking advantage of. But is Cypress the only one taking advantage of it? I don't know. We think it's safer if it behaves in the same expected manner as document.scroll. The real fix that we eventually want to make is probably an invisible underlay for all of our popover type components, this would block any scrolling and then we'd be able to drop this scroll listener. This is coming from the comment I linked earlier #1852 (comment) |
Cypress screenshot command triggers a scroll event where the event target is
window
, not a DOM element.Closes #2340
✅ Pull Request Checklist:
📝 Test Instructions:
Let me know if needed.