feat: add types for input components

This commit is contained in:
Joren Broekema 2020-09-28 19:00:59 +02:00 committed by Thomas Allmer
parent 35efc9c49e
commit cfa2daf674
15 changed files with 252 additions and 119 deletions

View file

@ -0,0 +1,9 @@
---
'@lion/input-date': minor
'@lion/input-email': minor
'@lion/input-iban': minor
'@lion/input-range': minor
'@lion/input-stepper': minor
---
Added types for all other input components except for datepicker.

View file

@ -2,8 +2,12 @@ import { IsDate } from '@lion/form-core';
import { LionInput } from '@lion/input';
import { formatDate, LocalizeMixin, parseDate } from '@lion/localize';
/**
* @param {Date|number} date
*/
function isValidDate(date) {
// to make sure it is a valid date we use isNaN and not Number.isNaN
// @ts-ignore dirty hack, you're not supposed to pass Date instances to isNaN
// eslint-disable-next-line no-restricted-globals
return date instanceof Date && !isNaN(date);
}
@ -13,8 +17,8 @@ function isValidDate(date) {
* on locale.
*
* @customElement lion-input-date
* @extends {LionInput}
*/
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
export class LionInputDate extends LocalizeMixin(LionInput) {
static get properties() {
return {
@ -24,15 +28,19 @@ export class LionInputDate extends LocalizeMixin(LionInput) {
constructor() {
super();
this.parser = (value, options) => (value === '' ? undefined : parseDate(value, options));
/**
* @param {string} value
*/
this.parser = value => (value === '' ? undefined : parseDate(value));
this.formatter = formatDate;
this.defaultValidators.push(new IsDate());
}
/** @param {import('lit-element').PropertyValues } changedProperties */
updated(changedProperties) {
super.updated(changedProperties);
if (changedProperties.has('locale')) {
this._calculateValues();
this._calculateValues({ source: null });
}
}
@ -42,6 +50,9 @@ export class LionInputDate extends LocalizeMixin(LionInput) {
this.type = 'text';
}
/**
* @param {Date} modelValue
*/
// eslint-disable-next-line class-methods-use-this
serializer(modelValue) {
if (!isValidDate(modelValue)) {
@ -50,9 +61,12 @@ export class LionInputDate extends LocalizeMixin(LionInput) {
// modelValue is localized, so we take the timezone offset in milliseconds and subtract it
// before converting it to ISO string.
const offset = modelValue.getTimezoneOffset() * 60000;
return new Date(modelValue - offset).toISOString().slice(0, 10);
return new Date(modelValue.getTime() - offset).toISOString().slice(0, 10);
}
/**
* @param {string} serializedValue
*/
// eslint-disable-next-line class-methods-use-this
deserializer(serializedValue) {
return new Date(serializedValue);

View file

@ -6,7 +6,6 @@ const tagString = 'lion-input-date';
describe('<lion-input-date> integrations', () => {
runInteractionStateMixinSuite({
tagString,
suffix: tagString,
allowedModelValueTypes: [Date],
});

View file

@ -2,9 +2,15 @@ import { html } from '@lion/core';
import { localize } from '@lion/localize';
import { localizeTearDown } from '@lion/localize/test-helpers.js';
import { MaxDate } from '@lion/form-core';
import { expect, fixture } from '@open-wc/testing';
import { expect, fixture as _fixture } from '@open-wc/testing';
import '../lion-input-date.js';
/**
* @typedef {import('../src/LionInputDate').LionInputDate} LionInputDate
* @typedef {import('lit-html').TemplateResult} TemplateResult
*/
const fixture = /** @type {(arg: TemplateResult) => Promise<LionInputDate>} */ (_fixture);
describe('<lion-input-date>', () => {
beforeEach(() => {
localizeTearDown();
@ -24,16 +30,16 @@ describe('<lion-input-date>', () => {
const el = await fixture(html`<lion-input-date></lion-input-date>`);
el.modelValue = '2005/11/10';
expect(el.hasFeedbackFor).to.include('error');
expect(el.validationStates).to.have.a.property('error');
expect(el.validationStates.error).to.have.a.property('IsDate');
expect(el.validationStates).to.have.property('error');
expect(el.validationStates.error).to.have.property('IsDate');
el.modelValue = new Date('2005/11/10');
expect(el.hasFeedbackFor).not.to.include('error');
expect(el.validationStates).to.have.a.property('error');
expect(el.validationStates.error).not.to.have.a.property('IsDate');
expect(el.validationStates).to.have.property('error');
expect(el.validationStates.error).not.to.have.property('IsDate');
});
it("does not throw on invalid dates like new Date('foo')", async () => {
it("does not throw on invalid dates like new Date('20.10.'), which could happen while the user types", async () => {
const el = await fixture(html`<lion-input-date></lion-input-date>`);
expect(() => {
el.modelValue = new Date('foo');
@ -48,13 +54,13 @@ describe('<lion-input-date>', () => {
></lion-input-date>
`);
expect(el.hasFeedbackFor).to.include('error');
expect(el.validationStates).to.have.a.property('error');
expect(el.validationStates.error).to.have.a.property('MaxDate');
expect(el.validationStates).to.have.property('error');
expect(el.validationStates.error).to.have.property('MaxDate');
el.modelValue = new Date('2017/06/14');
expect(el.hasFeedbackFor).not.to.include('error');
expect(el.validationStates).to.have.a.property('error');
expect(el.validationStates.error).not.to.have.a.property('MaxDate');
expect(el.validationStates).to.have.property('error');
expect(el.validationStates.error).not.to.have.property('MaxDate');
});
it('uses formatOptions.locale', async () => {

View file

@ -6,8 +6,8 @@ import { LocalizeMixin } from '@lion/localize';
* LionInputEmail: extension of lion-input
*
* @customElement lion-input-email
* @extends {LionInput}
*/
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
export class LionInputEmail extends LocalizeMixin(LionInput) {
constructor() {
super();

View file

@ -1,7 +1,13 @@
import { expect, fixture } from '@open-wc/testing';
import { expect, fixture as _fixture } from '@open-wc/testing';
import '../lion-input-email.js';
/**
* @typedef {import('../src/LionInputEmail').LionInputEmail} LionInputEmail
* @typedef {import('lit-html').TemplateResult} TemplateResult
*/
const fixture = /** @type {(arg: TemplateResult|string) => Promise<LionInputEmail>} */ (_fixture);
describe('<lion-input-email>', () => {
it('has a type = text', async () => {
const el = await fixture(`<lion-input-email></lion-input-email>`);

View file

@ -6,9 +6,9 @@ import { IsIBAN } from './validators.js';
/**
* `LionInputIban` is a class for an IBAN custom form element (`<lion-input-iban>`).
*
* @extends {LionInput}
* @customElement lion-input-iban
*/
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
export class LionInputIban extends LocalizeMixin(LionInput) {
constructor() {
super();

View file

@ -4,7 +4,7 @@ import { isValidIBAN } from 'ibantools';
* Parses an IBAN trimming spaces and making uppercase.
*
* @param {string} viewValue value to be parsed
* @return {string} parsed value
* @return {string|undefined} parsed value
*/
export function parseIBAN(viewValue) {
const trimmedValue = viewValue.replace(/\s/g, '').toUpperCase();

View file

@ -11,7 +11,7 @@ const loadTranslations = async () => {
}
await localize.loadNamespace(
{
'lion-validate+iban': locale => {
'lion-validate+iban': /** @param {string} locale */ locale => {
switch (locale) {
case 'bg-BG':
return import('../translations/bg-BG.js');
@ -86,7 +86,7 @@ const loadTranslations = async () => {
}
},
},
{ locale: localize.localize },
{ locale: localize.locale },
);
loaded = true;
};
@ -96,11 +96,22 @@ export class IsIBAN extends Validator {
return 'IsIBAN';
}
/** @param {string} value */
// eslint-disable-next-line class-methods-use-this
execute(value) {
return !isValidIBAN(value);
}
/**
* @param {object} [data]
* @param {*} [data.modelValue]
* @param {string} [data.fieldName]
* @param {*} [data.params]
* @param {string} [data.type]
* @param {Object.<string,?>} [data.config]
* @param {string} [data.name]
* @returns {Promise<string|Node>}
*/
static async getMessage(data) {
await loadTranslations();
return localize.msg('lion-validate+iban:error.IsIBAN', data);
@ -112,6 +123,10 @@ export class IsCountryIBAN extends IsIBAN {
return 'IsCountryIBAN';
}
/**
* @param {?} [value]
* @returns {Boolean}
*/
execute(value) {
const notIBAN = super.execute(value);
if (value.slice(0, 2) !== this.param) {
@ -123,6 +138,16 @@ export class IsCountryIBAN extends IsIBAN {
return false;
}
/**
* @param {object} [data]
* @param {*} [data.modelValue]
* @param {string} [data.fieldName]
* @param {*} [data.params]
* @param {string} [data.type]
* @param {Object.<string,?>} [data.config]
* @param {string} [data.name]
* @returns {Promise<string|Node>}
*/
static async getMessage(data) {
await loadTranslations();
return localize.msg('lion-validate+iban:error.IsCountryIBAN', data);

View file

@ -1,4 +1,4 @@
import { expect, fixture } from '@open-wc/testing';
import { expect, fixture as _fixture } from '@open-wc/testing';
import { html } from '@lion/core';
import { IsCountryIBAN } from '../src/validators.js';
@ -7,6 +7,12 @@ import { parseIBAN } from '../src/parsers.js';
import '../lion-input-iban.js';
/**
* @typedef {import('../src/LionInputIban').LionInputIban} LionInputIban
* @typedef {import('lit-html').TemplateResult} TemplateResult
*/
const fixture = /** @type {(arg: TemplateResult|string) => Promise<LionInputIban>} */ (_fixture);
describe('<lion-input-iban>', () => {
it('uses formatIBAN for formatting', async () => {
const el = await fixture(`<lion-input-iban></lion-input-iban>`);
@ -27,12 +33,12 @@ describe('<lion-input-iban>', () => {
const el = await fixture(`<lion-input-iban></lion-input-iban>`);
el.modelValue = 'FOO';
expect(el.hasFeedbackFor).to.include('error');
expect(el.validationStates).to.have.a.property('error');
expect(el.validationStates.error).to.have.a.property('IsIBAN');
expect(el.validationStates).to.have.property('error');
expect(el.validationStates.error).to.have.property('IsIBAN');
el.modelValue = 'DE89370400440532013000';
expect(el.hasFeedbackFor).not.to.include('error');
expect(el.validationStates).to.have.a.property('error');
expect(el.validationStates.error).not.to.have.a.property('IsIBAN');
expect(el.validationStates).to.have.property('error');
expect(el.validationStates.error).not.to.have.property('IsIBAN');
});
it('can apply validator "IsCountryIBAN" to restrict countries', async () => {
@ -41,17 +47,17 @@ describe('<lion-input-iban>', () => {
`);
el.modelValue = 'DE89370400440532013000';
expect(el.hasFeedbackFor).to.include('error');
expect(el.validationStates).to.have.a.property('error');
expect(el.validationStates.error).to.have.a.property('IsCountryIBAN');
expect(el.validationStates).to.have.property('error');
expect(el.validationStates.error).to.have.property('IsCountryIBAN');
el.modelValue = 'NL17INGB0002822608';
expect(el.hasFeedbackFor).not.to.include('error');
expect(el.validationStates).to.have.a.property('error');
expect(el.validationStates.error).not.to.have.a.property('IsCountryIBAN');
expect(el.validationStates).to.have.property('error');
expect(el.validationStates.error).not.to.have.property('IsCountryIBAN');
el.modelValue = 'FOO';
expect(el.hasFeedbackFor).to.include('error');
expect(el.validationStates).to.have.a.property('error');
expect(el.validationStates.error).to.have.a.property('IsIBAN');
expect(el.validationStates.error).to.have.a.property('IsCountryIBAN');
expect(el.validationStates).to.have.property('error');
expect(el.validationStates.error).to.have.property('IsIBAN');
expect(el.validationStates.error).to.have.property('IsCountryIBAN');
});
it('is accessible', async () => {

View file

@ -3,18 +3,31 @@ import { css, html, unsafeCSS } from '@lion/core';
import { LionInput } from '@lion/input';
import { formatNumber, LocalizeMixin } from '@lion/localize';
/**
* @typedef {import('lit-element').CSSResult} CSSResult
*/
/**
* LionInputRange: extension of lion-input.
*
* @customElement `lion-input-range`
* @extends LionInput
*/
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110 + false positive for incompatible static get properties. Lit-element merges super properties already for you.
export class LionInputRange extends LocalizeMixin(LionInput) {
static get properties() {
return {
min: Number,
max: Number,
unit: String,
min: {
type: Number,
reflect: true,
},
max: {
type: Number,
reflect: true,
},
unit: {
type: String,
reflect: true,
},
step: {
type: Number,
reflect: true,
@ -26,6 +39,9 @@ export class LionInputRange extends LocalizeMixin(LionInput) {
};
}
/**
* @param {CSSResult} scope
*/
static rangeStyles(scope) {
return css`
/* Custom input range styling comes here, be aware that this won't work for polyfilled browsers */
@ -37,9 +53,24 @@ export class LionInputRange extends LocalizeMixin(LionInput) {
`;
}
connectedCallback() {
if (super.connectedCallback) super.connectedCallback();
constructor() {
super();
this.min = Infinity;
this.max = Infinity;
this.step = 1;
this.unit = '';
this.type = 'range';
this.noMinMaxLabels = false;
/**
* @param {string} modelValue
*/
this.parser = modelValue => parseFloat(modelValue);
this.scopedClass = `${this.localName}-${Math.floor(Math.random() * 10000)}`;
this.__styleTag = document.createElement('style');
}
connectedCallback() {
super.connectedCallback();
/* eslint-disable-next-line wc/no-self-class */
this.classList.add(this.scopedClass);
@ -47,38 +78,34 @@ export class LionInputRange extends LocalizeMixin(LionInput) {
}
disconnectedCallback() {
if (super.disconnectedCallback) super.disconnectedCallback();
super.disconnectedCallback();
this.__teardownStyleTag();
}
constructor() {
super();
this.parser = modelValue => parseFloat(modelValue);
this.scopedClass = `${this.localName}-${Math.floor(Math.random() * 10000)}`;
}
/** @param {import('lit-element').PropertyValues } changedProperties */
updated(changedProperties) {
super.updated(changedProperties);
if (changedProperties.has('min')) {
this._inputNode.min = this.min;
this._inputNode.min = `${this.min}`;
}
if (changedProperties.has('max')) {
this._inputNode.max = this.max;
this._inputNode.max = `${this.max}`;
}
if (changedProperties.has('step')) {
this._inputNode.step = this.step;
this._inputNode.step = `${this.step}`;
}
}
/** @param {import('lit-element').PropertyValues } changedProperties */
firstUpdated(changedProperties) {
super.firstUpdated(changedProperties);
if (changedProperties.has('modelValue')) {
// TODO: find out why this hack is needed to display the initial modelValue
this.updateComplete.then(() => {
this._inputNode.value = this.modelValue;
this._inputNode.value = `${this.modelValue}`;
});
}
}
@ -86,7 +113,7 @@ export class LionInputRange extends LocalizeMixin(LionInput) {
_inputGroupTemplate() {
return html`
<div>
<span class="input-range__value">${formatNumber(this.formattedValue)}</span>
<span class="input-range__value">${formatNumber(parseFloat(this.formattedValue))}</span>
<span class="input-range__unit">${this.unit}</span>
</div>
<div class="input-group">
@ -117,8 +144,9 @@ export class LionInputRange extends LocalizeMixin(LionInput) {
}
__setupStyleTag() {
this.__styleTag = document.createElement('style');
this.__styleTag.textContent = this.constructor.rangeStyles(unsafeCSS(this.scopedClass));
this.__styleTag.textContent = /** @type {typeof LionInputRange} */ (this.constructor)
.rangeStyles(unsafeCSS(this.scopedClass))
.toString();
this.insertBefore(this.__styleTag, this.childNodes[0]);
}

View file

@ -1,7 +1,13 @@
import { expect, fixture, nextFrame, html } from '@open-wc/testing';
import { expect, fixture as _fixture, nextFrame, html } from '@open-wc/testing';
import '../lion-input-range.js';
/**
* @typedef {import('../src/LionInputRange').LionInputRange} LionInputRange
* @typedef {import('lit-html').TemplateResult} TemplateResult
*/
const fixture = /** @type {(arg: TemplateResult|string) => Promise<LionInputRange>} */ (_fixture);
describe('<lion-input-range>', () => {
it('has a type = range', async () => {
const el = await fixture(`<lion-input-range></lion-input-range>`);
@ -41,35 +47,41 @@ describe('<lion-input-range>', () => {
const el = await fixture(html`
<lion-input-range .modelValue=${75} unit="${`%`}"></lion-input-range>
`);
expect(el.shadowRoot.querySelector('.input-range__value').innerText).to.equal('75');
expect(el.shadowRoot.querySelector('.input-range__unit').innerText).to.equal('%');
expect(
/** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.input-range__value')).innerText,
).to.equal('75');
expect(
/** @type {HTMLElement} */ (el.shadowRoot?.querySelector('.input-range__unit')).innerText,
).to.equal('%');
});
it('displays 2 tick labels (min and max values) by default', async () => {
const el = await fixture(`<lion-input-range min="100" max="200"></lion-input-range>`);
expect(el.shadowRoot.querySelectorAll('.input-range__limits span').length).to.equal(2);
expect(el.shadowRoot.querySelectorAll('.input-range__limits span')[0].innerText).to.equal(
el.min,
);
expect(el.shadowRoot.querySelectorAll('.input-range__limits span')[1].innerText).to.equal(
el.max,
);
expect(el.shadowRoot?.querySelectorAll('.input-range__limits span').length).to.equal(2);
expect(
/** @type {HTMLElement} */ (el.shadowRoot?.querySelectorAll('.input-range__limits span')[0])
.innerText,
).to.equal(el.min.toString());
expect(
/** @type {HTMLElement} */ (el.shadowRoot?.querySelectorAll('.input-range__limits span')[1])
.innerText,
).to.equal(el.max.toString());
});
it('update min and max attributes when min and max property change', async () => {
const el = await fixture(`<lion-input-range min="100" max="200"></lion-input-range>`);
el.min = '120';
el.max = '220';
el.min = 120;
el.max = 220;
await nextFrame(); // sync to native element takes some time
expect(el._inputNode.min).to.equal(el.min);
expect(el._inputNode.max).to.equal(el.max);
expect(el._inputNode.min).to.equal(el.min.toString());
expect(el._inputNode.max).to.equal(el.max.toString());
});
it('can hide the tick labels', async () => {
const el = await fixture(
`<lion-input-range min="100" max="200" no-min-max-labels></lion-input-range>`,
);
expect(el.shadowRoot.querySelectorAll('.input-group__input')[0]).dom.to.equal(`
expect(el.shadowRoot?.querySelectorAll('.input-group__input')[0]).dom.to.equal(`
<div class="input-group__input">
<slot name="input"></slot>
</div>

View file

@ -6,8 +6,8 @@ import { IsNumber, MinNumber, MaxNumber } from '@lion/form-core';
* `LionInputStepper` is a class for custom input-stepper element (`<lion-input-stepper>` web component).
*
* @customElement lion-input-stepper
* @extends LitElement
*/
// @ts-expect-error false positive for incompatible static get properties. Lit-element merges super properties already for you.
export class LionInputStepper extends LionInput {
static get styles() {
return [
@ -22,30 +22,53 @@ export class LionInputStepper extends LionInput {
static get properties() {
return {
min: Number,
max: Number,
step: Number,
modelValue: Number,
__disableIncrementor: Boolean,
__disableDecrementor: Boolean,
min: {
type: Number,
reflect: true,
},
max: {
type: Number,
reflect: true,
},
step: {
type: Number,
reflect: true,
},
__disableIncrementor: { attribute: false },
__disableDecrementor: { attribute: false },
};
}
/**
* @returns {number}
*/
get currentValue() {
return parseFloat(this.value || 0);
return parseFloat(this.value) || 0;
}
constructor() {
super();
/** @param {string} modelValue */
this.parser = modelValue => parseFloat(modelValue);
this.__disableIncrementor = false;
this.__disableDecrementor = false;
this.min = Infinity;
this.max = Infinity;
this.step = 1;
this.values = {
max: this.max,
min: this.min,
step: this.step,
};
}
connectedCallback() {
if (super.connectedCallback) {
super.connectedCallback();
}
super.connectedCallback();
this.values = {
max: this.max,
min: this.min,
step: this.step,
};
this.role = 'spinbutton';
this.addEventListener('keydown', this.__keyDownHandler);
this._inputNode.setAttribute('inputmode', 'decimal');
@ -56,29 +79,27 @@ export class LionInputStepper extends LionInput {
}
disconnectedCallback() {
if (super.disconnectedCallback) {
super.disconnectedCallback();
}
super.disconnectedCallback();
this.removeEventListener('keydown', this.__keyDownHandler);
}
/**
* Update native input values
* @param {Object} changedProps - changed props
*/
updated(changedProps) {
super.updated(changedProps);
/** @param {import('lit-element').PropertyValues } changedProperties */
updated(changedProperties) {
super.updated(changedProperties);
if (changedProps.has('min')) {
this._inputNode.min = this.min;
if (changedProperties.has('min')) {
this._inputNode.min = `${this.min}`;
this.values.min = this.min;
}
if (changedProps.has('max')) {
this._inputNode.max = this.max;
if (changedProperties.has('max')) {
this._inputNode.max = `${this.max}`;
this.values.max = this.max;
}
if (changedProps.has('step')) {
this._inputNode.step = this.step;
if (changedProperties.has('step')) {
this._inputNode.step = `${this.step}`;
this.values.step = this.step;
}
}
@ -87,27 +108,23 @@ export class LionInputStepper extends LionInput {
* @private
*/
__setAriaLabelsAndValidator() {
this.values = {
max: parseFloat(this.max || Infinity),
min: parseFloat(this.min || Infinity),
step: parseFloat(this.step),
};
const ariaAttributes = {
'aria-valuemax': this.values.max,
'aria-valuemin': this.values.min,
};
let validators = Object.entries(ariaAttributes)
const minMaxValidators = /** @type {(MaxNumber | MinNumber)[]} */ (Object.entries(
ariaAttributes,
)
.map(([key, val]) => {
if (val !== Infinity) {
this.setAttribute(key, val);
this.setAttribute(key, `${val}`);
return key === 'aria-valuemax' ? new MaxNumber(val) : new MinNumber(val);
}
return null;
})
.filter(validator => validator);
validators = [new IsNumber(), ...validators];
.filter(validator => validator !== null));
const validators = [new IsNumber(), ...minMaxValidators];
this.defaultValidators.push(...validators);
}
@ -134,7 +151,7 @@ export class LionInputStepper extends LionInput {
const { min, max } = this.values;
this.__disableIncrementor = this.currentValue >= max && max !== Infinity;
this.__disableDecrementor = this.currentValue <= min && min !== Infinity;
this.setAttribute('aria-valuenow', this.currentValue);
this.setAttribute('aria-valuenow', `${this.currentValue}`);
this.dispatchEvent(
new CustomEvent('user-input-changed', {
bubbles: true,
@ -150,7 +167,7 @@ export class LionInputStepper extends LionInput {
const { step, max } = this.values;
const newValue = this.currentValue + step;
if (newValue <= max || max === Infinity) {
this.value = newValue;
this.value = `${newValue}`;
this.__toggleSpinnerButtonsState();
}
}
@ -163,7 +180,7 @@ export class LionInputStepper extends LionInput {
const { step, min } = this.values;
const newValue = this.currentValue - step;
if (newValue >= min || min === Infinity) {
this.value = newValue;
this.value = `${newValue}`;
this.__toggleSpinnerButtonsState();
}
}

View file

@ -1,6 +1,12 @@
import { expect, fixture, nextFrame, html } from '@open-wc/testing';
import { expect, fixture as _fixture, nextFrame, html } from '@open-wc/testing';
import '../lion-input-stepper.js';
/**
* @typedef {import('../src/LionInputStepper').LionInputStepper} LionInputStepper
* @typedef {import('lit-html').TemplateResult} TemplateResult
*/
const fixture = /** @type {(arg: TemplateResult|string) => Promise<LionInputStepper>} */ (_fixture);
const defaultInputStepper = html`
<lion-input-stepper name="year" label="Years"></lion-input-stepper>
`;
@ -18,26 +24,26 @@ describe('<lion-input-stepper>', () => {
it('should increment the value to 1 on + button click', async () => {
const el = await fixture(defaultInputStepper);
expect(el.value).to.equal('');
const incrementButton = el.shadowRoot.querySelector('[name=increment]');
incrementButton.dispatchEvent(new Event('click'));
const incrementButton = el.shadowRoot?.querySelector('[name=increment]');
incrementButton?.dispatchEvent(new Event('click'));
expect(el.value).to.equal('1');
});
it('should decrement the value to -1 on - button click', async () => {
const el = await fixture(defaultInputStepper);
expect(el.value).to.equal('');
const incrementButton = el.shadowRoot.querySelector('[name=decrement]');
incrementButton.dispatchEvent(new Event('click'));
const incrementButton = el.shadowRoot?.querySelector('[name=decrement]');
incrementButton?.dispatchEvent(new Event('click'));
expect(el.value).to.equal('-1');
});
it('should update min and max attributes when min and max property change', async () => {
const el = await fixture(inputStepperWithAttrs);
el.min = '100';
el.max = '200';
el.min = 100;
el.max = 200;
await nextFrame();
expect(el._inputNode.min).to.equal(el.min);
expect(el._inputNode.max).to.equal(el.max);
expect(el._inputNode.min).to.equal(el.min.toString());
expect(el._inputNode.max).to.equal(el.max.toString());
});
});
@ -60,8 +66,8 @@ describe('<lion-input-stepper>', () => {
it('updates aria-valuenow when stepper is changed', async () => {
const el = await fixture(inputStepperWithAttrs);
const incrementButton = el.shadowRoot.querySelector('[name=increment]');
incrementButton.dispatchEvent(new Event('click'));
const incrementButton = el.shadowRoot?.querySelector('[name=increment]');
incrementButton?.dispatchEvent(new Event('click'));
expect(el).to.have.attribute('aria-valuenow', '1');
});
});

View file

@ -26,6 +26,11 @@
"packages/form-core/**/*.js",
"packages/input/**/*.js",
"packages/input-amount/**/*.js",
"packages/input-date/**/*.js",
"packages/input-email/**/*.js",
"packages/input-iban/**/*.js",
"packages/input-range/**/*.js",
"packages/input-stepper/**/*.js",
"packages/listbox/src/*.js",
"packages/localize/**/*.js",
"packages/overlays/**/*.js",