-
Notifications
You must be signed in to change notification settings - Fork 588
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(SelectPanel): Correctly recalculate position on overflow #5562
base: main
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: c874e45 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
👋 Hi, this pull request contains changes to the source code that github/github depends on. If you are GitHub staff, we recommend testing these changes with github/github using the integration workflow. Thanks! |
size-limit report 📦
|
…ect-panel-overflow
@@ -30,22 +31,49 @@ export function useAnchoredPosition( | |||
const floatingElementRef = useProvidedRefOrCreate(settings?.floatingElementRef) | |||
const anchorElementRef = useProvidedRefOrCreate(settings?.anchorElementRef) | |||
const [position, setPosition] = React.useState<AnchorPosition | undefined>(undefined) | |||
const [_, setPrevHeight] = React.useState<number | undefined>(undefined) |
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.
used to force the height of the selectPanel when filtered and it wants to shrink but it's anchored at the top (we want it to stay at the top and lock the height)
if ( | ||
settings?.pinPosition && | ||
prev && | ||
['outside-top', 'inside-top'].includes(prev.anchorSide) && | ||
(prev.anchorSide !== newPosition.anchorSide || prev.top < newPosition.top) |
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.
if the selectPanel is anchored to the top and it used to sit higher on the page....
(prev.anchorSide !== newPosition.anchorSide || prev.top < newPosition.top) | ||
) { | ||
const anchorTop = anchorElementRef.current?.getBoundingClientRect().top ?? 0 | ||
if (anchorTop > (floatingElementRef.current?.clientHeight ?? 0)) { |
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.
if the anchorTop is bigger than the selectPanel's height, that means there is plenty of space for the selectPanel to stay at the top
if (anchorTop > (floatingElementRef.current?.clientHeight ?? 0)) { | ||
setPrevHeight(prevHeight => { | ||
if (prevHeight && prevHeight > (floatingElementRef.current?.clientHeight ?? 0)) { | ||
requestAnimationFrame(() => { |
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.
using requestAnimationFrame here to prevent a resizeObserver loop here due to us changing the element's height, but the observer listening for changes in the element...
const anchorTop = anchorElementRef.current?.getBoundingClientRect().top ?? 0 | ||
if (anchorTop > (floatingElementRef.current?.clientHeight ?? 0)) { | ||
setPrevHeight(prevHeight => { | ||
if (prevHeight && prevHeight > (floatingElementRef.current?.clientHeight ?? 0)) { |
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.
re-establish the height if it used to be bigger and is now attempting to shrink
} else { | ||
prev = newPosition |
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.
edgecase where the selectPanel is actually growing instead of shrinking, in this case we do want to allow it to recalculate/reposition
👋 Hi, there are new commits since the last successful integration test. We recommend running the integration workflow once more, unless you are sure the new changes do not affect github/github. Thanks! |
This reverts commit 8c1e8cf.
👋 Hi from github/github! Your integration PR is ready: https://github.com/github/github/pull/362265 |
🟢 golden-jobs completed with status |
👋 Hi, there are new commits since the last successful integration test. We recommend running the integration workflow once more, unless you are sure the new changes do not affect github/github. Thanks! |
@camertron cleaned up the |
Closes https://github.com/github/primer/issues/4027
Continuation of #5207.
When the selectPanel grows after opening (example: open in loading state, data is fetched, items are rendered), the position is not recalculated and may cause content to go off screen (See for more context https://github.com/github/primer/issues/4027). This PR corrects that undesired behavior by recalculating the position of the overlay on size change. Additionally, bake in logic so that the overlay retains it's size on filtering when anchored to the top (to prevent the panel from repositioning itself too many times).
before
Screen.Recording.2025-02-05.at.6.18.57.PM.mov
Screen.Recording.2025-02-05.at.6.19.47.PM.mov
after
Screen.Recording.2025-02-05.at.6.26.39.PM.mov
Screen.Recording.2025-02-05.at.6.21.45.PM.mov
Changelog
New
pinPosition
prop inAnchoredOverlay
to prevent position shifting when sitting at the top of the anchor.max-height: 100vh
default to AnchoredOverlay.width
overlay prop inSelectPanel
pinPosition
to true in SelectPanel if height hasn't been set by useruseAnchoredPosition
to watch for changes within the floating element (i.e., the overlay)Changed
useAnchoredPosition
'supdatePosition
function to prevent anchored element to unpin from the top when possible (dependent onpinPosition
) propuseResizeObserver
hookRollout strategy
Testing & Reviewing
Test new functionality in https://primer-88a2e13e51-13348165.drafts.github.io/storybook/?path=/story/components-selectpanel-examples--reposition-after-loading, https://primer-88a2e13e51-13348165.drafts.github.io/storybook/?path=/story/components-selectpanel-examples--select-panel-reposition-inside-dialog
Ensure default functionality still works as expected: https://primer-88a2e13e51-13348165.drafts.github.io/storybook/?path=/story/components-selectpanel--default
Merge checklist