vanillajs-browser-helpers
Version:
Collection of convenience code snippets (helpers) that aims to make it a little easier to work with vanilla JS in the browser
159 lines (134 loc) • 5.03 kB
text/typescript
import { appendFrame, byId, createDetachedDocument, generateId, getOne, insertHtml, removeElement } from './assets/helpers';
import scrollParent from '../scrollParent';
const scrollerID = generateId('scroller');
const childID = generateId('child');
const style = 'with: 10px; height: 10px;';
describe('"scrollParent', () => {
const scrollElm = (doc: Document) => doc.documentElement;
describe.each([
['<html> element', (doc: Document) => doc.documentElement],
['<body> element', (doc: Document) => doc.body]
])('%s, Returns document.scrollingElement (viewport)', (_, getElm) => {
it('In the current document', () => {
const doc = document;
expect(scrollParent(getElm(doc))).toBe(scrollElm(doc));
});
it('In a Frame document', () => {
const frame = appendFrame();
const doc = frame.contentDocument as Document;
expect(scrollParent(getElm(doc))).toBe(scrollElm(doc));
frame.remove();
});
it('A DOM Node in a detached Document', () => {
const doc = createDetachedDocument();
expect(scrollParent(getElm(doc))).toBe(scrollElm(doc));
});
});
describe.each([
['Single element', `<div class="root" id="${childID}" style="${style}" />`],
['"fixed" Element', `<div class="root" id="${childID}" style="${style}position: fixed"></div>`],
['Element inside "overflow: hidden;" element', `
<div class="root" style="${style}position: relative; overflow: hidden">
<div id="${childID}" />
</div>
`],
['Element inside "relative" Element', `
<div class="root" style="${style}">
<div style="position: relative">
<div id="${childID}" />
</div>
</div>
`],
['"absolute" Element inside an Element with "overflow"', `
<div class="root" style="${style}">
<div style="${style}position: static; overflow: auto">
<div id="${childID}" style="${style}position: absolute" />
</div>
</div>
`],
['"fixed" Element inside an Element with "overflow"', `
<div class="root" style="${style}overflow: auto">
<div id="${childID}" style="${style}position: fixed" />
</div>
`]
])('%s: Returns document.scrollingElement (viewport)', (_, HTML) => {
function suite(doc: Document) {
insertHtml(HTML, doc.body);
expect(scrollParent(byId(childID, doc))).toBe(scrollElm(doc));
removeElement(getOne('.root', doc), doc);
}
it('In the current document', () => {
suite(document);
});
it('In a Frame document', () => {
const frame = appendFrame();
const doc = frame.contentDocument as Document;
suite(doc);
frame.remove();
});
it('A DOM Node in a detached Document', () => {
const doc = createDetachedDocument();
suite(doc);
});
});
describe('Returns the nearest element with scrolling overflow when', () => {
describe.each([
['Element is direct child', `
<div class="root" id="${scrollerID}" style="${style}">
<div id="${childID}" style="${style}" />
</div>
`],
['Element is child of "relative" child', `
<div class="root" id="${scrollerID}">
<div style="${style}position: relative">
<div id="${childID}" style="${style}" />
<div>
</div>
`],
['It is "relative" and element is "absolute"', `
<div class="root" id="${scrollerID}" style="${style}position: relative">
<div style="${style}position: static; overflow: auto">
<div id="${childID}" style="${style}position: absolute" />
<div>
</div>
`],
['It is "relative" and element is "absolute" (skipping no scrolling "relative" parent)', `
<div class="root" id="${scrollerID}" style="${style}position: relative">
<div style="${style}position: static; overflow: auto">
<div style="${style}position: relative">
<div id="${childID}" style="${style}position: absolute" />
</div>
<div>
</div>
`],
['Itself is a child of a relative scrolling element', `
<div class="root">
<div style="${style}position: relative; overflow: auto">
<div id="${scrollerID}" style="${style}position: relative">
<div id="${childID}" style="${style}position: absolute" />
</div>
<div>
</div>
`]
])('%s', (_, HTML) => {
let scroller: HTMLElement;
let child: HTMLElement;
let root: Element;
beforeEach(() => {
insertHtml(HTML);
scroller = byId(scrollerID);
child = byId(childID);
root = getOne('.root');
});
afterAll(() => {
removeElement(root);
});
describe.each(['overflow', 'overflowX', 'overflowY'] as const)('with: %s', (overflow) => {
it.each(['auto', 'scroll'] as const)('%s', (scroll) => {
scroller.style[overflow] = scroll;
expect(scrollParent(child)).toBe(scroller);
});
});
});
});
});