From 5b9d117cc7621e3fee88b54b0985be3717e4749d Mon Sep 17 00:00:00 2001 From: Dimitri Benin Date: Tue, 12 Mar 2019 20:53:17 +0100 Subject: [PATCH 1/5] Update dependencies, add TypeScript definition --- index.d.ts | 21 +++++++++++ index.js | 11 +++--- index.test-d.ts | 11 ++++++ package.json | 16 +++++---- test.js | 96 ++++++++++++++++++++++++------------------------- 5 files changed, 96 insertions(+), 59 deletions(-) create mode 100644 index.d.ts create mode 100644 index.test-d.ts diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..b057ec7 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,21 @@ +import PCancelable from 'p-cancelable'; + +export interface Options { + /** + * The element that's expected to contain a match. + * + * @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; diff --git a/index.js b/index.js index 5e930df..f36a1e1 100644 --- a/index.js +++ b/index.js @@ -13,7 +13,7 @@ const cleanCache = (target, selector) => { } }; -module.exports = (selector, options) => { +const elementReady = (selector, options) => { options = Object.assign({ target: document }, options); @@ -24,9 +24,9 @@ module.exports = (selector, options) => { let alreadyFound = false; const promise = new PCancelable((resolve, reject, onCancel) => { - let raf; + let requestedAnimationFrameId; onCancel(() => { - cancelAnimationFrame(raf); + cancelAnimationFrame(requestedAnimationFrameId); cleanCache(options.target, selector); }); @@ -39,7 +39,7 @@ module.exports = (selector, options) => { alreadyFound = true; cleanCache(options.target, selector); } else { - raf = requestAnimationFrame(check); + requestedAnimationFrameId = requestAnimationFrame(check); } })(); }); @@ -55,3 +55,6 @@ module.exports = (selector, options) => { return promise; }; + +module.exports = elementReady; +module.exports.default = elementReady; diff --git a/index.test-d.ts b/index.test-d.ts new file mode 100644 index 0000000..dc41e7f --- /dev/null +++ b/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'); +elementReady('#unicorn', {target: document}); +elementReady('#unicorn', {target: document.documentElement}); + +expectType>(p); + +p.cancel(); diff --git a/package.json b/package.json index bca1ca0..9ee8ee0 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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" }, "xo": { "envs": [ diff --git a/test.js b/test.js index 5374c19..5a05953 100644 --- a/test.js +++ b/test.js @@ -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; @@ -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(); } } @@ -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')); }); From 50430fa88ee0d043884e8db8816932edcacda097 Mon Sep 17 00:00:00 2001 From: Dimitri Benin Date: Wed, 13 Mar 2019 11:00:06 +0100 Subject: [PATCH 2/5] Improve types for different kinds of elements --- index.d.ts | 18 +++++++++++++++--- index.test-d.ts | 4 +++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/index.d.ts b/index.d.ts index b057ec7..99a10f7 100644 --- a/index.d.ts +++ b/index.d.ts @@ -6,7 +6,7 @@ export interface Options { * * @default document */ - readonly target?: HTMLElement | Document; + readonly target?: Element | Document; } /** @@ -15,7 +15,19 @@ export interface Options { * @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( +export default function elementReady< + ElementName extends keyof HTMLElementTagNameMap +>( + selector: ElementName, + options?: Options +): PCancelable; +export default function elementReady< + ElementName extends keyof SVGElementTagNameMap +>( + selector: ElementName, + options?: Options +): PCancelable; +export default function elementReady( selector: string, options?: Options -): PCancelable; +): PCancelable; diff --git a/index.test-d.ts b/index.test-d.ts index dc41e7f..6a0c302 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -6,6 +6,8 @@ const p = elementReady('#unicorn'); elementReady('#unicorn', {target: document}); elementReady('#unicorn', {target: document.documentElement}); -expectType>(p); +expectType>(p); +expectType>(elementReady('div')); +expectType>(elementReady('text')); p.cancel(); From a3a69a05e8836c3e122efc4639c1b05e6cabfe8e Mon Sep 17 00:00:00 2001 From: Dimitri Benin Date: Wed, 13 Mar 2019 11:40:14 +0100 Subject: [PATCH 3/5] Fixes after review --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index f36a1e1..f01cdcc 100644 --- a/index.js +++ b/index.js @@ -24,9 +24,9 @@ const elementReady = (selector, options) => { let alreadyFound = false; const promise = new PCancelable((resolve, reject, onCancel) => { - let requestedAnimationFrameId; + let rafId; onCancel(() => { - cancelAnimationFrame(requestedAnimationFrameId); + cancelAnimationFrame(rafId); cleanCache(options.target, selector); }); @@ -39,7 +39,7 @@ const elementReady = (selector, options) => { alreadyFound = true; cleanCache(options.target, selector); } else { - requestedAnimationFrameId = requestAnimationFrame(check); + rafId = requestAnimationFrame(check); } })(); }); From f3ca86ae56f50de409b26ab690edb35cd11edd20 Mon Sep 17 00:00:00 2001 From: Dimitri Benin Date: Wed, 13 Mar 2019 12:04:31 +0100 Subject: [PATCH 4/5] Fixes after review --- index.d.ts | 16 ++++++++-------- index.test-d.ts | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/index.d.ts b/index.d.ts index 99a10f7..c6b14bc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2,19 +2,19 @@ import PCancelable from 'p-cancelable'; export interface Options { /** - * The element that's expected to contain a match. - * - * @default document + The element that's expected to contain a match. + + @default document */ readonly target?: Element | 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. - */ +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< ElementName extends keyof HTMLElementTagNameMap >( diff --git a/index.test-d.ts b/index.test-d.ts index 6a0c302..e055d39 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -2,12 +2,12 @@ import {expectType} from 'tsd-check'; import PCancelable from 'p-cancelable'; import elementReady from '.'; -const p = elementReady('#unicorn'); +const promise = elementReady('#unicorn'); elementReady('#unicorn', {target: document}); elementReady('#unicorn', {target: document.documentElement}); -expectType>(p); +expectType>(promise); expectType>(elementReady('div')); expectType>(elementReady('text')); -p.cancel(); +promise.cancel(); From f98911b7d406f55077f79eeb339a5965e595f426 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Wed, 13 Mar 2019 18:09:24 +0700 Subject: [PATCH 5/5] Update index.d.ts --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index c6b14bc..de9f334 100644 --- a/index.d.ts +++ b/index.d.ts @@ -5,7 +5,7 @@ export interface Options { The element that's expected to contain a match. @default document - */ + */ readonly target?: Element | Document; }