From d2035e6a3f9fb573e908e2c5420e6aff1dad1f3d Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Tue, 13 Aug 2019 08:38:39 +0200 Subject: [PATCH] feat(field): add reset method and capture inital model value --- packages/field/src/LionField.js | 10 +++ packages/field/test/lion-field.test.js | 118 ++++++++++++------------- 2 files changed, 68 insertions(+), 60 deletions(-) diff --git a/packages/field/src/LionField.js b/packages/field/src/LionField.js index f9abd5f2d..90b8b2ecb 100644 --- a/packages/field/src/LionField.js +++ b/packages/field/src/LionField.js @@ -101,6 +101,11 @@ export class LionField extends FormControlMixin( this.submitted = false; } + firstUpdated(c) { + super.firstUpdated(c); + this._initialModelValue = this.modelValue; + } + connectedCallback() { // TODO: Normally we put super calls on top for predictability, // here we temporarily need to do attribute delegation before, @@ -164,6 +169,11 @@ export class LionField extends FormControlMixin( this.submitted = false; } + reset() { + this.modelValue = this._initialModelValue; + this.resetInteractionState(); + } + clear() { if (super.clear) { // Let validationMixin and interactionStateMixin clear their diff --git a/packages/field/test/lion-field.test.js b/packages/field/test/lion-field.test.js index 053da22e9..19dd00d00 100644 --- a/packages/field/test/lion-field.test.js +++ b/packages/field/test/lion-field.test.js @@ -31,12 +31,12 @@ beforeEach(() => { describe('', () => { it(`puts a unique id "${tagString}-[hash]" on the native input`, async () => { - const el = await fixture(`<${tagString}>${inputSlotString}`); + const el = await fixture(html`<${tag}>${inputSlot}`); expect(el.$$slot('input').id).to.equal(el._inputId); }); it('fires focus/blur event on host and native input if focused/blurred', async () => { - const el = await fixture(`<${tagString}>${inputSlotString}`); + const el = await fixture(html`<${tag}>${inputSlot}`); const cbFocusHost = sinon.spy(); el.addEventListener('focus', cbFocusHost); const cbFocusNativeInput = sinon.spy(); @@ -68,32 +68,31 @@ describe('', () => { expect(cbBlurNativeInput.callCount).to.equal(2); }); + it('offers simple getter "this.focused" returning true/false for the current focus state', async () => { + const el = await fixture(html`<${tag}>${inputSlot}`); + expect(el.focused).to.equal(false); + await triggerFocusFor(el); + expect(el.focused).to.equal(true); + await triggerBlurFor(el); + expect(el.focused).to.equal(false); + }); + it('can be disabled via attribute', async () => { - const elDisabled = await fixture(`<${tagString} disabled>${inputSlotString}`); + const elDisabled = await fixture(html`<${tag} disabled>${inputSlot}`); expect(elDisabled.disabled).to.equal(true); expect(elDisabled.inputElement.disabled).to.equal(true); }); it('can be disabled via property', async () => { - const el = await fixture(`<${tagString}>${inputSlotString}`); + const el = await fixture(html`<${tag}>${inputSlot}`); el.disabled = true; await el.updateComplete; expect(el.inputElement.disabled).to.equal(true); }); - // classes are added only for backward compatibility - they are deprecated - it('sets a state-disabled class when disabled', async () => { - const el = await fixture(`<${tagString} disabled>${inputSlotString}`); - await el.updateComplete; - expect(el.classList.contains('state-disabled')).to.equal(true); - el.disabled = false; - await el.updateComplete; - expect(el.classList.contains('state-disabled')).to.equal(false); - }); - it('can be cleared which erases value, validation and interaction states', async () => { const el = await fixture( - `<${tagString} value="Some value from attribute">${inputSlotString}`, + html`<${tag} value="Some value from attribute">${inputSlot}`, ); el.clear(); expect(el.value).to.equal(''); @@ -103,35 +102,34 @@ describe('', () => { expect(el.value).to.equal(''); }); + it('can be reset which restores original modelValue', async () => { + const el = await fixture(html` + <${tag} .modelValue="${'foo'}"> + ${inputSlot} + `); + expect(el._initialModelValue).to.equal('foo'); + el.modelValue = 'bar'; + el.reset(); + expect(el.modelValue).to.equal('foo'); + }); + it('reads initial value from attribute value', async () => { - const el = await fixture(`<${tagString} value="one">${inputSlotString}`); + const el = await fixture(html`<${tag} value="one">${inputSlot}`); expect(el.$$slot('input').value).to.equal('one'); }); it('delegates value property', async () => { - const el = await fixture(`<${tagString}>${inputSlotString}`); + const el = await fixture(html`<${tag}>${inputSlot}`); expect(el.$$slot('input').value).to.equal(''); el.value = 'one'; expect(el.value).to.equal('one'); expect(el.$$slot('input').value).to.equal('one'); }); - it('has a name which is reflected to an attribute and is synced down to the native input', async () => { - const el = await fixture(`<${tagString}>${inputSlotString}`); - expect(el.name).to.equal(''); - expect(el.getAttribute('name')).to.equal(''); - expect(el.inputElement.getAttribute('name')).to.equal(''); - - el.name = 'foo'; - await el.updateComplete; - expect(el.getAttribute('name')).to.equal('foo'); - expect(el.inputElement.getAttribute('name')).to.equal('foo'); - }); - // TODO: find out if we could put all listeners on this.value (instead of this.inputElement.value) // and make it act on this.value again it('has a class "state-filled" if this.value is filled', async () => { - const el = await fixture(`<${tagString} value="filled">${inputSlotString}`); + const el = await fixture(html`<${tag} value="filled">${inputSlot}`); expect(el.classList.contains('state-filled')).to.equal(true); el.value = ''; await el.updateComplete; @@ -142,7 +140,7 @@ describe('', () => { }); it('preserves the caret position on value change for native text fields (input|textarea)', async () => { - const el = await fixture(`<${tagString}>${inputSlotString}`); + const el = await fixture(html`<${tag}>${inputSlot}`); await triggerFocusFor(el); await el.updateComplete; el.inputElement.value = 'hello world'; @@ -155,7 +153,7 @@ describe('', () => { // TODO: add pointerEvents test for disabled it('has a class "state-disabled"', async () => { - const el = await fixture(`<${tagString}>${inputSlotString}`); + const el = await fixture(html`<${tag}>${inputSlot}`); expect(el.classList.contains('state-disabled')).to.equal(false); expect(el.inputElement.hasAttribute('disabled')).to.equal(false); @@ -166,7 +164,7 @@ describe('', () => { expect(el.classList.contains('state-disabled')).to.equal(true); expect(el.inputElement.hasAttribute('disabled')).to.equal(true); - const disabledel = await fixture(`<${tagString} disabled>${inputSlotString}`); + const disabledel = await fixture(html`<${tag} disabled>${inputSlot}`); expect(disabledel.classList.contains('state-disabled')).to.equal(true); expect(disabledel.inputElement.hasAttribute('disabled')).to.equal(true); }); @@ -186,12 +184,12 @@ describe('', () => {
[feedback] ~~~`, async () => { - const el = await fixture(`<${tagString}> + const el = await fixture(html`<${tag}> - ${inputSlotString} + ${inputSlot} Enter your Name No name entered - + `); const nativeInput = el.$$slot('input'); @@ -202,13 +200,13 @@ describe('', () => { it(`allows additional slots (prefix, suffix, before, after) to be included in labelledby (via attribute data-label) and in describedby (via attribute data-description)`, async () => { - const el = await fixture(`<${tagString}> - ${inputSlotString} + const el = await fixture(html`<${tag}> + ${inputSlot} [before] [after] [prefix] [suffix] - + `); const nativeInput = el.$$slot('input'); @@ -223,45 +221,45 @@ describe('', () => { // TODO: put this test on FormControlMixin test once there it(`allows to add to aria description or label via addToAriaLabel() and addToAriaDescription()`, async () => { - const wrapper = await fixture(` + const wrapper = await fixture(html`
- <${tagString}> - ${inputSlotString} + <${tag}> + ${inputSlot}
Added to description by default
- +
This also needs to be read whenever the input has focus
Same for this
`); - const el = wrapper.querySelector(`${tagString}`); + const el = wrapper.querySelector(tagString); // wait until the field element is done rendering await el.updateComplete; + await el.updateComplete; const { inputElement } = el; - const get = by => inputElement.getAttribute(`aria-${by}`); // 1. addToAriaLabel() // Check if the aria attr is filled initially - expect(get('labelledby')).to.contain(`label-${el._inputId}`); + expect(inputElement.getAttribute('aria-labelledby')).to.contain(`label-${el._inputId}`); el.addToAriaLabel('additionalLabel'); // Now check if ids are added to the end (not overridden) - expect(get('labelledby')).to.contain(`label-${el._inputId}`); + expect(inputElement.getAttribute('aria-labelledby')).to.contain(`label-${el._inputId}`); // Should be placed in the end expect( - get('labelledby').indexOf(`label-${el._inputId}`) < - get('labelledby').indexOf('additionalLabel'), + inputElement.getAttribute('aria-labelledby').indexOf(`label-${el._inputId}`) < + inputElement.getAttribute('aria-labelledby').indexOf('additionalLabel'), ); // 2. addToAriaDescription() // Check if the aria attr is filled initially - expect(get('describedby')).to.contain(`feedback-${el._inputId}`); + expect(inputElement.getAttribute('aria-describedby')).to.contain(`feedback-${el._inputId}`); el.addToAriaDescription('additionalDescription'); // Now check if ids are added to the end (not overridden) - expect(get('describedby')).to.contain(`feedback-${el._inputId}`); + expect(inputElement.getAttribute('aria-describedby')).to.contain(`feedback-${el._inputId}`); // Should be placed in the end expect( - get('describedby').indexOf(`feedback-${el._inputId}`) < - get('describedby').indexOf('additionalDescription'), + inputElement.getAttribute('aria-describedby').indexOf(`feedback-${el._inputId}`) < + inputElement.getAttribute('aria-describedby').indexOf('additionalDescription'), ); }); }); @@ -285,7 +283,7 @@ describe('', () => { function hasX(str) { return { hasX: str.indexOf('x') > -1 }; } - const el = await fixture(`<${tagString}>${inputSlotString}`); + const el = await fixture(html`<${tag}>${inputSlot}`); const feedbackEl = el._feedbackElement; el.modelValue = 'a@b.nl'; @@ -355,17 +353,17 @@ describe('', () => { describe(`Content projection${nameSuffix}`, () => { it('renders correctly all slot elements in light DOM', async () => { - const el = await fixture(` - <${tagString}> + const el = await fixture(html` + <${tag}> - ${inputSlotString} + ${inputSlot} [help-text] [before] [after] [prefix] [suffix] [feedback] - + `); const names = [ @@ -388,9 +386,9 @@ describe('', () => { }); }); - describe(`Delegation${nameSuffix}`, () => { + describe('Delegation', () => { it('delegates property value', async () => { - const el = await fixture(`<${tagString}>${inputSlotString}`); + const el = await fixture(html`<${tag}>${inputSlot}`); expect(el.inputElement.value).to.equal(''); el.value = 'one'; expect(el.value).to.equal('one');