diff --git a/.changeset/fifty-jars-sing.md b/.changeset/fifty-jars-sing.md new file mode 100644 index 000000000..e4b0c2fa0 --- /dev/null +++ b/.changeset/fifty-jars-sing.md @@ -0,0 +1,5 @@ +--- +'@lion/helpers': patch +--- + +Fix types for helpers diff --git a/packages/helpers/renderLitAsNode/src/renderLitAsNode.js b/packages/helpers/renderLitAsNode/src/renderLitAsNode.js index da60eaff3..955a0cba0 100644 --- a/packages/helpers/renderLitAsNode/src/renderLitAsNode.js +++ b/packages/helpers/renderLitAsNode/src/renderLitAsNode.js @@ -1,5 +1,11 @@ import { render } from '@lion/core'; +/** + * Helper to render a lit TemplateResult as an offline-created DOM node + * Make sure that the top-most element in the template has no siblings, + * as they won't be taken into account. We only return firstElementChild. + * @param {import('lit-html').TemplateResult} litHtmlTemplate + */ export const renderLitAsNode = litHtmlTemplate => { const offlineRenderContainer = document.createElement('div'); render(litHtmlTemplate, offlineRenderContainer); diff --git a/packages/helpers/sb-action-logger/src/SbActionLogger.js b/packages/helpers/sb-action-logger/src/SbActionLogger.js index bdb5defed..bafd1ec99 100644 --- a/packages/helpers/sb-action-logger/src/SbActionLogger.js +++ b/packages/helpers/sb-action-logger/src/SbActionLogger.js @@ -122,11 +122,14 @@ export class SbActionLogger extends LitElement { constructor() { super(); this.title = 'Action Logger'; + this.simple = false; this.__logCounter = 0; } get loggerEl() { - return this.shadowRoot.querySelector('.logger'); + return /** @type {HTMLElement} */ ( + /** @type {ShadowRoot} */ (this.shadowRoot).querySelector('.logger') + ); } /** @@ -134,7 +137,7 @@ export class SbActionLogger extends LitElement { * Only supports simple values, will be interpreted to a String * E.g. an Object will become '[object Object]' * - * @param {} content Content to be logged to the action logger + * @param {string} content Content to be logged to the action logger */ log(content) { this.__animateCue(); @@ -147,7 +150,10 @@ export class SbActionLogger extends LitElement { this.__handleConsecutiveDuplicateLog(); } else { this.__appendLog(content); - this.loggerEl.scrollTo({ top: this.loggerEl.scrollHeight, behavior: 'smooth' }); + this.loggerEl.scrollTo({ + top: this.loggerEl.scrollHeight, + behavior: 'smooth', + }); } this.__logCounter += 1; // increment total log counter @@ -156,6 +162,7 @@ export class SbActionLogger extends LitElement { /** * Protected getter that returns the template of a single log * + * @param {string} content * @return {TemplateResult} TemplateResult that uses the content passed to create a log */ // eslint-disable-next-line class-methods-use-this @@ -183,16 +190,24 @@ export class SbActionLogger extends LitElement { `; } + /** + * @param {string} content + */ __appendLog(content) { const offlineRenderContainer = document.createElement('div'); render(this._logTemplate(content), offlineRenderContainer); - this.loggerEl.appendChild(offlineRenderContainer.firstElementChild); + if (offlineRenderContainer.firstElementChild) { + this.loggerEl.appendChild(offlineRenderContainer.firstElementChild); + } } + /** + * @param {string} content + */ __isConsecutiveDuplicateLog(content) { if ( this.loggerEl.lastElementChild && - this.loggerEl.lastElementChild.querySelector('code').textContent.trim() === content + this.loggerEl.lastElementChild.querySelector('code')?.textContent?.trim() === content ) { return true; } @@ -200,41 +215,51 @@ export class SbActionLogger extends LitElement { } __handleConsecutiveDuplicateLog() { - if (!this.loggerEl.lastElementChild.querySelector('.logger__log-count')) { + if (!this.loggerEl.lastElementChild?.querySelector('.logger__log-count')) { this.__prependLogCounterElement(); } // Increment log counter for these duplicate logs - const logCounter = this.loggerEl.lastElementChild.querySelector('.logger__log-count'); - let incrementedLogCount = logCounter.textContent; - incrementedLogCount = parseInt(incrementedLogCount, 10) + 1; - logCounter.innerText = incrementedLogCount; + + const logCounter = this.loggerEl.lastElementChild?.querySelector('.logger__log-count'); + if (logCounter instanceof HTMLElement) { + const logCount = logCounter.textContent; + if (logCount != null) { + const incrementedLogCount = parseInt(logCount, 10) + 1; + logCounter.innerText = incrementedLogCount.toString(); + } + } } __prependLogCounterElement() { const countEl = document.createElement('div'); countEl.classList.add('logger__log-count'); - countEl.innerText = 1; - this.loggerEl.lastElementChild.insertBefore( - countEl, - this.loggerEl.lastElementChild.firstElementChild, - ); + countEl.innerText = (1).toString(); + + const loggerLastElementChild = this.loggerEl.lastElementChild; + if (loggerLastElementChild) { + loggerLastElementChild.insertBefore(countEl, loggerLastElementChild.firstElementChild); + } } __animateCue() { - const cueEl = this.shadowRoot.querySelector('.header__log-cue-overlay'); - cueEl.classList.remove('header__log-cue-overlay--slide'); - // This triggers browser to stop batching changes because it has to evaluate something. - // eslint-disable-next-line no-void - void this.offsetWidth; - // So that when we arrive here, the browser sees this adding as an actual 'change' - // and this means the animation gets refired. - cueEl.classList.add('header__log-cue-overlay--slide'); + const cueEl = this.shadowRoot?.querySelector('.header__log-cue-overlay'); + if (cueEl) { + cueEl.classList.remove('header__log-cue-overlay--slide'); + // This triggers browser to stop batching changes because it has to evaluate something. + // eslint-disable-next-line no-void + void this.offsetWidth; + // So that when we arrive here, the browser sees this adding as an actual 'change' + // and this means the animation gets refired. + cueEl.classList.add('header__log-cue-overlay--slide'); + } } __clearLogs() { - const loggerEl = this.shadowRoot.querySelector('.logger'); - loggerEl.innerHTML = ''; - this.__logCounter = 0; + const loggerEl = this.shadowRoot?.querySelector('.logger'); + if (loggerEl) { + loggerEl.innerHTML = ''; + this.__logCounter = 0; + } } } diff --git a/packages/helpers/sb-action-logger/test/sb-action-logger.test.js b/packages/helpers/sb-action-logger/test/sb-action-logger.test.js index 754173145..2ccbdf9b3 100644 --- a/packages/helpers/sb-action-logger/test/sb-action-logger.test.js +++ b/packages/helpers/sb-action-logger/test/sb-action-logger.test.js @@ -1,21 +1,25 @@ -import { expect, fixture, html } from '@open-wc/testing'; +import { expect, fixture as _fixture, html } from '@open-wc/testing'; import '../../sb-action-logger.js'; -// Note: skips are left out of first iteration +/** + * @typedef {import('../src/SbActionLogger').SbActionLogger} SbActionLogger + * @typedef {import('lit-html').TemplateResult} TemplateResult + */ +const fixture = /** @type {(arg: TemplateResult|string) => Promise} */ (_fixture); describe('sb-action-logger', () => { it('has a default title "Action Logger"', async () => { const el = await fixture(html``); + const titleEl = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.header__title')); - expect(el.shadowRoot.querySelector('.header__title').innerText).to.equal('Action Logger'); + expect(titleEl.innerText).to.equal('Action Logger'); }); it('has a title property / attribute that can be overridden', async () => { const el = await fixture(html` `); - - const titleEl = el.shadowRoot.querySelector('.header__title'); + const titleEl = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.header__title')); expect(titleEl.innerText).to.equal('Logging your favorite fruit'); }); @@ -26,10 +30,10 @@ describe('sb-action-logger', () => { el.log('Hello, World!'); - const loggerEl = el.shadowRoot.querySelector('.logger'); - + const loggerEl = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.logger')); + const loggerFirstEntryEl = /** @type {HTMLElement} */ (loggerEl.firstElementChild); expect(loggerEl.children.length).to.equal(1); - expect(loggerEl.firstElementChild.innerText).to.equal('Hello, World!'); + expect(loggerFirstEntryEl.innerText).to.equal('Hello, World!'); }); it('appends new logs to the logger', async () => { @@ -41,7 +45,7 @@ describe('sb-action-logger', () => { el.log('Hello, World!'); el.log('Hello, Planet!'); - const loggerEl = el.shadowRoot.querySelector('.logger'); + const loggerEl = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.logger')); expect(loggerEl.children.length).to.equal(5); }); @@ -49,7 +53,9 @@ describe('sb-action-logger', () => { it('shows a visual cue whenever something is logged to the logger', async () => { const el = await fixture(html``); - const cueEl = el.shadowRoot.querySelector('.header__log-cue-overlay'); + const cueEl = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector( + '.header__log-cue-overlay', + )); expect(cueEl.classList.contains('header__log-cue-overlay--slide')).to.be.false; el.log('Hello, World!'); @@ -59,7 +65,9 @@ describe('sb-action-logger', () => { it('has a visual counter that counts the amount of total logs', async () => { const el = await fixture(html``); - const cueEl = el.shadowRoot.querySelector('.header__log-cue-overlay'); + const cueEl = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector( + '.header__log-cue-overlay', + )); expect(cueEl.classList.contains('.header__log-cue-overlay--slide')).to.be.false; @@ -73,10 +81,10 @@ describe('sb-action-logger', () => { el.log('Hello, World!'); el.log('Hello, Planet!'); - const clearBtn = el.shadowRoot.querySelector('.header__clear'); + const clearBtn = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.header__clear')); clearBtn.click(); - expect(el.shadowRoot.querySelector('.logger').children.length).to.equal(0); + expect(el.shadowRoot?.querySelector('.logger')?.children.length).to.equal(0); }); it('duplicate consecutive logs are kept as one, adds a visual counter', async () => { @@ -90,33 +98,39 @@ describe('sb-action-logger', () => { el.log('Hello, Planet!'); el.log('Hello, Planet!'); // 2 consecutive dupes - const loggerEl = el.shadowRoot.querySelector('.logger'); + const loggerEl = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.logger')); - const firstLog = loggerEl.firstElementChild; - const lastLog = loggerEl.lastElementChild; + const firstLogCount = /** @type {HTMLElement} */ (loggerEl.firstElementChild?.querySelector( + '.logger__log-count', + )); + const lastLogCount = /** @type {HTMLElement} */ (loggerEl.lastElementChild?.querySelector( + '.logger__log-count', + )); expect(loggerEl.children.length).to.equal(4); - expect(firstLog.querySelector('.logger__log-count').innerText).to.equal('3'); - expect(lastLog.querySelector('.logger__log-count').innerText).to.equal('2'); + expect(firstLogCount.innerText).to.equal('3'); + expect(lastLogCount.innerText).to.equal('2'); }); it('can be set to simple mode for only showing a single log statement', async () => { const el = await fixture(html``); el.log('Hello, World!'); - const loggerEl = el.shadowRoot.querySelector('.logger'); + const loggerEl = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.logger')); + const loggerCountEl = loggerEl.firstElementChild?.querySelector('.logger__log-count'); + const codeEl = /** @type {HTMLElement} */ (loggerEl.firstElementChild?.querySelector('code')); expect(loggerEl.children.length).to.equal(1); - expect(loggerEl.firstElementChild.querySelector('code').innerText).to.equal('Hello, World!'); + expect(codeEl.innerText).to.equal('Hello, World!'); el.log('Hello, Earth!'); expect(loggerEl.children.length).to.equal(1); - expect(loggerEl.firstElementChild.querySelector('code').innerText).to.equal('Hello, Earth!'); + expect(codeEl.innerText).to.equal('Hello, Earth!'); el.log('Hello, Planet!'); el.log('Hello, Planet!'); expect(loggerEl.children.length).to.equal(1); - expect(loggerEl.firstElementChild.querySelector('code').innerText).to.equal('Hello, Planet!'); - expect(loggerEl.firstElementChild.querySelector('.logger__log-count')).to.be.null; + expect(codeEl.innerText).to.equal('Hello, Planet!'); + expect(loggerCountEl).to.be.null; }); }); diff --git a/packages/helpers/sb-locale-switcher/src/SbLocaleSwitcher.js b/packages/helpers/sb-locale-switcher/src/SbLocaleSwitcher.js index 0df4f4781..8c30fe195 100644 --- a/packages/helpers/sb-locale-switcher/src/SbLocaleSwitcher.js +++ b/packages/helpers/sb-locale-switcher/src/SbLocaleSwitcher.js @@ -12,6 +12,9 @@ export class SbLocaleSwitcher extends LitElement { this.showLocales = ['en-GB', 'en-US', 'en-AU', 'nl-NL', 'nl-BE']; } + /** + * @param {string} locale + */ // eslint-disable-next-line class-methods-use-this callback(locale) { document.documentElement.lang = locale; diff --git a/packages/helpers/sortEachDepth/src/sortEachDepth.js b/packages/helpers/sortEachDepth/src/sortEachDepth.js index d1c7d6e9b..45ee68750 100644 --- a/packages/helpers/sortEachDepth/src/sortEachDepth.js +++ b/packages/helpers/sortEachDepth/src/sortEachDepth.js @@ -12,10 +12,10 @@ * ]), * }); * - * @param {array} orderPerDepth array of arrays giving the order of each level + * @param {Array.<[]>} orderPerDepth array of arrays giving the order of each level */ export function sortEachDepth(orderPerDepth) { - return (a, b) => { + return (/** @type {Array.} */ a, /** @type {Array.} */ b) => { // If the two stories have the same story kind, then use the default // ordering, which is the order they are defined in the story file. if (a[1].kind === b[1].kind) { @@ -28,6 +28,7 @@ export function sortEachDepth(orderPerDepth) { let nameB; let indexA; let indexB; + /** @type {Array.} */ let ordering = orderPerDepth[0] || []; if (ordering.indexOf('...') !== -1 && ordering.indexOf('...abc') !== -1) { throw new Error( diff --git a/tsconfig.json b/tsconfig.json index e186db970..5d9493e95 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,10 +27,9 @@ "packages/remark-extend/**/*.js", "packages/select-rich/test/**/*.js", // TODO: Needs to get typed! "packages/overlays/test/utils-tests/**/*.js", // TODO: Needs to get typed! - "packages/helpers/**/*.js", // FIXME: Needs to get typed! "packages/form-integrations/test/**/*.js", // TODO: Needs to get typed! "packages/combobox/test/**/*.js", // TODO: Needs to get typed! - "packages/ajax/**/*.js", // FIXME: Needs to get typed! + "packages/ajax/**/*.js", // Deprecated because we will move to redaxios soon. // ignore test/demos for singleton manager until overlays are typed as it's used in there "packages/singleton-manager/demo/", "packages/singleton-manager/test/"