Skip to content

Commit

Permalink
Merge pull request #195 from primer/special-case-anchored-position-fo…
Browse files Browse the repository at this point in the history
…r-top-layer-elements

special case anchored-position for top-layer elements
  • Loading branch information
keithamus committed May 11, 2023
2 parents 9dacc0b + bddd6c5 commit 3f2f45a
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/clean-hornets-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/behaviors': patch
---

Special case anchored-position calls on top-layer elements
21 changes: 21 additions & 0 deletions src/__tests__/anchored-position.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,27 @@ describe('getAnchoredPosition', () => {
expect(left).toEqual(300)
})

it('returns the correct position in the case of top-layer elements', () => {
const anchorRect = makeDOMRect(300, 200, 50, 50)
const parentRect = makeDOMRect(800, 600, 50, 50)
const floatingRect = makeDOMRect(NaN, NaN, 100, 100)
document.body.innerHTML = '<div><dialog id="float"></dialog><div id="anchor"></div></div>'
const parent = document.querySelector('div')!
parent.style.overflow = 'hidden'
parent.style.position = 'relative'
const float = document.querySelector('#float')!
const anchor = document.querySelector('#anchor')!
parent.getBoundingClientRect = () => parentRect
float.getBoundingClientRect = () => floatingRect
anchor.getBoundingClientRect = () => anchorRect
document.body.getBoundingClientRect = () => makeDOMRect(0, 0, 1920, 0)
Object.defineProperty(window, 'innerHeight', {get: () => 1080})
const settings: Partial<PositionSettings> = {anchorOffset: 4}
const {top, left} = getAnchoredPosition(float, anchor, settings)
expect(top).toEqual(254)
expect(left).toEqual(300)
})

it('returns the correct position in the default case with no overflow, inside a clipping parent', () => {
const parentRect = makeDOMRect(20, 20, 500, 500)
const anchorRect = makeDOMRect(300, 200, 50, 50)
Expand Down
18 changes: 18 additions & 0 deletions src/anchored-position.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export function getAnchoredPosition(
* position is not "static", or document.body, whichever is closer
*/
function getPositionedParent(element: Element) {
if (isOnTopLayer(element)) return document.body
let parentNode = element.parentNode
while (parentNode !== null) {
if (parentNode instanceof HTMLElement && getComputedStyle(parentNode).position !== 'static') {
Expand All @@ -179,6 +180,23 @@ function getPositionedParent(element: Element) {
return document.body
}

/**
* Returns true if the element is likely to be on the `top-layer`.
*/
function isOnTopLayer(element: Element) {
if (element.tagName === 'DIALOG') {
return true
}
try {
if (element.matches(':popover-open')) {
return true
}
} catch {
return false
}
return false
}

/**
* Returns the rectangle (relative to the window) that will clip the given element
* if it is rendered outside of its bounds.
Expand Down

0 comments on commit 3f2f45a

Please sign in to comment.