Merge pull request #1080 from ing-bank/feat/types-leftover

chore: whitelist -> blacklist for tsconfig
This commit is contained in:
gerjanvangeest 2020-11-05 11:23:53 +01:00 committed by GitHub
commit cf760056e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 184 additions and 110 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/helpers': patch
---
Fix types for helpers

View file

@ -0,0 +1,5 @@
---
'@lion/icon': patch
---
Fix lion icon types

View file

@ -1,5 +1,11 @@
import { render } from '@lion/core'; 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 => { export const renderLitAsNode = litHtmlTemplate => {
const offlineRenderContainer = document.createElement('div'); const offlineRenderContainer = document.createElement('div');
render(litHtmlTemplate, offlineRenderContainer); render(litHtmlTemplate, offlineRenderContainer);

View file

@ -122,11 +122,14 @@ export class SbActionLogger extends LitElement {
constructor() { constructor() {
super(); super();
this.title = 'Action Logger'; this.title = 'Action Logger';
this.simple = false;
this.__logCounter = 0; this.__logCounter = 0;
} }
get loggerEl() { 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 * Only supports simple values, will be interpreted to a String
* E.g. an Object will become '[object Object]' * 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) { log(content) {
this.__animateCue(); this.__animateCue();
@ -147,7 +150,10 @@ export class SbActionLogger extends LitElement {
this.__handleConsecutiveDuplicateLog(); this.__handleConsecutiveDuplicateLog();
} else { } else {
this.__appendLog(content); 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 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 * 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 * @return {TemplateResult} TemplateResult that uses the content passed to create a log
*/ */
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
@ -183,16 +190,24 @@ export class SbActionLogger extends LitElement {
`; `;
} }
/**
* @param {string} content
*/
__appendLog(content) { __appendLog(content) {
const offlineRenderContainer = document.createElement('div'); const offlineRenderContainer = document.createElement('div');
render(this._logTemplate(content), offlineRenderContainer); render(this._logTemplate(content), offlineRenderContainer);
this.loggerEl.appendChild(offlineRenderContainer.firstElementChild); if (offlineRenderContainer.firstElementChild) {
this.loggerEl.appendChild(offlineRenderContainer.firstElementChild);
}
} }
/**
* @param {string} content
*/
__isConsecutiveDuplicateLog(content) { __isConsecutiveDuplicateLog(content) {
if ( if (
this.loggerEl.lastElementChild && this.loggerEl.lastElementChild &&
this.loggerEl.lastElementChild.querySelector('code').textContent.trim() === content this.loggerEl.lastElementChild.querySelector('code')?.textContent?.trim() === content
) { ) {
return true; return true;
} }
@ -200,41 +215,51 @@ export class SbActionLogger extends LitElement {
} }
__handleConsecutiveDuplicateLog() { __handleConsecutiveDuplicateLog() {
if (!this.loggerEl.lastElementChild.querySelector('.logger__log-count')) { if (!this.loggerEl.lastElementChild?.querySelector('.logger__log-count')) {
this.__prependLogCounterElement(); this.__prependLogCounterElement();
} }
// Increment log counter for these duplicate logs // Increment log counter for these duplicate logs
const logCounter = this.loggerEl.lastElementChild.querySelector('.logger__log-count');
let incrementedLogCount = logCounter.textContent; const logCounter = this.loggerEl.lastElementChild?.querySelector('.logger__log-count');
incrementedLogCount = parseInt(incrementedLogCount, 10) + 1; if (logCounter instanceof HTMLElement) {
logCounter.innerText = incrementedLogCount; const logCount = logCounter.textContent;
if (logCount != null) {
const incrementedLogCount = parseInt(logCount, 10) + 1;
logCounter.innerText = incrementedLogCount.toString();
}
}
} }
__prependLogCounterElement() { __prependLogCounterElement() {
const countEl = document.createElement('div'); const countEl = document.createElement('div');
countEl.classList.add('logger__log-count'); countEl.classList.add('logger__log-count');
countEl.innerText = 1; countEl.innerText = (1).toString();
this.loggerEl.lastElementChild.insertBefore(
countEl, const loggerLastElementChild = this.loggerEl.lastElementChild;
this.loggerEl.lastElementChild.firstElementChild, if (loggerLastElementChild) {
); loggerLastElementChild.insertBefore(countEl, loggerLastElementChild.firstElementChild);
}
} }
__animateCue() { __animateCue() {
const cueEl = this.shadowRoot.querySelector('.header__log-cue-overlay'); const cueEl = this.shadowRoot?.querySelector('.header__log-cue-overlay');
cueEl.classList.remove('header__log-cue-overlay--slide'); if (cueEl) {
// This triggers browser to stop batching changes because it has to evaluate something. cueEl.classList.remove('header__log-cue-overlay--slide');
// eslint-disable-next-line no-void // This triggers browser to stop batching changes because it has to evaluate something.
void this.offsetWidth; // eslint-disable-next-line no-void
// So that when we arrive here, the browser sees this adding as an actual 'change' void this.offsetWidth;
// and this means the animation gets refired. // So that when we arrive here, the browser sees this adding as an actual 'change'
cueEl.classList.add('header__log-cue-overlay--slide'); // and this means the animation gets refired.
cueEl.classList.add('header__log-cue-overlay--slide');
}
} }
__clearLogs() { __clearLogs() {
const loggerEl = this.shadowRoot.querySelector('.logger'); const loggerEl = this.shadowRoot?.querySelector('.logger');
loggerEl.innerHTML = ''; if (loggerEl) {
this.__logCounter = 0; loggerEl.innerHTML = '';
this.__logCounter = 0;
}
} }
} }

View file

@ -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'; 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<SbActionLogger>} */ (_fixture);
describe('sb-action-logger', () => { describe('sb-action-logger', () => {
it('has a default title "Action Logger"', async () => { it('has a default title "Action Logger"', async () => {
const el = await fixture(html`<sb-action-logger></sb-action-logger>`); const el = await fixture(html`<sb-action-logger></sb-action-logger>`);
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 () => { it('has a title property / attribute that can be overridden', async () => {
const el = await fixture(html` const el = await fixture(html`
<sb-action-logger title="Logging your favorite fruit"></sb-action-logger> <sb-action-logger title="Logging your favorite fruit"></sb-action-logger>
`); `);
const titleEl = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.header__title'));
const titleEl = el.shadowRoot.querySelector('.header__title');
expect(titleEl.innerText).to.equal('Logging your favorite fruit'); expect(titleEl.innerText).to.equal('Logging your favorite fruit');
}); });
@ -26,10 +30,10 @@ describe('sb-action-logger', () => {
el.log('Hello, World!'); 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.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 () => { it('appends new logs to the logger', async () => {
@ -41,7 +45,7 @@ describe('sb-action-logger', () => {
el.log('Hello, World!'); el.log('Hello, World!');
el.log('Hello, Planet!'); 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); 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 () => { it('shows a visual cue whenever something is logged to the logger', async () => {
const el = await fixture(html`<sb-action-logger></sb-action-logger>`); const el = await fixture(html`<sb-action-logger></sb-action-logger>`);
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; expect(cueEl.classList.contains('header__log-cue-overlay--slide')).to.be.false;
el.log('Hello, World!'); 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 () => { it('has a visual counter that counts the amount of total logs', async () => {
const el = await fixture(html`<sb-action-logger></sb-action-logger>`); const el = await fixture(html`<sb-action-logger></sb-action-logger>`);
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; 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, World!');
el.log('Hello, Planet!'); el.log('Hello, Planet!');
const clearBtn = el.shadowRoot.querySelector('.header__clear'); const clearBtn = /** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.header__clear'));
clearBtn.click(); 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 () => { 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!');
el.log('Hello, Planet!'); // 2 consecutive dupes 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 firstLogCount = /** @type {HTMLElement} */ (loggerEl.firstElementChild?.querySelector(
const lastLog = loggerEl.lastElementChild; '.logger__log-count',
));
const lastLogCount = /** @type {HTMLElement} */ (loggerEl.lastElementChild?.querySelector(
'.logger__log-count',
));
expect(loggerEl.children.length).to.equal(4); expect(loggerEl.children.length).to.equal(4);
expect(firstLog.querySelector('.logger__log-count').innerText).to.equal('3'); expect(firstLogCount.innerText).to.equal('3');
expect(lastLog.querySelector('.logger__log-count').innerText).to.equal('2'); expect(lastLogCount.innerText).to.equal('2');
}); });
it('can be set to simple mode for only showing a single log statement', async () => { it('can be set to simple mode for only showing a single log statement', async () => {
const el = await fixture(html`<sb-action-logger simple></sb-action-logger>`); const el = await fixture(html`<sb-action-logger simple></sb-action-logger>`);
el.log('Hello, World!'); 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.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!'); el.log('Hello, Earth!');
expect(loggerEl.children.length).to.equal(1); 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!');
el.log('Hello, Planet!'); el.log('Hello, Planet!');
expect(loggerEl.children.length).to.equal(1); expect(loggerEl.children.length).to.equal(1);
expect(loggerEl.firstElementChild.querySelector('code').innerText).to.equal('Hello, Planet!'); expect(codeEl.innerText).to.equal('Hello, Planet!');
expect(loggerEl.firstElementChild.querySelector('.logger__log-count')).to.be.null; expect(loggerCountEl).to.be.null;
}); });
}); });

View file

@ -12,6 +12,9 @@ export class SbLocaleSwitcher extends LitElement {
this.showLocales = ['en-GB', 'en-US', 'en-AU', 'nl-NL', 'nl-BE']; this.showLocales = ['en-GB', 'en-US', 'en-AU', 'nl-NL', 'nl-BE'];
} }
/**
* @param {string} locale
*/
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
callback(locale) { callback(locale) {
document.documentElement.lang = locale; document.documentElement.lang = locale;

View file

@ -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) { 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 // 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. // ordering, which is the order they are defined in the story file.
if (a[1].kind === b[1].kind) { if (a[1].kind === b[1].kind) {
@ -28,6 +28,7 @@ export function sortEachDepth(orderPerDepth) {
let nameB; let nameB;
let indexA; let indexA;
let indexB; let indexB;
/** @type {Array.<?>} */
let ordering = orderPerDepth[0] || []; let ordering = orderPerDepth[0] || [];
if (ordering.indexOf('...') !== -1 && ordering.indexOf('...abc') !== -1) { if (ordering.indexOf('...') !== -1 && ordering.indexOf('...abc') !== -1) {
throw new Error( throw new Error(

View file

@ -1,3 +1,8 @@
/**
* @typedef {import('lit-html').TemplateResult} TemplateResult
* @typedef {import('lit-html').nothing} nothing
*/
export class IconManager { export class IconManager {
constructor() { constructor() {
this.__iconResolvers = new Map(); this.__iconResolvers = new Map();
@ -9,7 +14,7 @@ export class IconManager {
* icon as a TemplateResult. This function can be sync or async. * icon as a TemplateResult. This function can be sync or async.
* *
* @param {string} namespace * @param {string} namespace
* @param {(iconset: string, icon: string) => TemplateResult | Promise<TemplateResult>} iconResolver * @param {(iconset: string, icon: string) => TemplateResult | Promise<TemplateResult> | nothing | Promise<nothing> } iconResolver
*/ */
addIconResolver(namespace, iconResolver) { addIconResolver(namespace, iconResolver) {
if (this.__iconResolvers.has(namespace)) { if (this.__iconResolvers.has(namespace)) {
@ -54,6 +59,6 @@ export class IconManager {
throw new Error(`Incorrect iconId: ${iconId}. Format: <namespace>:<iconset>:<icon>`); throw new Error(`Incorrect iconId: ${iconId}. Format: <namespace>:<iconset>:<icon>`);
} }
return this.resolveIcon(...splitIconId); return this.resolveIcon(splitIconId[0], splitIconId[1], splitIconId[2]);
} }
} }

View file

@ -1,12 +1,18 @@
import { css, html, LitElement, nothing, render, TemplateResult } from '@lion/core'; import { css, html, LitElement, nothing, render, TemplateResult } from '@lion/core';
import { icons } from './icons.js'; import { icons } from './icons.js';
/**
* @param {?} wrappedSvgObject
*/
function unwrapSvg(wrappedSvgObject) { function unwrapSvg(wrappedSvgObject) {
const svgObject = const svgObject =
wrappedSvgObject && wrappedSvgObject.default ? wrappedSvgObject.default : wrappedSvgObject; wrappedSvgObject && wrappedSvgObject.default ? wrappedSvgObject.default : wrappedSvgObject;
return typeof svgObject === 'function' ? svgObject(html) : svgObject; return typeof svgObject === 'function' ? svgObject(html) : svgObject;
} }
/**
* @param {TemplateResult|nothing} svg
*/
function validateSvg(svg) { function validateSvg(svg) {
if (!(svg === nothing || svg instanceof TemplateResult)) { if (!(svg === nothing || svg instanceof TemplateResult)) {
throw new Error( throw new Error(
@ -24,15 +30,13 @@ export class LionIcon extends LitElement {
/** /**
* @desc When icons are not loaded as part of an iconset defined on iconManager, * @desc When icons are not loaded as part of an iconset defined on iconManager,
* it's possible to directly load an svg. * it's possible to directly load an svg.
* @type {TemplateResult|function}
*/ */
svg: { svg: {
type: Object, attribute: false,
}, },
/** /**
* @desc The iconId allows to access icons that are registered to the IconManager * @desc The iconId allows to access icons that are registered to the IconManager
* For instance, "lion:space:alienSpaceship" * For instance, "lion:space:alienSpaceship"
* @type {string}
*/ */
ariaLabel: { ariaLabel: {
type: String, type: String,
@ -42,7 +46,6 @@ export class LionIcon extends LitElement {
/** /**
* @desc The iconId allows to access icons that are registered to the IconManager * @desc The iconId allows to access icons that are registered to the IconManager
* For instance, "lion:space:alienSpaceship" * For instance, "lion:space:alienSpaceship"
* @type {string}
*/ */
iconId: { iconId: {
type: String, type: String,
@ -93,16 +96,20 @@ export class LionIcon extends LitElement {
constructor() { constructor() {
super(); super();
this.role = 'img'; this.role = 'img';
this.ariaLabel = '';
this.iconId = '';
this.__svg = nothing;
} }
/** @param {import('lit-element').PropertyValues} changedProperties */
update(changedProperties) { update(changedProperties) {
super.update(changedProperties); super.update(changedProperties);
if (changedProperties.has('ariaLabel')) { if (changedProperties.has('ariaLabel')) {
this._onLabelChanged(changedProperties); this._onLabelChanged();
} }
if (changedProperties.has('iconId')) { if (changedProperties.has('iconId')) {
this._onIconIdChanged(changedProperties.get('iconId')); this._onIconIdChanged(/** @type {string} */ (changedProperties.get('iconId')));
} }
} }
@ -119,6 +126,7 @@ export class LionIcon extends LitElement {
/** /**
* On IE11, svgs without focusable false appear in the tab order * On IE11, svgs without focusable false appear in the tab order
* so make sure to have <svg focusable="false"> in svg files * so make sure to have <svg focusable="false"> in svg files
* @param {TemplateResult|nothing} svg
*/ */
set svg(svg) { set svg(svg) {
this.__svg = svg; this.__svg = svg;
@ -142,6 +150,9 @@ export class LionIcon extends LitElement {
} }
} }
/**
* @param {TemplateResult | nothing} svgObject
*/
_renderSvg(svgObject) { _renderSvg(svgObject) {
validateSvg(svgObject); validateSvg(svgObject);
render(svgObject, this); render(svgObject, this);
@ -150,11 +161,14 @@ export class LionIcon extends LitElement {
} }
} }
/**
* @param {string} prevIconId
*/
async _onIconIdChanged(prevIconId) { async _onIconIdChanged(prevIconId) {
if (!this.iconId) { if (!this.iconId) {
// clear if switching from iconId to no iconId // clear if switching from iconId to no iconId
if (prevIconId) { if (prevIconId) {
this.svg = null; this.svg = nothing;
} }
} else { } else {
const iconIdBeforeResolve = this.iconId; const iconIdBeforeResolve = this.iconId;

View file

@ -4,6 +4,7 @@ import { IconManager } from './IconManager.js';
// eslint-disable-next-line import/no-mutable-exports // eslint-disable-next-line import/no-mutable-exports
export let icons = singletonManager.get('@lion/icon::icons::0.5.x') || new IconManager(); export let icons = singletonManager.get('@lion/icon::icons::0.5.x') || new IconManager();
// @ts-ignore since we don't know which singleton icon manager version will be used, we cannot type it.
export function setIcons(newIcons) { export function setIcons(newIcons) {
icons = newIcons; icons = newIcons;
} }

View file

@ -1,7 +1,12 @@
import { nothing } from '@lion/core';
import { expect } from '@open-wc/testing'; import { expect } from '@open-wc/testing';
import { stub } from 'sinon'; import { stub } from 'sinon';
import { IconManager } from '../src/IconManager.js'; import { IconManager } from '../src/IconManager.js';
/**
* @typedef {import("lit-html").TemplateResult} TemplateResult
*/
describe('IconManager', () => { describe('IconManager', () => {
it('starts off with an empty map of resolvers', () => { it('starts off with an empty map of resolvers', () => {
const manager = new IconManager(); const manager = new IconManager();
@ -10,7 +15,15 @@ describe('IconManager', () => {
it('allows adding an icon resolver', () => { it('allows adding an icon resolver', () => {
const manager = new IconManager(); const manager = new IconManager();
const resolver = () => {}; /**
* @param {string} iconset
* @param {string} icon
* @return {TemplateResult | Promise<TemplateResult> | nothing | Promise<nothing>}
*/
// eslint-disable-next-line no-unused-vars
const resolver = (iconset, icon) => {
return nothing;
};
manager.addIconResolver('foo', resolver); manager.addIconResolver('foo', resolver);
expect(manager.__iconResolvers.get('foo')).to.equal(resolver); expect(manager.__iconResolvers.get('foo')).to.equal(resolver);

View file

@ -1,2 +1,2 @@
export default tag => export default /** @param {(strings: TemplateStringsArray, ... expr: string[]) => string} tag */ tag =>
tag`<svg focusable="false" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" data-test-id="svg-hammer"><path d="M.476 12.915L6.7 6 9 8.556l-6.223 6.915a1.542 1.542 0 0 1-1.15.529 1.54 1.54 0 0 1-1.15-.53c-.636-.706-.636-1.85 0-2.555zm12.638-9.031L16 6.863 12.866 10 4 .919 9.35 0l1.912 1.972.251-.251c.52-.52 2.4 1.363 1.88 1.882l-.279.28z"></path></svg>`; tag`<svg focusable="false" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" data-test-id="svg-hammer"><path d="M.476 12.915L6.7 6 9 8.556l-6.223 6.915a1.542 1.542 0 0 1-1.15.529 1.54 1.54 0 0 1-1.15-.53c-.636-.706-.636-1.85 0-2.555zm12.638-9.031L16 6.863 12.866 10 4 .919 9.35 0l1.912 1.972.251-.251c.52-.52 2.4 1.363 1.88 1.882l-.279.28z"></path></svg>`;

View file

@ -1,2 +1,2 @@
export default tag => export default /** @param {(strings: TemplateStringsArray, ... expr: string[]) => string} tag */ tag =>
tag`<svg focusable="false" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" data-test-id="svg-heart"><path d="M8.002 14.867a.828.828 0 0 1-.324-.066C7.36 14.665 0 11.466 0 6.153 0 3.592 1.543.825 4.98 1.01c1.465.077 2.437.828 3.018 1.491.581-.667 1.553-1.414 3.023-1.491.107-.008.207-.008.31-.008C13.58 1.001 16 2.614 16 6.153c0 5.313-7.36 8.512-7.671 8.644a.787.787 0 0 1-.327.07z"></path></svg>`; tag`<svg focusable="false" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" data-test-id="svg-heart"><path d="M8.002 14.867a.828.828 0 0 1-.324-.066C7.36 14.665 0 11.466 0 6.153 0 3.592 1.543.825 4.98 1.01c1.465.077 2.437.828 3.018 1.491.581-.667 1.553-1.414 3.023-1.491.107-.008.207-.008.31-.008C13.58 1.001 16 2.614 16 6.153c0 5.313-7.36 8.512-7.671 8.644a.787.787 0 0 1-.327.07z"></path></svg>`;

View file

@ -1,19 +1,28 @@
import { until } from '@lion/core'; import { nothing, until } from '@lion/core';
import { aTimeout, expect, fixture, fixtureSync, html } from '@open-wc/testing'; import { aTimeout, expect, fixture as _fixture, fixtureSync, html } from '@open-wc/testing';
import '../lion-icon.js'; import '../lion-icon.js';
import { icons } from '../src/icons.js'; import { icons } from '../src/icons.js';
import hammerSvg from './hammer.svg.js'; import hammerSvg from './hammer.svg.js';
import heartSvg from './heart.svg.js'; import heartSvg from './heart.svg.js';
/**
* @typedef {(strings: TemplateStringsArray, ... expr: string[]) => string} TaggedTemplateLiteral
* @typedef {import('../src/LionIcon').LionIcon} LionIcon
* @typedef {import('lit-html').TemplateResult} TemplateResult
*/
const fixture = /** @type {(arg: TemplateResult|string) => Promise<LionIcon>} */ (_fixture);
describe('lion-icon', () => { describe('lion-icon', () => {
it('supports svg icon as a function which recieves a tag function as an argument and returns a tagged template literal', async () => { it('supports svg icon as a function which recieves a tag function as an argument and returns a tagged template literal', async () => {
const iconFunction = tag => tag`<svg data-test-id="svg"></svg>`; const iconFunction = /** @param {TaggedTemplateLiteral} tag */ tag =>
tag`<svg data-test-id="svg"></svg>`;
const el = await fixture(html`<lion-icon .svg=${iconFunction}></lion-icon>`); const el = await fixture(html`<lion-icon .svg=${iconFunction}></lion-icon>`);
expect(el.children[0].getAttribute('data-test-id')).to.equal('svg'); expect(el.children[0].getAttribute('data-test-id')).to.equal('svg');
}); });
it('is hidden when attribute hidden is true', async () => { it('is hidden when attribute hidden is true', async () => {
const iconFunction = tag => tag`<svg data-test-id="svg"></svg>`; const iconFunction = /** @param {TaggedTemplateLiteral} tag */ tag =>
tag`<svg data-test-id="svg"></svg>`;
const el = await fixture(html`<lion-icon .svg=${iconFunction} hidden></lion-icon>`); const el = await fixture(html`<lion-icon .svg=${iconFunction} hidden></lion-icon>`);
expect(el).not.to.be.displayed; expect(el).not.to.be.displayed;
}); });
@ -126,7 +135,7 @@ describe('lion-icon', () => {
await svgLoading; await svgLoading;
// We need to await the until directive is resolved and rendered to the dom // We need to await the until directive is resolved and rendered to the dom
// You can not use updateComplete as until renders on it's own // You can not use updateComplete as until renders on it's own
await aTimeout(); await aTimeout(0);
expect(el.children[0].getAttribute('data-test-id')).to.equal('svg-heart'); expect(el.children[0].getAttribute('data-test-id')).to.equal('svg-heart');
}); });
@ -134,7 +143,7 @@ describe('lion-icon', () => {
it('does not render "undefined" if changed from valid input to undefined', async () => { it('does not render "undefined" if changed from valid input to undefined', async () => {
const el = await fixture(html`<lion-icon .svg=${heartSvg}></lion-icon>`); const el = await fixture(html`<lion-icon .svg=${heartSvg}></lion-icon>`);
await el.updateComplete; await el.updateComplete;
el.svg = undefined; el.svg = nothing;
await el.updateComplete; await el.updateComplete;
expect(el.innerHTML).to.equal('<!----><!---->'); // don't use lightDom.to.equal(''), it gives false positives expect(el.innerHTML).to.equal('<!----><!---->'); // don't use lightDom.to.equal(''), it gives false positives
}); });
@ -142,7 +151,7 @@ describe('lion-icon', () => {
it('does not render "null" if changed from valid input to null', async () => { it('does not render "null" if changed from valid input to null', async () => {
const el = await fixture(html`<lion-icon .svg=${heartSvg}></lion-icon>`); const el = await fixture(html`<lion-icon .svg=${heartSvg}></lion-icon>`);
await el.updateComplete; await el.updateComplete;
el.svg = null; el.svg = nothing;
await el.updateComplete; await el.updateComplete;
expect(el.innerHTML).to.equal('<!----><!---->'); // don't use lightDom.to.equal(''), it gives false positives expect(el.innerHTML).to.equal('<!----><!---->'); // don't use lightDom.to.equal(''), it gives false positives
}); });
@ -152,7 +161,7 @@ describe('lion-icon', () => {
icons.addIconResolver('foo', () => heartSvg); icons.addIconResolver('foo', () => heartSvg);
const el = await fixture(html`<lion-icon icon-id="foo:lorem:ipsum"></lion-icon>`); const el = await fixture(html`<lion-icon icon-id="foo:lorem:ipsum"></lion-icon>`);
expect(el.children[0].dataset.testId).to.equal('svg-heart'); expect(/** @type {HTMLElement} */ (el.children[0]).dataset.testId).to.equal('svg-heart');
} finally { } finally {
icons.removeIconResolver('foo'); icons.removeIconResolver('foo');
} }
@ -189,10 +198,10 @@ describe('lion-icon', () => {
await aTimeout(4); await aTimeout(4);
// heart is still loading at this point, but hammer came later so that should be later // heart is still loading at this point, but hammer came later so that should be later
expect(el.children[0].dataset.testId).to.equal('svg-hammer'); expect(/** @type {HTMLElement} */ (el.children[0]).dataset.testId).to.equal('svg-hammer');
await aTimeout(10); await aTimeout(10);
// heart finished loading, but it should not be rendered because hammer came later // heart finished loading, but it should not be rendered because hammer came later
expect(el.children[0].dataset.testId).to.equal('svg-hammer'); expect(/** @type {HTMLElement} */ (el.children[0]).dataset.testId).to.equal('svg-hammer');
} finally { } finally {
icons.removeIconResolver('foo'); icons.removeIconResolver('foo');
icons.removeIconResolver('bar'); icons.removeIconResolver('bar');

View file

@ -14,49 +14,22 @@
"esModuleInterop": true, "esModuleInterop": true,
"suppressImplicitAnyIndexErrors": true "suppressImplicitAnyIndexErrors": true
}, },
"include": [ "include": ["packages/**/*.js"],
"packages/accordion/**/*.js",
"packages/button/**/*.js",
"packages/calendar/**/*.js",
"packages/checkbox-group/**/*.js",
"packages/collapsible/**/*.js",
"packages/core/**/*.js",
"packages/combobox/(test|src)/**/*.js",
"packages/combobox/*.js",
"packages/dialog/**/*.js",
"packages/fieldset/**/*.js",
"packages/form/**/*.js",
"packages/form-core/**/*.js",
"packages/input/**/*.js",
"packages/input-amount/**/*.js",
"packages/input-date/**/*.js",
"packages/input-datepicker/**/*.js",
"packages/input-email/**/*.js",
"packages/input-iban/**/*.js",
"packages/input-range/**/*.js",
"packages/input-stepper/**/*.js",
"packages/listbox/**/*.js",
"packages/localize/**/*.js",
"packages/overlays/**/*.js",
"packages/pagination/**/*.js",
"packages/progress-indicator/**/*.js",
"packages/radio-group/**/*.js",
"packages/select/**/*.js",
"packages/select-rich/**/*.js",
"packages/singleton-manager/**/*.js",
"packages/steps/**/*.js",
"packages/switch/**/*.js",
"packages/tabs/**/*.js",
"packages/textarea/**/*.js",
"packages/tooltip/**/*.js",
"packages/validate-messages/**/*.js"
],
"exclude": [ "exclude": [
"node_modules", "node_modules",
"**/node_modules/*", "**/node_modules/*",
"**/coverage/*", "**/coverage/*",
"**/dist/**/*", "**/dist/**/*",
"packages/**/test-helpers", "packages/**/test-helpers",
"packages/**/docs/**/*",
"packages/babel-plugin-extend-docs/**/*.js",
"packages/providence-analytics/**/*.js",
"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/form-integrations/test/**/*.js", // TODO: Needs to get typed!
"packages/combobox/test/**/*.js", // TODO: 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 // ignore test/demos for singleton manager until overlays are typed as it's used in there
"packages/singleton-manager/demo/", "packages/singleton-manager/demo/",
"packages/singleton-manager/test/" "packages/singleton-manager/test/"