Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
feat(dom): Add closest ponyfill
Browse files Browse the repository at this point in the history
  • Loading branch information
Kenneth G. Franqueiro committed Sep 13, 2018
1 parent b775801 commit 1e584eb
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 2 deletions.
1 change: 1 addition & 0 deletions packages/mdc-dom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ The `ponyfill` module provides the following functions:

Function Signature | Description
--- | ---
`closest(element: Element, selector: string) => ?Element` | Returns the ancestor of the given element matching the given selector (which may be the element itself if it matches), or `null` if no matching ancestor is found.
`matches(element: Element, selector: string) => boolean` | Returns true if the given element matches the given CSS selector.
22 changes: 21 additions & 1 deletion packages/mdc-dom/ponyfill.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@
* This makes ponyfills safer than traditional polyfills, especially for libraries like MDC.
*/

/**
* @param {!Element} element
* @param {string} selector
* @return {?Element}
*/
function closest(element, selector) {
if (element.closest) {
return element.closest(selector);
}

let el = element;
while (el) {
if (matches(el, selector)) {
return el;
}
el = el.parentElement;
}
return null;
}

/**
* @param {!Element} element
* @param {string} selector
Expand All @@ -38,4 +58,4 @@ function matches(element, selector) {
return nativeMatches.call(element, selector);
}

export {matches};
export {closest, matches};
45 changes: 44 additions & 1 deletion test/unit/mdc-dom/ponyfill.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,54 @@

import {assert} from 'chai';
import bel from 'bel';
import td from 'testdouble';

import {matches} from '../../../packages/mdc-dom/ponyfill';
import {closest, matches} from '../../../packages/mdc-dom/ponyfill';

suite('MDCDom - ponyfill');

test('#closest returns result from native method if available', () => {
const mockElement = td.object({closest: () => {}});
const selector = '.foo';
td.when(mockElement.closest(selector)).thenReturn(mockElement);

assert.strictEqual(closest(mockElement, selector), mockElement);
});

test('#closest returns the element when the selector matches the element', () => {
const mockElement = td.object({matches: () => {}});
const selector = '.foo';
td.when(mockElement.matches(selector)).thenReturn(true);

assert.strictEqual(closest(mockElement, selector), mockElement);
});

test('#closest returns the parent element when the selector matches the parent element', () => {
const mockParentElement = td.object({matches: () => {}});
const mockChildElement = {
matches: td.func('mockChildElement#matches'),
parentElement: mockParentElement,
};
const selector = '.foo';
td.when(mockChildElement.matches(selector)).thenReturn(false);
td.when(mockParentElement.matches(selector)).thenReturn(true);

assert.strictEqual(closest(mockChildElement, selector), mockParentElement);
});

test('#closest returns null when there is no ancestor matching the selector', () => {
const mockParentElement = td.object({matches: () => {}});
const mockChildElement = {
matches: td.func('mockChildElement#matches'),
parentElement: mockParentElement,
};
const selector = '.foo';
td.when(mockChildElement.matches(selector)).thenReturn(false);
td.when(mockParentElement.matches(selector)).thenReturn(false);

assert.isNull(closest(mockChildElement, selector));
});

test('#matches returns true when the selector matches the element', () => {
const element = bel`<div class="foo"></div>`;
assert.isTrue(matches(element, '.foo'));
Expand Down

0 comments on commit 1e584eb

Please sign in to comment.