Added stepUp, stepDown and showPicker functions (#1013)

* added stepUp and stepDown to sl-range

* step function & default props tests for sl-range

* stepUp, stepDown & showPicker functions for input

* step functions & default props tests for sl-input

* made name and placeholder default to empty string

* updated changelog

Co-authored-by: Cory LaViska <cory@abeautifulsite.net>
This commit is contained in:
Bünyamin Eskiocak
2022-11-21 20:41:09 +03:00
committed by GitHub
parent bc6a813c46
commit a706e69be6
7 changed files with 195 additions and 5 deletions

View File

@@ -10,6 +10,43 @@ describe('<sl-input>', () => {
await expect(el).to.be.accessible();
});
it('default properties', async () => {
const el = await fixture<SlInput>(html` <sl-input></sl-input> `);
expect(el.type).to.equal('text');
expect(el.size).to.equal('medium');
expect(el.name).to.equal('');
expect(el.value).to.equal('');
expect(el.defaultValue).to.equal('');
expect(el.filled).to.be.false;
expect(el.pill).to.be.false;
expect(el.label).to.equal('');
expect(el.helpText).to.equal('');
expect(el.clearable).to.be.false;
expect(el.passwordToggle).to.be.false;
expect(el.passwordVisible).to.be.false;
expect(el.noSpinButtons).to.be.false;
expect(el.placeholder).to.equal('');
expect(el.disabled).to.be.false;
expect(el.readonly).to.be.false;
expect(el.minlength).to.be.undefined;
expect(el.maxlength).to.be.undefined;
expect(el.min).to.be.undefined;
expect(el.max).to.be.undefined;
expect(el.step).to.be.undefined;
expect(el.pattern).to.be.undefined;
expect(el.required).to.be.false;
expect(el.autocapitalize).to.be.undefined;
expect(el.autocorrect).to.be.undefined;
expect(el.autocomplete).to.be.undefined;
expect(el.autofocus).to.be.undefined;
expect(el.enterkeyhint).to.be.undefined;
expect(el.spellcheck).to.be.undefined;
expect(el.inputmode).to.be.undefined;
expect(el.valueAsDate).to.be.null;
expect(isNaN(el.valueAsNumber)).to.be.true;
});
it('should be disabled with the disabled attribute', async () => {
const el = await fixture<SlInput>(html` <sl-input disabled></sl-input> `);
const input = el.shadowRoot!.querySelector<HTMLInputElement>('[part~="input"]')!;
@@ -208,5 +245,52 @@ describe('<sl-input>', () => {
await el.updateComplete;
expect(el.invalid).to.be.true;
});
it('should increment by step if stepUp() is called', async () => {
const el = await fixture<SlInput>(html` <sl-input type="number" step="2" value="2"></sl-input> `);
el.stepUp();
await el.updateComplete;
expect(el.value).to.equal('4');
});
it('should decrement by step if stepDown() is called', async () => {
const el = await fixture<SlInput>(html` <sl-input type="number" step="2" value="2"></sl-input> `);
el.stepDown();
await el.updateComplete;
expect(el.value).to.equal('0');
});
it('should fire sl-input and sl-change if stepUp() is called', async () => {
const el = await fixture<SlInput>(html` <sl-input type="number" step="2" value="2"></sl-input> `);
const inputHandler = sinon.spy();
const changeHandler = sinon.spy();
el.addEventListener('sl-input', inputHandler);
el.addEventListener('sl-change', changeHandler);
el.stepUp();
await waitUntil(() => inputHandler.calledOnce);
await waitUntil(() => changeHandler.calledOnce);
expect(inputHandler).to.have.been.calledOnce;
expect(changeHandler).to.have.been.calledOnce;
});
it('should fire sl-input and sl-change if stepDown() is called', async () => {
const el = await fixture<SlInput>(html` <sl-input type="number" step="2" value="2"></sl-input> `);
const inputHandler = sinon.spy();
const changeHandler = sinon.spy();
el.addEventListener('sl-input', inputHandler);
el.addEventListener('sl-change', changeHandler);
el.stepUp();
await waitUntil(() => inputHandler.calledOnce);
await waitUntil(() => changeHandler.calledOnce);
expect(changeHandler).to.have.been.calledOnce;
});
});
});

View File

