feat(switch): add types

This commit is contained in:
Joren Broekema 2020-09-30 17:45:27 +02:00 committed by Thomas Allmer
parent 7d4b6d32db
commit 06123918d0
5 changed files with 63 additions and 16 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/switch': minor
---
Add types for switch package.

View file

@ -2,6 +2,7 @@ import { css, html, ScopedElementsMixin } from '@lion/core';
import { ChoiceInputMixin, LionField } from '@lion/form-core'; import { ChoiceInputMixin, LionField } from '@lion/form-core';
import { LionSwitchButton } from './LionSwitchButton.js'; import { LionSwitchButton } from './LionSwitchButton.js';
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
export class LionSwitch extends ScopedElementsMixin(ChoiceInputMixin(LionField)) { export class LionSwitch extends ScopedElementsMixin(ChoiceInputMixin(LionField)) {
static get styles() { static get styles() {
return [ return [
@ -25,10 +26,26 @@ export class LionSwitch extends ScopedElementsMixin(ChoiceInputMixin(LionField))
}; };
} }
/**
* Input node here is the lion-switch-button, which is not compatible with LionField _inputNode --> HTMLInputElement
* Therefore we do a full override and typecast to an intersection type that includes LionSwitchButton
* @returns {HTMLInputElement & LionSwitchButton}
*/
get _inputNode() {
return /** @type {HTMLInputElement & LionSwitchButton} */ (Array.from(this.children).find(
el => el.slot === 'input',
));
}
get slots() { get slots() {
return { return {
...super.slots, ...super.slots,
input: () => document.createElement(this.constructor.getScopedTagName('lion-switch-button')), input: () =>
document.createElement(
/** @type {typeof LionSwitch} */ (this.constructor).getScopedTagName(
'lion-switch-button',
),
),
}; };
} }
@ -82,6 +99,7 @@ export class LionSwitch extends ScopedElementsMixin(ChoiceInputMixin(LionField))
} }
} }
/** @param {import('lit-element').PropertyValues } changedProperties */
updated(changedProperties) { updated(changedProperties) {
super.updated(changedProperties); super.updated(changedProperties);
this._syncButtonSwitch(); this._syncButtonSwitch();

View file

@ -113,6 +113,9 @@ export class LionSwitchButton extends DisabledWithTabIndexMixin(LitElement) {
this.setAttribute('aria-checked', `${this.checked}`); this.setAttribute('aria-checked', `${this.checked}`);
} }
/**
* @param {KeyboardEvent} e
*/
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
__handleKeydown(e) { __handleKeydown(e) {
// prevent "space" scrolling on "macOS" // prevent "space" scrolling on "macOS"
@ -121,12 +124,16 @@ export class LionSwitchButton extends DisabledWithTabIndexMixin(LitElement) {
} }
} }
/**
* @param {KeyboardEvent} e
*/
__handleKeyup(e) { __handleKeyup(e) {
if ([32 /* space */, 13 /* enter */].indexOf(e.keyCode) !== -1) { if ([32 /* space */, 13 /* enter */].indexOf(e.keyCode) !== -1) {
this.__toggleChecked(); this.__toggleChecked();
} }
} }
/** @param {import('lit-element').PropertyValues } changedProperties */
updated(changedProperties) { updated(changedProperties) {
if (changedProperties.has('disabled')) { if (changedProperties.has('disabled')) {
this.setAttribute('aria-disabled', `${this.disabled}`); // create mixin if we need it in more places this.setAttribute('aria-disabled', `${this.disabled}`); // create mixin if we need it in more places
@ -136,6 +143,8 @@ export class LionSwitchButton extends DisabledWithTabIndexMixin(LitElement) {
/** /**
* We synchronously update aria-checked to support voice over on safari. * We synchronously update aria-checked to support voice over on safari.
* *
* @param {PropertyKey} name
* @param {?} oldValue
* @override * @override
*/ */
requestUpdateInternal(name, oldValue) { requestUpdateInternal(name, oldValue) {

View file

@ -1,8 +1,16 @@
import { expect, fixture, html } from '@open-wc/testing'; import { expect, fixture as _fixture, html } from '@open-wc/testing';
import sinon from 'sinon'; import sinon from 'sinon';
import '../lion-switch-button.js'; import '../lion-switch-button.js';
/**
* @typedef {import('../src/LionSwitchButton').LionSwitchButton} LionSwitchButton
* @typedef {import('lit-html').TemplateResult} TemplateResult
*/
const fixture = /** @type {(arg: TemplateResult) => Promise<LionSwitchButton>} */ (_fixture);
describe('lion-switch-button', () => { describe('lion-switch-button', () => {
/** @type {LionSwitchButton} */
let el; let el;
beforeEach(async () => { beforeEach(async () => {
el = await fixture(html`<lion-switch-button></lion-switch-button>`); el = await fixture(html`<lion-switch-button></lion-switch-button>`);
@ -62,15 +70,15 @@ describe('lion-switch-button', () => {
el.click(); el.click();
el.click(); el.click();
expect(handlerSpy.callCount).to.equal(2); expect(handlerSpy.callCount).to.equal(2);
const checkCall = call => { const checkCall = /** @param {import('sinon').SinonSpyCall} call */ call => {
expect(call.args).to.have.a.lengthOf(1); expect(call.args).to.have.lengthOf(1);
const e = call.args[0]; const e = call.args[0];
expect(e).to.be.an.instanceof(Event); expect(e).to.be.an.instanceof(Event);
expect(e.bubbles).to.be.true; expect(e.bubbles).to.be.true;
expect(e.composed).to.be.true; expect(e.composed).to.be.true;
}; };
checkCall(handlerSpy.getCall(0), true); checkCall(handlerSpy.getCall(0));
checkCall(handlerSpy.getCall(1), false); checkCall(handlerSpy.getCall(1));
}); });
it('should dispatch "checked-changed" event when checked changed', () => { it('should dispatch "checked-changed" event when checked changed', () => {
@ -79,15 +87,15 @@ describe('lion-switch-button', () => {
el.checked = true; el.checked = true;
el.checked = false; el.checked = false;
expect(handlerSpy.callCount).to.equal(2); expect(handlerSpy.callCount).to.equal(2);
const checkCall = call => { const checkCall = /** @param {import('sinon').SinonSpyCall} call */ call => {
expect(call.args).to.have.a.lengthOf(1); expect(call.args).to.have.lengthOf(1);
const e = call.args[0]; const e = call.args[0];
expect(e).to.be.an.instanceof(Event); expect(e).to.be.an.instanceof(Event);
expect(e.bubbles).to.be.true; expect(e.bubbles).to.be.true;
expect(e.composed).to.be.true; expect(e.composed).to.be.true;
}; };
checkCall(handlerSpy.getCall(0), true); checkCall(handlerSpy.getCall(0));
checkCall(handlerSpy.getCall(1), false); checkCall(handlerSpy.getCall(1));
}); });
it('should not dispatch "checked-changed" event if disabled', () => { it('should not dispatch "checked-changed" event if disabled', () => {
@ -117,7 +125,7 @@ describe('lion-switch-button', () => {
await el.updateComplete; await el.updateComplete;
expect(el.getAttribute('aria-checked')).to.equal('false'); expect(el.getAttribute('aria-checked')).to.equal('false');
el.setAttribute('checked', true); el.setAttribute('checked', '');
await el.updateComplete; await el.updateComplete;
expect(el.getAttribute('aria-checked')).to.equal('true'); expect(el.getAttribute('aria-checked')).to.equal('true');
el.removeAttribute('checked'); el.removeAttribute('checked');

View file

@ -1,7 +1,14 @@
import { expect, fixture, html } from '@open-wc/testing'; import { expect, fixture as _fixture, html } from '@open-wc/testing';
import sinon from 'sinon'; import sinon from 'sinon';
import '../lion-switch.js'; import '../lion-switch.js';
/**
* @typedef {import('../src/LionSwitch').LionSwitch} LionSwitch
* @typedef {import('lit-html').TemplateResult} TemplateResult
*/
const fixture = /** @type {(arg: TemplateResult) => Promise<LionSwitch>} */ (_fixture);
describe('lion-switch', () => { describe('lion-switch', () => {
it('should have default "input" element', async () => { it('should have default "input" element', async () => {
const el = await fixture(html`<lion-switch></lion-switch>`); const el = await fixture(html`<lion-switch></lion-switch>`);
@ -83,15 +90,15 @@ describe('lion-switch', () => {
el._labelNode.click(); el._labelNode.click();
await el.updateComplete; await el.updateComplete;
expect(handlerSpy.callCount).to.equal(2); expect(handlerSpy.callCount).to.equal(2);
const checkCall = call => { const checkCall = /** @param {import('sinon').SinonSpyCall} call */ call => {
expect(call.args).to.have.a.lengthOf(1); expect(call.args).to.have.lengthOf(1);
const e = call.args[0]; const e = call.args[0];
expect(e).to.be.an.instanceof(Event); expect(e).to.be.an.instanceof(Event);
expect(e.bubbles).to.be.true; expect(e.bubbles).to.be.true;
expect(e.composed).to.be.true; expect(e.composed).to.be.true;
}; };
checkCall(handlerSpy.getCall(0), true); checkCall(handlerSpy.getCall(0));
checkCall(handlerSpy.getCall(1), false); checkCall(handlerSpy.getCall(1));
}); });
it('should dispatch "checked-changed" event when checked changed', async () => { it('should dispatch "checked-changed" event when checked changed', async () => {