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

[Security Solution][Resolver] Update the resolver element ref on scroll events if the position of the element has changed within the page #72461

Merged

Conversation

kqualters-elastic
Copy link
Contributor

Summary

Fixes an issue in the useCamera hook where the useAutoUpdatingClientRect hook returned a reference to the resolver dom element that was stale, namely the top/y & left/x properties that are used by the zoom action.
BEFORE:
resolver_zoom_broken
AFTER:
resolver_zoom_fixed

For maintainers

@kqualters-elastic kqualters-elastic requested review from a team as code owners July 20, 2020 15:26
@kqualters-elastic kqualters-elastic added v7.9.0 v8.0.0 release_note:skip Skip the PR/issue when compiling release notes Feature:Endpoint Elastic Endpoint feature labels Jul 20, 2020
@elasticmachine
Copy link
Contributor

Pinging @elastic/endpoint-app-team (Feature:Endpoint)

@kqualters-elastic kqualters-elastic added the Team:Endpoint Data Visibility Team managing the endpoint resolver label Jul 20, 2020
@elasticmachine
Copy link
Contributor

Pinging @elastic/endpoint-data-visibility-team (Team:Endpoint Data Visibility)

@kqualters-elastic kqualters-elastic changed the title Update the resolver element ref on scroll events if the position of the element has changed within the page [Security Solution] Update the resolver element ref on scroll events if the position of the element has changed within the page Jul 20, 2020
@kqualters-elastic kqualters-elastic changed the title [Security Solution] Update the resolver element ref on scroll events if the position of the element has changed within the page [Security Solution][Resolver] Update the resolver element ref on scroll events if the position of the element has changed within the page Jul 20, 2020
useEffect(() => {
window.addEventListener('scroll', handleScroll, { passive: true });
return () => {
window.removeEventListener('scroll', handleScroll);
Copy link
Contributor

@bkimmel bkimmel Jul 20, 2020

Choose a reason for hiding this comment

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

(edit) ❔ I think you might also need to supply { passive: true } here for it to treat it as a "matching" signature and do the removal? I'll test and get back to you in a sec on that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no, passive is only a possible value for the options object in addEventListener, not removeEventListener

Copy link
Contributor

Choose a reason for hiding this comment

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

OK, nevermind looks like that's just for useCapture / {capture: true}

Copy link
Contributor

Choose a reason for hiding this comment

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

The advice for this on MDN reads as:

It's worth noting that some browser releases have been inconsistent on this, and unless you have specific reasons otherwise, it's probably wise to use the same values used for the call to addEventListener() when calling removeEventListener().

https://wiki.developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener

Copy link
Contributor

Choose a reason for hiding this comment

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

You can leave this one be. I'm not sure how relevant that advice on MDN is anymore - probably "not very".

const ref = useCallback((node: Element | null) => {
setCurrentNode(node);
if (node !== null) {
setRect(node.getBoundingClientRect());
}
}, []);
const { ResizeObserver } = useContext(SideEffectContext);
const handleScroll = useCallback(() => {
if (currentX !== window.scrollX || currentY !== window.scrollY) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🔴 might want to wrap this in a requestAnimationFrame b/c I think when you query scrollX, scrollY, and getBoundingClientRect it forces layout sync

Copy link
Contributor

Choose a reason for hiding this comment

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

re: scrollX, scrollY, getBoundingClientRect :

https://gist.github.com/paulirish/5d52fb081b3570c81e3a

Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible useCallback already does this for you under the hood? Seems like something @oatkiller would know.

Copy link
Contributor

Choose a reason for hiding this comment

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

@bkimmel can you explain how running that in raf would help?

Copy link
Contributor

Choose a reason for hiding this comment

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

Reading scrollX, scrollY and getBoundingClientRect all force sync layout according to: https://gist.github.com/paulirish/5d52fb081b3570c81e3a Since while this is scrolling, it will get called rapidly, wrapping it in a rAF may reduce layout thrashing. Otherwise it seems like we'd be in a tight loop of this code forcing layout calculations and changing them in quick succession. The rAF doesn't solve everything, but it at least aligns the queries on scrollX etc. until after the current layout has been calculated... so I'd expect it to be at least a little better. Is there a reason you know of or something I'm missing that would make it OK in this case?

@bkimmel
Copy link
Contributor

bkimmel commented Jul 20, 2020

Finished review: 1 🔴 and 1 ❔ / discussion. I'm on track for a thumb here the 🔴 is addressed.

@@ -284,14 +284,30 @@ export function useAutoUpdatingClientRect(): [DOMRect | null, (node: Element | n
const [rect, setRect] = useState<DOMRect | null>(null);
// Using state as ref.current update does not trigger effect hook when reset
const [currentNode, setCurrentNode] = useState<Element | null>(null);

const [currentY, setCurrentY] = useState<number>(0);
Copy link
Contributor

Choose a reason for hiding this comment

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

should you initialize this to the value by reading it from the dom?

  const [currentY, setCurrentY] = useState<number>(() => window.scrollY);
  const [currentX, setCurrentX] = useState<number>(() => window.scrollX);

if (currentX !== window.scrollX || currentY !== window.scrollY) {
if (currentNode !== null) {
setRect(currentNode.getBoundingClientRect());
setCurrentY(window.scrollY);
Copy link
Contributor

Choose a reason for hiding this comment

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

do you need to use state for currentY and currentX? If they change but setRect doesn't, there shouldn't be any need to render right?
how about making it like:

// assuming currentY and currentX are now refs
currentY.current = window.scrollY
currentX.current = window.scrollX
setRect(currentNode.getBoundingClientRect());

const ref = useCallback((node: Element | null) => {
setCurrentNode(node);
if (node !== null) {
setRect(node.getBoundingClientRect());
}
}, []);
const { ResizeObserver } = useContext(SideEffectContext);
const handleScroll = useCallback(() => {
if (currentX !== window.scrollX || currentY !== window.scrollY) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@bkimmel can you explain how running that in raf would help?

const currentY = window.scrollY;

if (currentNode !== null && (previousX !== currentX || previousY !== currentY)) {
setRect(currentNode.getBoundingClientRect());
Copy link
Contributor

Choose a reason for hiding this comment

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

future improvement: you could check that the new DOMRect is different before setting it. this would prevent useless rerenders.

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Build metrics

async chunks size

id value diff baseline
securitySolution 7.3MB +728.0B 7.3MB

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@kqualters-elastic kqualters-elastic merged commit c3263aa into elastic:master Jul 20, 2020
@kqualters-elastic kqualters-elastic deleted the resolver-scroll-zoom-fix branch July 20, 2020 21:41
gmmorris added a commit to gmmorris/kibana that referenced this pull request Jul 21, 2020
* master: (28 commits)
  allow some env settings for ingest manager (elastic#72544)
  Add inspector for VEGA (elastic#70941)
  chore(NA): fix grunt task for test:coverage (elastic#72539)
  Archive e2e test results in ES (elastic#72575)
  preserve 401 errors from new es client (elastic#71248)
  [SIEM][Detections] Updates text for severity and risk_score overrides  (elastic#72244)
  fixing error occurences tooltip (elastic#72425)
  use KibanaClient interface instead of Client for new client interface (elastic#72388)
  [APM] Handle ML errors (elastic#72316)
  [Discover] Improve histogram tests (elastic#72235)
  [ftr/webdriver] retry on all errors, use Rx so that timers are canceled (elastic#72540)
  [pre-req] Move .storybook to storybook; standardize files (elastic#72384)
  [Security_Solution][Resolver][Bug]: Restore breadcrumb background (elastic#72538)
  [ML] Fix annotation detector linking & delayed_data(0) (elastic#72468)
  [Security Solution][Exceptions] - Make esTypes and subType available to index patterns (elastic#72336)
  [SIEM] Uses faster wait from testing-library and removes duplicate older wait idiom (elastic#72509)
  Fix long combo box items breaking out of flex item width (elastic#72512)
  [pipeline/commitStatus] update commit status in baseline-capture job (elastic#72366)
  [Security Solution][Resolver] Update the resolver element ref on scroll events if the position of the element has changed within the page (elastic#72461)
  [Maps] auto-fit to data bounds (elastic#72129)
  ...
kqualters-elastic added a commit that referenced this pull request Jul 21, 2020
…n scroll events if the position of the element has changed within the page (#72461) (#72543)
kqualters-elastic added a commit that referenced this pull request Jul 22, 2020
…n scroll events if the position of the element has changed within the page (#72461) (#72790)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:Endpoint Elastic Endpoint feature release_note:skip Skip the PR/issue when compiling release notes Team:Endpoint Data Visibility Team managing the endpoint resolver v7.9.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants