Skip to content

Commit

Permalink
fix: compute iframe parents
Browse files Browse the repository at this point in the history
  • Loading branch information
92thunder committed Jan 30, 2020
1 parent eae3654 commit 4041747
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 6 deletions.
5 changes: 5 additions & 0 deletions integration/__snapshots__/iframe.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@ Array [
"left": 0,
"top": 116,
},
Object {
"el": "html",
"left": 0,
"top": 0,
},
]
`;
11 changes: 11 additions & 0 deletions integration/__snapshots__/iframe_outside_viewport.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`target in iframe is outside viewport should scroll top window 1`] = `
Array [
Object {
"el": "html",
"left": 0,
"top": 366,
},
]
`;
2 changes: 1 addition & 1 deletion integration/iframe.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('scrollable element is "overflow: visible" but hidden by iframe', () =>
})
.map(window.mapActions)
})
expect(actual).toHaveLength(1)
expect(actual).toHaveLength(2)
expect(actual[0]).toMatchObject({ el: 'html' })
expect(actual).toMatchSnapshot()
})
Expand Down
12 changes: 12 additions & 0 deletions integration/iframe_outside_viewport.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<script src="../umd/compute-scroll-into-view.js"></script>
<script src="./utils.js"></script>

<div style="height: 100vh"></div>

<iframe srcdoc="
<div class='target' style='background: crimson; height: 100px; width: 100px;'></div>
">
</iframe>
21 changes: 21 additions & 0 deletions integration/iframe_outside_viewport.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
beforeAll(async () => {
await page.goto('http://localhost:3000/integration/iframe_outside_viewport')
})

describe('target in iframe is outside viewport', () => {
test('should scroll top window', async () => {
expect.assertions(3)
const actual = await page.evaluate(() => {
const iframe = document.querySelector('iframe')
const target = iframe.contentDocument.querySelector('.target')
return window
.computeScrollIntoView(target, {
scrollMode: 'always',
})
.map(window.mapActions)
})
expect(actual).toHaveLength(1)
expect(actual[0]).toMatchObject({ el: 'html' })
expect(actual).toMatchSnapshot()
})
})
46 changes: 41 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,14 @@ interface CustomScrollAction {
}

// @TODO better shadowdom test, 11 = document fragment
function isElement(el: any) {
function isElement(el: any): el is Element {
return el != null && typeof el === 'object' && el.nodeType === 1
}

function isDocument(el: any): el is Document {
return el != null && typeof el === 'object' && el.nodeType === 9
}

function canOverflow(
overflow: string | null,
skipOverflowHiddenElements?: boolean
Expand Down Expand Up @@ -236,6 +240,33 @@ function alignNearest(
return 0
}

/**
* Get rect considering iframe
*/
function getRect(target: Element) {
const clientRect = target.getBoundingClientRect()
const rect = {
height: clientRect.height,
width: clientRect.width,
top: clientRect.top,
left: clientRect.left,
bottom: clientRect.bottom,
right: clientRect.right,
}
const doc = target.ownerDocument
let childWindow = doc?.defaultView

while (childWindow && window.top !== childWindow) {
rect.top += childWindow.frameElement.getBoundingClientRect().top
rect.left += childWindow.frameElement.getBoundingClientRect().left
rect.bottom += childWindow.frameElement.getBoundingClientRect().top
rect.right += childWindow.frameElement.getBoundingClientRect().left
childWindow = childWindow.parent
}

return rect
}

export default (target: Element, options: Options): CustomScrollAction[] => {
const {
scrollMode,
Expand All @@ -260,9 +291,14 @@ export default (target: Element, options: Options): CustomScrollAction[] => {
// Collect all the scrolling boxes, as defined in the spec: https://drafts.csswg.org/cssom-view/#scrolling-box
const frames: Element[] = []
let cursor = target
while (isElement(cursor) && checkBoundary(cursor)) {
// Move cursor to parent
cursor = cursor.parentNode as Element
while ((isElement(cursor) || isDocument(cursor)) && checkBoundary(cursor)) {
if (isDocument(cursor)) {
const document = cursor
cursor = document.defaultView?.frameElement as Element
} else {
// Move cursor to parent
cursor = cursor.parentNode as Element
}

// Stop when we reach the viewport
if (cursor === scrollingElement) {
Expand Down Expand Up @@ -308,7 +344,7 @@ export default (target: Element, options: Options): CustomScrollAction[] => {
right: targetRight,
bottom: targetBottom,
left: targetLeft,
} = target.getBoundingClientRect()
} = getRect(target)

// These values mutate as we loop through and generate scroll coordinates
let targetBlock: number =
Expand Down

0 comments on commit 4041747

Please sign in to comment.