diff --git a/packages/icon/README.md b/packages/icon/README.md index b9acb2712..ea02c1a11 100644 --- a/packages/icon/README.md +++ b/packages/icon/README.md @@ -32,6 +32,19 @@ Use it in your lit-html template: ``` +### Icon format + +Icon file is an ES module with an extension `.svg.js` which exports a function like this: + +```js +// bug.svg.js +export default tag => tag` + ... +`; +``` + +Make sure you have `focusable="false"` in the icon file to prevent bugs in IE/Edge when the icon appears in tab-order. + ### Accessibiltiy You may add an `aria-label` to provide information to visually impaired users: diff --git a/packages/icon/src/LionIcon.js b/packages/icon/src/LionIcon.js index 1d492511a..200a60460 100644 --- a/packages/icon/src/LionIcon.js +++ b/packages/icon/src/LionIcon.js @@ -1,4 +1,4 @@ -import { html, css, LitElement } from '@lion/core'; +import { html, nothing, TemplateResult, css, render, LitElement } from '@lion/core'; const isPromise = action => typeof action === 'object' && Promise.resolve(action) === action; @@ -11,7 +11,7 @@ export class LionIcon extends LitElement { return { // svg is a property to ensure the setter is called if the property is set before upgrading svg: { - type: String, + type: Object, }, role: { type: String, @@ -84,17 +84,17 @@ export class LionIcon extends LitElement { set svg(svg) { this.__svg = svg; if (svg === undefined) { - this._renderSvg(''); + this._renderSvg(nothing); } else if (isPromise(svg)) { - this._renderSvg(''); // show nothing before resolved + this._renderSvg(nothing); // show nothing before resolved svg.then(resolvedSvg => { // render only if it is still the same and was not replaced after loading started if (svg === this.__svg) { - this._renderSvg(resolvedSvg); + this._renderSvg(this.constructor.__unwrapSvg(resolvedSvg)); } }); } else { - this._renderSvg(svg); + this._renderSvg(this.constructor.__unwrapSvg(svg)); } } @@ -111,8 +111,22 @@ export class LionIcon extends LitElement { } } - _renderSvg(svgOrModule) { - const svg = svgOrModule && svgOrModule.default ? svgOrModule.default : svgOrModule; - this.innerHTML = svg; + _renderSvg(svgObject) { + this.constructor.__validateSvg(svgObject); + render(svgObject, this); + } + + static __unwrapSvg(wrappedSvgObject) { + const svgObject = + wrappedSvgObject && wrappedSvgObject.default ? wrappedSvgObject.default : wrappedSvgObject; + return typeof svgObject === 'function' ? svgObject(html) : svgObject; + } + + static __validateSvg(svg) { + if (!(svg === nothing || svg instanceof TemplateResult)) { + throw new Error( + 'icon accepts only lit-html templates or functions like "tag => tag`...`"', + ); + } } } diff --git a/packages/icon/stories/icons/bugs/bug01.svg.js b/packages/icon/stories/icons/bugs/bug01.svg.js index c57d889b2..db8f6cde2 100644 --- a/packages/icon/stories/icons/bugs/bug01.svg.js +++ b/packages/icon/stories/icons/bugs/bug01.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/bugs/bug02.svg.js b/packages/icon/stories/icons/bugs/bug02.svg.js index 5211c1e8f..e8c81e7b2 100644 --- a/packages/icon/stories/icons/bugs/bug02.svg.js +++ b/packages/icon/stories/icons/bugs/bug02.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/bugs/bug05.svg.js b/packages/icon/stories/icons/bugs/bug05.svg.js index 33fe059b4..b9272d231 100644 --- a/packages/icon/stories/icons/bugs/bug05.svg.js +++ b/packages/icon/stories/icons/bugs/bug05.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/bugs/bug06.svg.js b/packages/icon/stories/icons/bugs/bug06.svg.js index 8d5caf285..ba57ae18d 100644 --- a/packages/icon/stories/icons/bugs/bug06.svg.js +++ b/packages/icon/stories/icons/bugs/bug06.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/bugs/bug08.svg.js b/packages/icon/stories/icons/bugs/bug08.svg.js index d2c7c504e..dd2f9073c 100644 --- a/packages/icon/stories/icons/bugs/bug08.svg.js +++ b/packages/icon/stories/icons/bugs/bug08.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/bugs/bug12.svg.js b/packages/icon/stories/icons/bugs/bug12.svg.js index 9a42c96c2..7f0679de4 100644 --- a/packages/icon/stories/icons/bugs/bug12.svg.js +++ b/packages/icon/stories/icons/bugs/bug12.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/bugs/bug19.svg.js b/packages/icon/stories/icons/bugs/bug19.svg.js index 832aebafa..ec4055a35 100644 --- a/packages/icon/stories/icons/bugs/bug19.svg.js +++ b/packages/icon/stories/icons/bugs/bug19.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/bugs/bug23.svg.js b/packages/icon/stories/icons/bugs/bug23.svg.js index 330716eaa..0c76bdf37 100644 --- a/packages/icon/stories/icons/bugs/bug23.svg.js +++ b/packages/icon/stories/icons/bugs/bug23.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/bugs/bug24.svg.js b/packages/icon/stories/icons/bugs/bug24.svg.js index 7e77f6b9b..b604f065a 100644 --- a/packages/icon/stories/icons/bugs/bug24.svg.js +++ b/packages/icon/stories/icons/bugs/bug24.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/aliens-spaceship.svg.js b/packages/icon/stories/icons/space/aliens-spaceship.svg.js index a6cd30a53..9438a9317 100644 --- a/packages/icon/stories/icons/space/aliens-spaceship.svg.js +++ b/packages/icon/stories/icons/space/aliens-spaceship.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/meteor.svg.js b/packages/icon/stories/icons/space/meteor.svg.js index 40770e093..197133816 100644 --- a/packages/icon/stories/icons/space/meteor.svg.js +++ b/packages/icon/stories/icons/space/meteor.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/moon-flag.svg.js b/packages/icon/stories/icons/space/moon-flag.svg.js index 2be77c6b9..9283c4010 100644 --- a/packages/icon/stories/icons/space/moon-flag.svg.js +++ b/packages/icon/stories/icons/space/moon-flag.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/moon.svg.js b/packages/icon/stories/icons/space/moon.svg.js index ac5cf1c1d..dbfb92b1c 100644 --- a/packages/icon/stories/icons/space/moon.svg.js +++ b/packages/icon/stories/icons/space/moon.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/night.svg.js b/packages/icon/stories/icons/space/night.svg.js index 8528f3dc2..ba258dac1 100644 --- a/packages/icon/stories/icons/space/night.svg.js +++ b/packages/icon/stories/icons/space/night.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/orbit.svg.js b/packages/icon/stories/icons/space/orbit.svg.js index 9819d763f..e3f26ebb0 100644 --- a/packages/icon/stories/icons/space/orbit.svg.js +++ b/packages/icon/stories/icons/space/orbit.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/planet.svg.js b/packages/icon/stories/icons/space/planet.svg.js index 2364e9af8..2d0283f43 100644 --- a/packages/icon/stories/icons/space/planet.svg.js +++ b/packages/icon/stories/icons/space/planet.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/robot.svg.js b/packages/icon/stories/icons/space/robot.svg.js index fa6609620..73dd887fd 100644 --- a/packages/icon/stories/icons/space/robot.svg.js +++ b/packages/icon/stories/icons/space/robot.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/rocket.svg.js b/packages/icon/stories/icons/space/rocket.svg.js index 68dae39d7..6d7a1fdc6 100644 --- a/packages/icon/stories/icons/space/rocket.svg.js +++ b/packages/icon/stories/icons/space/rocket.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/satellite.svg.js b/packages/icon/stories/icons/space/satellite.svg.js index 0291c2f02..363059924 100644 --- a/packages/icon/stories/icons/space/satellite.svg.js +++ b/packages/icon/stories/icons/space/satellite.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/signal.svg.js b/packages/icon/stories/icons/space/signal.svg.js index e7e1dbb6f..8613b0e1f 100644 --- a/packages/icon/stories/icons/space/signal.svg.js +++ b/packages/icon/stories/icons/space/signal.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/space-helmet.svg.js b/packages/icon/stories/icons/space/space-helmet.svg.js index 45300e92f..4b7b539ef 100644 --- a/packages/icon/stories/icons/space/space-helmet.svg.js +++ b/packages/icon/stories/icons/space/space-helmet.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/sun.svg.js b/packages/icon/stories/icons/space/sun.svg.js index 134f315a6..11a341845 100644 --- a/packages/icon/stories/icons/space/sun.svg.js +++ b/packages/icon/stories/icons/space/sun.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/stories/icons/space/telescope.svg.js b/packages/icon/stories/icons/space/telescope.svg.js index 498c6f8b6..4fdffb02c 100644 --- a/packages/icon/stories/icons/space/telescope.svg.js +++ b/packages/icon/stories/icons/space/telescope.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/test/hammer.svg.js b/packages/icon/test/hammer.svg.js index aa6130703..1d909b91e 100644 --- a/packages/icon/test/hammer.svg.js +++ b/packages/icon/test/hammer.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/test/heart.svg.js b/packages/icon/test/heart.svg.js index 05f2b801a..0272044f0 100644 --- a/packages/icon/test/heart.svg.js +++ b/packages/icon/test/heart.svg.js @@ -1 +1,2 @@ -export default ''; +export default tag => + tag``; diff --git a/packages/icon/test/lion-icon.test.js b/packages/icon/test/lion-icon.test.js index 675c4002b..316c4db9b 100644 --- a/packages/icon/test/lion-icon.test.js +++ b/packages/icon/test/lion-icon.test.js @@ -6,6 +6,47 @@ import hammerSvg from './hammer.svg.js'; import '../lion-icon.js'; 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 () => { + const iconFunction = tag => tag``; + const el = await fixture( + html` + + `, + ); + expect(el.children[0].getAttribute('data-test-id')).to.equal('svg'); + }); + + it('supports svg icon as a lit-html template', async () => { + const icon = html` + + `; + const el = await fixture( + html` + + `, + ); + expect(el.children[0].getAttribute('data-test-id')).to.equal('svg'); + }); + + it('does not support string svg icon', async () => { + const errorMessage = + 'icon accepts only lit-html templates or functions like "tag => tag`...`"'; + expect(() => { + fixtureSync( + html` + '}> + `, + ); + }).to.throw(Error, errorMessage); + expect(() => { + fixtureSync( + html` + ''}> + `, + ); + }).to.throw(Error, errorMessage); + }); + it('displays an svg icon with an aria label attribute', async () => { const el = await fixture( html` @@ -135,7 +176,7 @@ describe('lion-icon', () => { dispatchEvent(new CustomEvent('importDone')); return e.default; }), - '', + html``, )} aria-label="Love" > @@ -159,7 +200,7 @@ describe('lion-icon', () => { await el.updateComplete; el.svg = undefined; await el.updateComplete; - expect(el.innerHTML).to.equal(''); + expect(el.innerHTML).to.equal(''); // don't use lightDom.to.equal(''), it gives false positives }); describe('race conditions with dynamic promisified icons', () => {