-
Notifications
You must be signed in to change notification settings - Fork 1.2k
⚗️ [Popover] Expose imperative relayout function #4385
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
Conversation
src/components/Popover/Popover.tsx
Outdated
| } | ||
|
|
||
| interface PopoverSubcomponents { | ||
| // TODO: How can we make these required props? |
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.
This appears to be a difficult type to wrangle... and there major issues with me setting these to optional (?), or is there another way I can type this to keep these subcomponents as required?
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 Curtis 👋🏽 I played around with this and ended up 🔥 the PopoverSubcomponents interface and trying the second answer to this StackOverflow question and it works locally.
const PopoverNamespace = Object.assign(Popover, {Pane, Section});
export {PopoverNamespace as Popover};I'm having trouble running web right now so I haven't been able to test on a consuming app (the comment on line 83 I'm guessing means this was a problem in the past 🤔 )
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.
These shouldn't be made optional. I'd go with something along the lines of what @chloerice suggested (assigning Pane and Section to Popover after you've stored a reference to the ref forwarded component in a separate variable):
export const Popover = Object.assign(PopoverComponentWithForwardedRef, {Pane, Section});59f9a6c to
7e56a25
Compare
clauderic
left a comment
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.
Looks good overall. There's a significant amount of ref drilling going on, but there aren't other good solutions around it based on how these components are structured so LGTM.
src/components/Popover/Popover.tsx
Outdated
| ...rest | ||
| }: PopoverProps) { | ||
| export interface PopoverHandles { | ||
| forceReLayout(): void; |
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.
naming nitpick:
| forceReLayout(): void; | |
| forceUpdatePosition(): void; |
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.
The only point of push back I'd offer is that this also handles dimensions - so it is more than just "position". However, I'm okay with the name change, as under the hood it is calling handleMeasurement and that itself isn't the most explicit name.
src/components/Popover/Popover.tsx
Outdated
| zIndexOverride, | ||
| ...rest | ||
| }: PopoverProps) { | ||
| export interface PopoverHandles { |
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.
How about something like:
| export interface PopoverHandles { | |
| export interface PopoverPublicAPI { |
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.
*ComponentName*Handles actually seems to be a Polaris convention - having looked at other components that use forwardRef. However, I agree that PublicAPI is more explicit. It better communicates this types' responsibility (although you could argue that Props also belong within a "public API").
I'll go ahead and make the change, and if anyone from Polaris would like me to revert, I am okay with that.
src/components/Popover/Popover.tsx
Outdated
| > & | ||
| PopoverSubcomponents; | ||
|
|
||
| export const Popover: PopoverComponentType = forwardRef(function Popover( |
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.
| export const Popover: PopoverComponentType = forwardRef(function Popover( | |
| const PopoverComponent = forwardRef<PopoverPublicAPI, PopoverPops>((props, ref) => { |
| // Consumer's may also need to setup their own timers for | ||
| // triggering forceReLayout() `children` use animation. | ||
| // Ideally, forceReLayout() is fired at the end of a transition event. | ||
| requestAnimationFrame(() => this.handleMeasurement()); |
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.
nit:
| requestAnimationFrame(() => this.handleMeasurement()); | |
| requestAnimationFrame(this.handleMeasurement); |
src/components/Popover/Popover.tsx
Outdated
| } | ||
|
|
||
| interface PopoverSubcomponents { | ||
| // TODO: How can we make these required props? |
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.
These shouldn't be made optional. I'd go with something along the lines of what @chloerice suggested (assigning Pane and Section to Popover after you've stored a reference to the ref forwarded component in a separate variable):
export const Popover = Object.assign(PopoverComponentWithForwardedRef, {Pane, Section});92e6dc8 to
ba6d8b0
Compare
size-limit report
|
|
@clauderic + @chloerice thanks so much for the review! I've addressed all of your feedback and I think this should be ready for final review 😄 |
46ee6cd to
e96b2bf
Compare
| activatorContainer.current; | ||
| if (!focusNextFocusableNode(focusableActivator, isInPortal)) { | ||
| focusableActivator.focus(); | ||
| useImperativeHandle(ref, () => { |
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.
For anyone wondering - I've barely changes this file, but since we now wrap with a forwardRef, the indentation/formatting got updated and therefor its hard to really parse exact lines of code that have changed 😭
|
The |
This PR continues to try and find a solution to my struggles with getting
Popoverto re-render its size dimensions while toggling nestedCollapsiblecomponents.There have been two previous attempts:
The final attempt has ended in numerous Polaris tests (which use
Popoversomewhere under the hood) stalling in what appears to be an endless re-render cycle. Something about theattributesMutationObserver is causing certain tests to never complete.Some additional conversation can be found here:
https://shopify.slack.com/archives/C4Y8N30KD/p1628608394090200
Before I start fighting through each of these tests to attempt a resolution, I want us to first entertain this alternative approach.
Solution
This was first suggested by @clauderic - which is to offer an imperative API which enables consumer's to dictate exactly when they might need to re-render the
Overlaydimensions.I have wrapped
PopoverwithforwardRefand exposed aforceReLayout()function viauseImperativeHandle. Consumer's can retrieve thePopoverinstance by way ofref, and callpopoverRef.current.forceReLayout();whenever they need to update the dimensions.This actually calls through multiple component layers to get at the code which handles updating measurements:
Popover > PopoverOverlay > PositionedOverlay.It certainly is not the most elegant solution, but it hopefully leads us to a more explicit result. Rather than forcing the
MutationObserverto watch for additional attributes and potentially incur some ramification, we can put control in the hands of consumer's and request a layout update on command.Reviewers
I very much appreciate any review / advice reviewers can offer, as we are in desperate need of this functionality in Online Store. Some questions to consider:
Please tophat this PR using the following snippet:
Playground snippet: