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: Render Visual Refresh background height in sync with content #1331

Merged
merged 57 commits into from
Jul 28, 2023

Conversation

jperals
Copy link
Member

@jperals jperals commented Jul 14, 2023

Description

Alternative approach to #1344, a bit less orthodox (adding imperative DOM updates) but more performant.

As a long-term fix, we want to refactor app layout so that resize observer calls are not necessary to keep the content and the background in sync.

Ticket: AWSUI-21410

How has this been tested?

  • Tested manually by locally switching the package to React 18 and using Chrome developer tools (CPU throttling, performance tools). Performance measurements yielded no performance regression
  • Added page to manually test
  • Added unit tests
  • Deployed in my pipeline. Lighthouse test results report actually faster results than main:
    • Table view:
      • Score: +0.01
      • Bootup time: -118ms (-7.58%)
      • Total blocking time: -41ms (-6.38%)
    • Table inline editing view:
      • Score: +0.01
      • Bootup time: -22ms (-1.35%)
      • Total blocking time: -50ms (-7.27%)
Review checklist

The following items are to be evaluated by the author(s) and the reviewer(s).

Correctness

  • Changes include appropriate documentation updates.
  • Changes are backward-compatible if not indicated, see CONTRIBUTING.md.
  • Changes do not include unsupported browser features, see CONTRIBUTING.md.
  • Changes were manually tested for accessibility, see accessibility guidelines.

Security

Testing

  • Changes are covered with new/existing unit tests?
  • Changes are covered with new/existing integration tests?

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@jperals jperals changed the title Fix/dynamic overlap sync fix: Render dynamic overlap in sync Jul 14, 2023
@codecov
Copy link

codecov bot commented Jul 14, 2023

Codecov Report

Patch coverage: 100.00% and project coverage change: +0.01% 🎉

Comparison is base (51c36a3) 93.47% compared to head (0bc9b7e) 93.48%.
Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1331      +/-   ##
==========================================
+ Coverage   93.47%   93.48%   +0.01%     
==========================================
  Files         624      625       +1     
  Lines       16778    16810      +32     
  Branches     5550     5561      +11     
==========================================
+ Hits        15683    15715      +32     
  Misses       1022     1022              
  Partials       73       73              
Files Changed Coverage Δ
src/app-layout/visual-refresh/layout.tsx 100.00% <ø> (ø)
src/app-layout/visual-refresh/background.tsx 85.71% <100.00%> (ø)
src/app-layout/visual-refresh/context.tsx 90.38% <100.00%> (+0.87%) ⬆️
...nal/hooks/container-queries/use-resize-observer.ts 100.00% <100.00%> (ø)
src/internal/hooks/container-queries/utils.ts 100.00% <100.00%> (ø)
src/internal/hooks/use-dynamic-overlap/index.ts 100.00% <100.00%> (ø)

... and 4 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

jkuelz and others added 14 commits July 17, 2023 14:37
…ize (#1311)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Abdallah AlHalees <[email protected]>
Co-authored-by: Timo <[email protected]>
expect(isDynamicOverlapDisabled()).toBe('false');
});

test('sets dynamic overlap when height is higher than 0', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we have a test case where the height dynamically changes (for example, from 800 to 0) and we check for the expected values of isDynamicOverlapSet and isDynamicOverlapDisabled?

Or is this not something that can happen?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's not impossible that these state variables change during the lifecycle of an application but I think it should be rather rare. Some of these events should happen:

  1. The height of a child component that uses the dynamic overlap hook changes from 0 to non-0, or the other way around from non-0 to 0
  2. A child component that uses the dynamic overlap hook is added or removed
  3. contentHeader is passed and then not passed anymore, or the other way around
  4. The value of the disableDynamicOverlap property passed to the app layout changes

I just added a test that checks these state variables after re-rendering, based on the case 2 above. Case 1 would be tricky to test because we would need to simulate a call to the resize observer hook.

Copy link
Contributor

Choose a reason for hiding this comment

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

Alright, that makes sense!

element?.style.setProperty(customCssProps.overlapHeight, `${height}px`);
}
},
[layoutElement, props.contentHeader, props.disableContentHeaderOverlap]
Copy link
Member

Choose a reason for hiding this comment

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

props.contentHeader is a ReactNode, which is rarely stable between renders anyway. This means the function will update identity each render, making useCallback useless.

If it's just provided to check for the existence of the property rather than the contents, maybe you could define a variable outside the function and use it internally? That would make it safer.

const hasContentHeader = !!props.contentHeader;
useCallback(() => { ... }, [layoutElement, hasContentHeader, props.disableContentHeaderOverlap]);

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated as per your suggestion.

* Using React state for `isDynamicOverlapSet` and `isDynamicOverlapDisabled` is less problematic
* because they will rarely change within the lifecycle of an application.
*/
const element = typeof layoutElement === 'function' ? layoutElement(null) : layoutElement?.current;
Copy link
Member

Choose a reason for hiding this comment

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

Not sure what layoutElement(null) does here. It just unsets the ref? I think it works here just because of internal implementation details, since layoutElement happens to be an ObjectRef usually. If we're using the ref for things other than resize observer, maybe another ref property (typed as ObjectRef) is needed.

Copy link
Member Author

Choose a reason for hiding this comment

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

This variable is returned by useContainerQuery which returns React.Ref<any>, which in turn is RefCallback<any> | RefObject<any> | null, but its value is set only by the Layout component here so we know it's going to be an ObjectRef and not a RefCallback.

If it looks better, we could as well just do nothing if it is a function.

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated.

Copy link
Member

Choose a reason for hiding this comment

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

Not ideal, but it does have a comment and it works for now. Not going to treat it as blocking.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should layoutElement somehow end up being used as a RefCallback, it will also cause this useCallback to recreate the updateDynamicOverlapHeight function on every render.

Anyway, it seems unlikely, so it is also non-blocking for me.


useLayoutEffect(
function handleDynamicOverlapHeight() {
const getElement = useCallback(() => overlapElementRef.current, [overlapElementRef]);
Copy link
Member

Choose a reason for hiding this comment

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

😭 I'm so not a fan of this ResizeObserver hook API

@jperals jperals merged commit 254fbb1 into main Jul 28, 2023
24 checks passed
@jperals jperals deleted the fix/dynamic-overlap-sync branch July 28, 2023 05:54
jperals added a commit that referenced this pull request Jul 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants