diff --git a/src/Dom/findDOMNode.ts b/src/Dom/findDOMNode.ts index 288ec01a..0b90b9b8 100644 --- a/src/Dom/findDOMNode.ts +++ b/src/Dom/findDOMNode.ts @@ -1,9 +1,10 @@ -import React from 'react'; +import type React from 'react'; export function isDOM(node: any): node is HTMLElement | SVGElement { - // https://developer.mozilla.org/en-US/docs/Web/API/Element - // Since XULElement is also subclass of Element, we only need HTMLElement and SVGElement - return node instanceof HTMLElement || node instanceof SVGElement; + // Cross-frame safe check: `instanceof` fails for elements created in a + // different frame (e.g. iframe via createPortal) because each frame has its + // own HTMLElement/SVGElement constructors. + return typeof node === 'object' && node !== null && node.nodeType === 1; } /** diff --git a/src/Dom/isVisible.ts b/src/Dom/isVisible.ts index 13dea10c..67badcef 100644 --- a/src/Dom/isVisible.ts +++ b/src/Dom/isVisible.ts @@ -3,7 +3,7 @@ export default (element: Element): boolean => { return false; } - if (element instanceof Element) { + if (element.nodeType === 1) { if ((element as HTMLElement).offsetParent) { return true; } diff --git a/tests/findDOMNode.test.tsx b/tests/findDOMNode.test.tsx index 0856b81a..973e7a76 100644 --- a/tests/findDOMNode.test.tsx +++ b/tests/findDOMNode.test.tsx @@ -63,6 +63,27 @@ describe('findDOMNode', () => { expect(findDOMNode(svg)).toBe(svg); }); + it('supports DOM nodes from iframe', () => { + const iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + + try { + const iframeWindow = iframe.contentWindow!; + const iframeDocument = iframeWindow.document; + const div = iframeDocument.createElement('div'); + iframeDocument.body.appendChild(div); + + expect(div).toBeInstanceOf(iframeWindow.HTMLElement); + expect(div).not.toBeInstanceOf(HTMLElement); + expect(isDOM(div)).toBe(true); + expect(getDOM(div)).toBe(div); + expect(findDOMNode(div)).toBe(div); + expect(getDOM({ nativeElement: div })).toBe(div); + } finally { + document.body.removeChild(iframe); + } + }); + it('isDOM type', () => { const svg: any = document.createElementNS( 'http://www.w3.org/2000/svg', diff --git a/tests/isVisible.test.ts b/tests/isVisible.test.ts new file mode 100644 index 00000000..bb2d474d --- /dev/null +++ b/tests/isVisible.test.ts @@ -0,0 +1,33 @@ +import isVisible from '../src/Dom/isVisible'; + +describe('isVisible', () => { + it('supports DOM nodes from iframe', () => { + const iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + + try { + const iframeWindow = iframe.contentWindow!; + const iframeDocument = iframeWindow.document; + const div = iframeDocument.createElement('div'); + iframeDocument.body.appendChild(div); + + jest.spyOn(div, 'getBoundingClientRect').mockReturnValue({ + bottom: 10, + height: 10, + left: 0, + right: 10, + toJSON: () => {}, + top: 0, + width: 10, + x: 0, + y: 0, + }); + + expect(div).toBeInstanceOf(iframeWindow.HTMLElement); + expect(div).not.toBeInstanceOf(Element); + expect(isVisible(div)).toBe(true); + } finally { + document.body.removeChild(iframe); + } + }); +});