diff --git a/src/components/input/input.test.ts b/src/components/input/input.test.ts index 12e03a8c4..1e5c5aa37 100644 --- a/src/components/input/input.test.ts +++ b/src/components/input/input.test.ts @@ -1,5 +1,5 @@ // eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment -import { expect, fixture, html, oneEvent, waitUntil } from '@open-wc/testing'; +import { elementUpdated, expect, fixture, html, oneEvent, waitUntil } from '@open-wc/testing'; import { getFormControls, serialize } from '../../../dist/shoelace.js'; import { runFormControlBaseTests } from '../../internal/test/form-control-base-tests'; import { sendKeys } from '@web/test-runner-commands'; // must come from the same module @@ -66,21 +66,69 @@ describe('', () => { describe('value methods', () => { it('should set the value as a date when using valueAsDate', async () => { - const el = await fixture(html` `); + const el = document.createElement(`sl-input`); + el.type = 'date'; const today = new Date(); el.valueAsDate = today; + // Test before we render in the dom expect(el.value).to.equal(today.toISOString().split('T')[0]); + expect(el.valueAsDate.toISOString().split('T')[0]).to.equal(today.toISOString().split('T')[0]); + + document.body.appendChild(el); + + await elementUpdated(el); + + // Update valueAsDate after we render to make sure it reflects properly + el.valueAsDate = null; + + await elementUpdated(el); + + expect(el.value).to.equal(''); + expect(el.valueAsDate).to.equal(null); + + // Update again with a real date to make sure it works + el.valueAsDate = today; + + await elementUpdated(el); + + expect(el.value).to.equal(today.toISOString().split('T')[0]); + expect(el.valueAsDate.toISOString().split('T')[0]).to.equal(today.toISOString().split('T')[0]); + + el.remove(); }); it('should set the value as a number when using valueAsNumber', async () => { - const el = await fixture(html` `); + const el = document.createElement(`sl-input`); + el.type = 'number'; const num = 12345; el.valueAsNumber = num; expect(el.value).to.equal(num.toString()); + expect(el.valueAsNumber).to.equal(num); + + document.body.appendChild(el); + + await elementUpdated(el); + + // Wait for render, then update the value + const otherNum = 4567; + el.valueAsNumber = otherNum; + + await elementUpdated(el); + + expect(el.value).to.equal(otherNum.toString()); + expect(el.valueAsNumber).to.equal(otherNum); + + // Re-set valueAsNumber and make sure it updates. + el.valueAsNumber = num; + await elementUpdated(el); + expect(el.value).to.equal(num.toString()); + expect(el.valueAsNumber).to.equal(num); + + el.remove(); }); }); diff --git a/src/components/input/input.ts b/src/components/input/input.ts index 54ac09e70..5d42d5499 100644 --- a/src/components/input/input.ts +++ b/src/components/input/input.ts @@ -63,6 +63,9 @@ export default class SlInput extends ShoelaceElement implements ShoelaceFormCont @state() private hasFocus = false; @property() title = ''; // make reactive to pass through + private __numberInput = Object.assign(document.createElement('input'), { type: 'number' }); + private __dateInput = Object.assign(document.createElement('input'), { type: 'date' }); + /** * The type of input. Works the same as a native `` element, but only a subset of types are supported. Defaults * to `text`. @@ -197,32 +200,24 @@ export default class SlInput extends ShoelaceElement implements ShoelaceFormCont /** Gets or sets the current value as a `Date` object. Returns `null` if the value can't be converted. */ get valueAsDate() { - const input = document.createElement('input'); - input.type = 'date'; - input.value = this.value; - return input.valueAsDate; + this.__dateInput.value = this.value; + return this.input?.valueAsDate || this.__dateInput.valueAsDate; } set valueAsDate(newValue: Date | null) { - const input = document.createElement('input'); - input.type = 'date'; - input.valueAsDate = newValue; - this.value = input.value; + this.__dateInput.valueAsDate = newValue; + this.value = this.__dateInput.value; } /** Gets or sets the current value as a number. Returns `NaN` if the value can't be converted. */ get valueAsNumber() { - const input = document.createElement('input'); - input.type = 'number'; - input.value = this.value; - return input.valueAsNumber; + this.__numberInput.value = this.value; + return this.input?.valueAsNumber || this.__numberInput.valueAsNumber; } set valueAsNumber(newValue: number) { - const input = document.createElement('input'); - input.type = 'number'; - input.valueAsNumber = newValue; - this.value = input.value; + this.__numberInput.valueAsNumber = newValue; + this.value = this.__numberInput.value; } /** Gets the validity state object */