@@ -88,7 +88,7 @@ export default class SlInput extends ShoelaceElement implements ShoelaceFormCont
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
/** The input's name attribute. */
@property() name: string;
@property() name = '';
/** The input's value attribute. */
@property() value = '';
@@ -121,7 +121,7 @@ export default class SlInput extends ShoelaceElement implements ShoelaceFormCont
@property({ attribute: 'no-spin-buttons', type: Boolean }) noSpinButtons = false;
/** The input's placeholder text. */
@property() placeholder: string;
@property() placeholder = '';
/** Disables the input. */
@property({ type: Boolean, reflect: true }) disabled = false;
@@ -247,6 +247,33 @@ export default class SlInput extends ShoelaceElement implements ShoelaceFormCont
}
}
/** Displays the browser picker for an input element (only works if the browser supports it for the input type). */
showPicker() {
if ('showPicker' in HTMLInputElement.prototype) {
this.input.showPicker();
}
}
/** Increments the value of a numeric input type by the value of the step attribute. */
stepUp() {
this.input.stepUp();
if (this.value !== this.input.value) {
this.value = this.input.value;
this.emit('sl-input');
this.emit('sl-change');
}
}
/** Decrements the value of a numeric input type by the value of the step attribute. */
stepDown() {
this.input.stepDown();
if (this.value !== this.input.value) {
this.value = this.input.value;
this.emit('sl-input');
this.emit('sl-change');
}
}
/** Checks for validity but does not show the browser's validation message. */
checkValidity() {
return this.input.checkValidity();

View File

@@ -1,4 +1,5 @@
import { expect, fixture, html, oneEvent } from '@open-wc/testing';
import { expect, fixture, html, oneEvent, waitUntil } from '@open-wc/testing';
import sinon from 'sinon';
import { serialize } from '../../utilities/form';
import type SlRange from './range';
@@ -8,6 +9,22 @@ describe('<sl-range>', () => {
await expect(el).to.be.accessible();
});
it('default properties', async () => {
const el = await fixture<SlRange>(html` <sl-range></sl-range> `);
expect(el.name).to.equal('');
expect(el.value).to.equal(0);
expect(el.label).to.equal('');
expect(el.helpText).to.equal('');
expect(el.disabled).to.be.false;
expect(el.invalid).to.be.false;
expect(el.min).to.equal(0);
expect(el.max).to.equal(100);
expect(el.step).to.equal(1);
expect(el.tooltip).to.equal('top');
expect(el.defaultValue).to.equal(0);
});
it('should be disabled with the disabled attribute', async () => {
const el = await fixture<SlRange>(html` <sl-range disabled></sl-range> `);
const input = el.shadowRoot!.querySelector<HTMLInputElement>('[part~="input"]')!;
@@ -15,6 +32,44 @@ describe('<sl-range>', () => {
expect(input.disabled).to.be.true;
});
describe('step', () => {
it('should increment by step if stepUp() is called', async () => {
const el = await fixture<SlRange>(html` <sl-range step="2" value="2"></sl-range> `);
el.stepUp();
await el.updateComplete;
expect(el.value).to.equal(4);
});
it('should decrement by step if stepDown() is called', async () => {
const el = await fixture<SlRange>(html` <sl-range step="2" value="2"></sl-range> `);
el.stepDown();
await el.updateComplete;
expect(el.value).to.equal(0);
});
it('should fire sl-change if stepUp() is called', async () => {
const el = await fixture<SlRange>(html` <sl-range step="2" value="2"></sl-range> `);
const changeHandler = sinon.spy();
el.addEventListener('sl-change', changeHandler);
el.stepUp();
await waitUntil(() => changeHandler.calledOnce);
expect(changeHandler).to.have.been.calledOnce;
});
it('should fire sl-change if stepDown() is called', async () => {
const el = await fixture<SlRange>(html` <sl-range step="2" value="2"></sl-range> `);
const changeHandler = sinon.spy();
el.addEventListener('sl-change', changeHandler);
el.stepUp();
await waitUntil(() => changeHandler.calledOnce);
expect(changeHandler).to.have.been.calledOnce;
});
});
describe('when serializing', () => {
it('should serialize its name and value with FormData', async () => {
const form = await fixture<HTMLFormElement>(html` <form><sl-range name="a" value="1"></sl-range></form> `);

View File

@@ -123,6 +123,24 @@ export default class SlRange extends ShoelaceElement implements ShoelaceFormCont
this.input.blur();
}
/** Increments the value of the input by the value of the step attribute. */
stepUp() {
this.input.stepUp();
if (this.value !== Number(this.input.value)) {
this.value = Number(this.input.value);
this.emit('sl-change');
}
}
/** Decrements the value of the input by the value of the step attribute. */
stepDown() {
this.input.stepDown();
if (this.value !== Number(this.input.value)) {
this.value = Number(this.input.value);
this.emit('sl-change');
}
}
/** Checks for validity but does not show the browser's validation message. */
checkValidity() {
return this.input.checkValidity();

View File

@@ -51,7 +51,7 @@ export default class SlTextarea extends ShoelaceElement implements ShoelaceFormC
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
/** The textarea's name attribute. */
@property() name: string;
@property() name = '';
/** The textarea's value attribute. */
@property() value = '';
@@ -66,7 +66,7 @@ export default class SlTextarea extends ShoelaceElement implements ShoelaceFormC
@property({ attribute: 'help-text' }) helpText = '';
/** The textarea's placeholder text. */
@property() placeholder: string;
@property() placeholder = '';
/** The number of rows to display by default. */
@property({ type: Number }) rows = 4;

View File

@@ -10,3 +10,7 @@ declare namespace Chai {
accessible: (options?: Object) => PromiseLike<Assertion>;
}
}
interface HTMLInputElement {
showPicker: () => void;
}