Skip to content

Commit

Permalink
Merge branch 'next' of https://github.com/shoelace-style/shoelace int…
Browse files Browse the repository at this point in the history
…o next
  • Loading branch information
claviska committed Nov 21, 2022
2 parents 599373c + a706e69 commit ade05a6
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 6 deletions.
3 changes: 3 additions & 0 deletions docs/resources/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ New versions of Shoelace are released as-needed and generally occur when a criti
- Added `--sl-input-required-content-color` custom property to all form controls [#948](https://github.com/shoelace-style/shoelace/pull/948)
- Added the ability to cancel `sl-show` and `sl-hide` events in `<sl-details>` [#993](https://github.com/shoelace-style/shoelace/issues/993)
- Added `focus()` and `blur()` methods to `<sl-radio-button>`
- Added `stepUp()` and `stepDown()` methods to `<sl-input>` and `<sl-range>` [#1013](https://github.com/shoelace-style/shoelace/pull/1013)
- Added `showPicker()` method to `<sl-input>` [#1013](https://github.com/shoelace-style/shoelace/pull/1013)
- Added the `handle-icon` part to `<sl-image-comparer>`
- Added `caret`, `check`, `grip-vertical`, `indeterminate`, and `radio` icons to the system library and removed `check-lg` [#985](https://github.com/shoelace-style/shoelace/issues/985)
- Added the `loading` attribute to `<sl-avatar>` to allow lazy loading of image avatars [#1006](https://github.com/shoelace-style/shoelace/pull/1006)
- Added `formenctype` attribute to `<sl-button>` [#1009](https://github.com/shoelace-style/shoelace/pull/1009)
- Added `exports` to `package.json` and removed the `main` and `module` properties [#1007](https://github.com/shoelace-style/shoelace/pull/1007)
- Fixed a bug in `<sl-card>` that prevented the border radius to apply correctly to the header [#934](https://github.com/shoelace-style/shoelace/pull/934)
- Fixed a bug in `<sl-button-group>` where the inner border disappeared on focus [#980](https://github.com/shoelace-style/shoelace/pull/980)
Expand Down
4 changes: 4 additions & 0 deletions src/components/button/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ export default class SlButton extends ShoelaceElement implements ShoelaceFormCon
/** Used to override the form owner's `action` attribute. */
@property({ attribute: 'formaction' }) formAction: string;

/** Used to override the form owner's `enctype` attribute. */
@property({ attribute: 'formenctype' })
formEnctype: 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/plain';

/** Used to override the form owner's `method` attribute. */
@property({ attribute: 'formmethod' }) formMethod: 'post' | 'get';

Expand Down
84 changes: 84 additions & 0 deletions src/components/input/input.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"]')!;
Expand Down Expand Up @@ -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;
});
});
});
31 changes: 29 additions & 2 deletions src/components/input/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '';
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
57 changes: 56 additions & 1 deletion src/components/range/range.test.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -8,13 +9,67 @@ 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"]')!;

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> `);
Expand Down
18 changes: 18 additions & 0 deletions src/components/range/range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions src/components/textarea/textarea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '';
Expand All @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/declaration.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ declare namespace Chai {
accessible: (options?: Object) => PromiseLike<Assertion>;
}
}

interface HTMLInputElement {
showPicker: () => void;
}
2 changes: 1 addition & 1 deletion src/internal/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export class FormSubmitController implements ReactiveController {

// Pass form attributes through to the temporary button
if (invoker) {
['formaction', 'formmethod', 'formnovalidate', 'formtarget'].forEach(attr => {
['formaction', 'formenctype', 'formmethod', 'formnovalidate', 'formtarget'].forEach(attr => {
if (invoker.hasAttribute(attr)) {
button.setAttribute(attr, invoker.getAttribute(attr)!);
}
Expand Down

0 comments on commit ade05a6

Please sign in to comment.