Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: test
on: [push]
jobs:
build:
name: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '12'
- run: npm install yarn
- run: make check
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
- core dom: Add ``querySelectorAllAndMe`` to do a querySelectorAll including the starter element.
- core dom: Add ``wrap`` wrap an element with a wrapper element.
- core dom: Add ``hide`` and ``show`` for DOM elements which retain the original display value.
- core dom: Add ``find_parents`` to find all parents of an element matching a CSS selector.
- core dom: Add ``find_scoped`` to search for elements matching the given selector within the current scope of the given element
- core dom: Add ``is_visible`` to check if an element is visible or not.
unless an ``id`` selector is given - in that case the search is done globally.
- pat date picker: Support updating a date if it is before another dependent date.
- pat tabs: Refactor based on ``ResizeObserver`` and fix problems calculating the with with transitions.
- pat tabs: When clicking on the ``extra-tabs`` element, toggle between ``open`` and ``closed`` classes to allow opening/closing an extra-tabs menu via CSS.
Expand Down Expand Up @@ -89,6 +93,7 @@
- pat autofocus: Implement documented behavior to not focus on prefilled element, if there is another autofocus element which is empty.
- pat autofocus: Instead of calling autofocus for each element call it only once.
- pat autofocus: Register event handler only once.
- pat-checklist: For global de/select buttons, do not change any other checkboxes than the ones the de/select button belongs to.


## 3.0.0-dev - unreleased
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"spectrum-colorpicker": "^1.8.0",
"stickyfilljs": "git+https://github.com/syslabcom/stickyfill.git",
"tippy.js": "^6.2.7",
"underscore": "^1.11.0",
"underscore": "^1.12.0",
"url-polyfill": "^1.1.9",
"validate.js": "^0.13.1",
"whatwg-fetch": "^3.4.0"
Expand Down
31 changes: 31 additions & 0 deletions src/core/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,43 @@ const show = (el) => {
delete el[DATA_STYLE_DISPLAY];
};

const find_parents = (el, selector) => {
// Return all direct parents of ``el`` matching ``selector``.
// This matches against all parents but not the element itself.
// The order of elements is from the search starting point up to higher
// DOM levels.
let parent = el.parentNode?.closest(selector) || null;
const ret = [];
while (parent) {
ret.push(parent);
parent = parent.parentNode?.closest(selector) || null;
}
return ret;
};

const find_scoped = (el, selector) => {
// If the selector starts with an object id do a global search,
// otherwise do a local search.
return (selector.indexOf("#") === 0 ? document : el).querySelectorAll(
selector
);
};

const is_visible = (el) => {
// Check, if element is visible in DOM.
// https://stackoverflow.com/a/19808107/1337474
return el.offsetWidth > 0 && el.offsetHeight > 0;
};

const dom = {
toNodeArray: toNodeArray,
querySelectorAllAndMe: querySelectorAllAndMe,
wrap: wrap,
hide: hide,
show: show,
find_parents: find_parents,
find_scoped: find_scoped,
is_visible: is_visible,
};

export default dom;
103 changes: 103 additions & 0 deletions src/core/dom.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import dom from "./dom";
describe("core.dom tests", () => {
// Tests from the core.dom module

afterEach(() => {
document.body.innerHTML = "";
});

describe("toNodeArray tests", () => {
it("returns an array of nodes, if a jQuery object was passed.", (done) => {
const html = document.createElement("div");
Expand Down Expand Up @@ -146,4 +150,103 @@ describe("core.dom tests", () => {
done();
});
});

describe("find_parents", () => {
it("it finds all parents matching a selector.", (done) => {
document.body.innerHTML = `
<div class="findme level1">
<div class="dontfindme level2">
<div class="findme level3">
<div class="findme starthere level4">
</div>
</div>
</div>
</div>
`;
const res = dom.find_parents(
document.querySelector(".starthere"),
".findme"
);

// level4 is not found - it's about to find parents.
expect(res.length).toEqual(2);
expect(res[0]).toEqual(document.querySelector(".level3")); // inner dom levels first // prettier-ignore
expect(res[1]).toEqual(document.querySelector(".level1"));

done();
});
});

describe("find_scoped", () => {
it("Find all instances within the current structure.", (done) => {
document.body.innerHTML = `
<div class="findme level1">
<div class="starthere level2">
<div class="findme level3">
<div class="findme level4">
</div>
</div>
</div>
</div>
`;

const res = dom.find_scoped(
document.querySelector(".starthere"),
".findme"
);

expect(res.length).toEqual(2);
expect(res[0]).toEqual(document.querySelector(".level3")); // outer dom levels first // prettier-ignore
expect(res[1]).toEqual(document.querySelector(".level4"));

done();
});

it("Find all instances within the current structure.", (done) => {
document.body.innerHTML = `
<div id="findme" class="level1">
<div class="starthere level2">
<div class="level3">
</div>
</div>
</div>
`;

const res = dom.find_scoped(
document.querySelector(".starthere"),
"#findme"
);

expect(res.length).toEqual(1);
expect(res[0]).toEqual(document.querySelector(".level1"));

done();
});
});

describe("is_visible", () => {
it.skip("checks, if an element is visible or not.", (done) => {
const div1 = document.createElement("div");
div1.setAttribute("id", "div1");

const div2 = document.createElement("div");
div2.setAttribute("id", "div2");

const div3 = document.createElement("div");
div3.setAttribute("id", "div3");

div2.style.display = "none";
div3.style.visibility = "hidden";

document.body.appendChild(div1);
document.body.appendChild(div2);
document.body.appendChild(div3);

expect(dom.is_visible(document.querySelector("#div1"))).toBeTruthy(); // prettier-ignore
expect(dom.is_visible(document.querySelector("#div2"))).toBeFalsy();
expect(dom.is_visible(document.querySelector("#div3"))).toBeFalsy();

done();
});
});
});
Loading