Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update dependencies, add TypeScript definition #15

Merged
merged 5 commits into from Mar 13, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 21 additions & 0 deletions index.d.ts
@@ -0,0 +1,21 @@
import PCancelable from 'p-cancelable';

export interface Options {
/**
* The element that's expected to contain a match.
*
BendingBender marked this conversation as resolved.
Show resolved Hide resolved
* @default document
*/
readonly target?: HTMLElement | Document;
}

/**
* Detect when an element is ready in the DOM.
*
* @param selector - [CSS selector.](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
* @returns The matching element.
*/
export default function elementReady(
selector: string,
options?: Options
): PCancelable<HTMLElement>;
11 changes: 7 additions & 4 deletions index.js
Expand Up @@ -13,7 +13,7 @@ const cleanCache = (target, selector) => {
}
};

module.exports = (selector, options) => {
const elementReady = (selector, options) => {
options = Object.assign({
target: document
}, options);
Expand All @@ -24,9 +24,9 @@ module.exports = (selector, options) => {

let alreadyFound = false;
const promise = new PCancelable((resolve, reject, onCancel) => {
let raf;
let requestedAnimationFrameId;
BendingBender marked this conversation as resolved.
Show resolved Hide resolved
onCancel(() => {
cancelAnimationFrame(raf);
cancelAnimationFrame(requestedAnimationFrameId);
cleanCache(options.target, selector);
});

Expand All @@ -39,7 +39,7 @@ module.exports = (selector, options) => {
alreadyFound = true;
cleanCache(options.target, selector);
} else {
raf = requestAnimationFrame(check);
requestedAnimationFrameId = requestAnimationFrame(check);
}
})();
});
Expand All @@ -55,3 +55,6 @@ module.exports = (selector, options) => {

return promise;
};

module.exports = elementReady;
module.exports.default = elementReady;
11 changes: 11 additions & 0 deletions index.test-d.ts
@@ -0,0 +1,11 @@
import {expectType} from 'tsd-check';
import PCancelable from 'p-cancelable';
import elementReady from '.';

const p = elementReady('#unicorn');
BendingBender marked this conversation as resolved.
Show resolved Hide resolved
elementReady('#unicorn', {target: document});
elementReady('#unicorn', {target: document.documentElement});

expectType<PCancelable<HTMLElement>>(p);

p.cancel();
16 changes: 9 additions & 7 deletions package.json
Expand Up @@ -13,10 +13,11 @@
"node": ">=6"
},
"scripts": {
"test": "xo && ava"
"test": "xo && ava && tsd-check"
},
"files": [
"index.js"
"index.js",
"index.d.ts"
],
"keywords": [
"browser",
Expand All @@ -31,13 +32,14 @@
"check"
],
"dependencies": {
"p-cancelable": "^0.4.1"
"p-cancelable": "^1.1.0"
},
"devDependencies": {
"ava": "*",
"delay": "^2.0.0",
"jsdom": "^11.10.0",
"xo": "*"
"ava": "^1.3.1",
"delay": "^4.1.0",
"jsdom": "^14.0.0",
"tsd-check": "^0.3.0",
"xo": "^0.24.0"
sindresorhus marked this conversation as resolved.
Show resolved Hide resolved
},
"xo": {
"envs": [
Expand Down
96 changes: 48 additions & 48 deletions test.js
Expand Up @@ -2,7 +2,7 @@ import test from 'ava';
import jsdom from 'jsdom';
import delay from 'delay';
import PCancelable from 'p-cancelable';
import m from '.';
import elementReady from '.';

const dom = new jsdom.JSDOM();
global.window = dom.window;
Expand All @@ -11,68 +11,68 @@ global.requestAnimationFrame = fn => setTimeout(fn, 16);
global.cancelAnimationFrame = id => clearTimeout(id);

test('check if element ready', async t => {
const elCheck = m('#unicorn');
const elementCheck = elementReady('#unicorn');

delay(500).then(() => {
const el = document.createElement('p');
el.id = 'unicorn';
document.body.appendChild(el);
const element = document.createElement('p');
element.id = 'unicorn';
document.body.append(element);
});

const el = await elCheck;
t.is(el.id, 'unicorn');
const element = await elementCheck;
t.is(element.id, 'unicorn');
});

test('check if element ready inside target', async t => {
const target = document.createElement('p');
const elCheck = m('#unicorn', {
const elCheck = elementReady('#unicorn', {
target
});

delay(500).then(() => {
const el = document.createElement('p');
el.id = 'unicorn';
target.appendChild(el);
const element = document.createElement('p');
element.id = 'unicorn';
target.append(element);
});

const el = await elCheck;
t.is(el.id, 'unicorn');
const element = await elCheck;
t.is(element.id, 'unicorn');
});

test('check if different elements ready inside different targets with same selector', async t => {
const target1 = document.createElement('p');
const elCheck1 = m('.unicorn', {
const elementCheck1 = elementReady('.unicorn', {
target: target1
});
const target2 = document.createElement('span');
const elCheck2 = m('.unicorn', {
const elementCheck2 = elementReady('.unicorn', {
target: target2
});

delay(500).then(() => {
const el1 = document.createElement('p');
el1.id = 'unicorn1';
el1.className = 'unicorn';
target1.appendChild(el1);

const el2 = document.createElement('span');
el2.id = 'unicorn2';
el2.className = 'unicorn';
target2.appendChild(el2);
const element1 = document.createElement('p');
element1.id = 'unicorn1';
element1.className = 'unicorn';
target1.append(element1);

const element2 = document.createElement('span');
element2.id = 'unicorn2';
element2.className = 'unicorn';
target2.append(element2);
});

const el1 = await elCheck1;
t.is(el1.id, 'unicorn1');
const element1 = await elementCheck1;
t.is(element1.id, 'unicorn1');

const el2 = await elCheck2;
t.is(el2.id, 'unicorn2');
const element2 = await elementCheck2;
t.is(element2.id, 'unicorn2');
});

test('ensure only one promise is returned on multiple calls passing the same selector', t => {
const elCheck = m('#unicorn');
const elementCheck = elementReady('#unicorn');

for (let i = 0; i <= 10; i++) {
if (m('#unicorn') !== elCheck) {
if (elementReady('#unicorn') !== elementCheck) {
t.fail();
}
}
Expand All @@ -81,43 +81,43 @@ test('ensure only one promise is returned on multiple calls passing the same sel
});

test('check if wait can be canceled', async t => {
const elCheck = m('#dofle');
const elementCheck = elementReady('#dofle');

await delay(200);
elCheck.cancel();
elementCheck.cancel();

await delay(500);
const el = document.createElement('p');
el.id = 'dofle';
document.body.appendChild(el);
const element = document.createElement('p');
element.id = 'dofle';
document.body.append(element);

await t.throws(elCheck, PCancelable.CancelError);
await t.throwsAsync(elementCheck, PCancelable.CancelError);
});

test('ensure different promises are returned on second call with the same selector when first was canceled', async t => {
const elCheck1 = m('.unicorn');
const elementCheck1 = elementReady('.unicorn');

elCheck1.cancel();
elementCheck1.cancel();

const elCheck2 = m('.unicorn');
const elementCheck2 = elementReady('.unicorn');

await t.throws(elCheck1, PCancelable.CancelError);
t.not(elCheck1, elCheck2);
await t.throwsAsync(elementCheck1, PCancelable.CancelError);
t.not(elementCheck1, elementCheck2);
});

test('ensure different promises are returned on second call with the same selector when first was found', async t => {
const prependElement = () => {
const el = document.createElement('p');
el.className = 'unicorn';
document.body.prepend(el);
return el;
const element = document.createElement('p');
element.className = 'unicorn';
document.body.prepend(element);
return element;
};

t.is(prependElement(), await m('.unicorn'));
t.is(prependElement(), await elementReady('.unicorn'));

document.querySelector('.unicorn').remove();
t.is(prependElement(), await m('.unicorn'));
t.is(prependElement(), await elementReady('.unicorn'));

document.querySelector('.unicorn').remove();
t.is(prependElement(), await m('.unicorn'));
t.is(prependElement(), await elementReady('.unicorn'));
});