@lion/field
Advanced tools
Comparing version 0.1.44 to 0.2.0
@@ -6,2 +6,18 @@ # Change Log | ||
# [0.2.0](https://github.com/ing-bank/lion/compare/@lion/field@0.1.44...@lion/field@0.2.0) (2019-08-15) | ||
### Bug Fixes | ||
* **field:** cleaned up old register code ([95a9ce7](https://github.com/ing-bank/lion/commit/95a9ce7)) | ||
### Features | ||
* **field:** add reset method and capture inital model value ([d2035e6](https://github.com/ing-bank/lion/commit/d2035e6)) | ||
## [0.1.44](https://github.com/ing-bank/lion/compare/@lion/field@0.1.43...@lion/field@0.1.44) (2019-08-15) | ||
@@ -8,0 +24,0 @@ |
{ | ||
"name": "@lion/field", | ||
"version": "0.1.44", | ||
"version": "0.2.0", | ||
"description": "Fields are the most fundamental building block of the Form System", | ||
@@ -45,3 +45,3 @@ "author": "ing-bank", | ||
}, | ||
"gitHead": "d406de45d6c0980fb4c1f2186ea5585667045074" | ||
"gitHead": "59053f161c74e22efd5ba231a2e40a8346786ffc" | ||
} |
@@ -32,6 +32,6 @@ import { dedupeMixin } from '@lion/core'; | ||
if (formRegistrarManager.ready) { | ||
this._registerFormElement(); | ||
this._dispatchRegistration(); | ||
} else { | ||
formRegistrarManager.addEventListener('all-forms-open-for-registration', () => { | ||
this._registerFormElement(); | ||
this._dispatchRegistration(); | ||
}); | ||
@@ -41,7 +41,2 @@ } | ||
_registerFormElement() { | ||
this._dispatchRegistration(); | ||
this._requestParentFormGroupUpdateOfResetModelValue(); | ||
} | ||
_dispatchRegistration() { | ||
@@ -61,14 +56,3 @@ this.dispatchEvent( | ||
} | ||
/** | ||
* Makes sure our parentFormGroup has the most up to date resetModelValue | ||
* FormGroups will call the same on their parentFormGroup so the full tree gets the correct | ||
* values. | ||
*/ | ||
_requestParentFormGroupUpdateOfResetModelValue() { | ||
if (this.__parentFormGroup && this.__parentFormGroup._updateResetModelValue) { | ||
this.__parentFormGroup._updateResetModelValue(); | ||
} | ||
} | ||
}, | ||
); |
@@ -104,2 +104,7 @@ import { SlotMixin, LitElement } from '@lion/core'; | ||
firstUpdated(c) { | ||
super.firstUpdated(c); | ||
this._initialModelValue = this.modelValue; | ||
} | ||
connectedCallback() { | ||
@@ -121,10 +126,2 @@ // TODO: Normally we put super calls on top for predictability, | ||
super.disconnectedCallback(); | ||
if (this.__parentFormGroup) { | ||
const event = new CustomEvent('form-element-unregister', { | ||
detail: { element: this }, | ||
bubbles: true, | ||
}); | ||
this.__parentFormGroup.dispatchEvent(event); | ||
} | ||
this.inputElement.removeEventListener('change', this._onChange); | ||
@@ -169,2 +166,7 @@ } | ||
reset() { | ||
this.modelValue = this._initialModelValue; | ||
this.resetInteractionState(); | ||
} | ||
clear() { | ||
@@ -171,0 +173,0 @@ if (super.clear) { |
@@ -1,129 +0,19 @@ | ||
import { expect, fixture, html, defineCE, unsafeStatic } from '@open-wc/testing'; | ||
import sinon from 'sinon'; | ||
import { LitElement, UpdatingElement } from '@lion/core'; | ||
import { html } from '@open-wc/testing'; | ||
import { UpdatingElement, LitElement } from '@lion/core'; | ||
import { runRegistrationSuite } from '../test-suites/FormRegistrationMixins.suite.js'; | ||
import { formRegistrarManager } from '../src/formRegistrarManager.js'; | ||
import { FormRegisteringMixin } from '../src/FormRegisteringMixin.js'; | ||
import { FormRegistrarMixin } from '../src/FormRegistrarMixin.js'; | ||
runRegistrationSuite({ | ||
suffix: 'with UpdatingElement', | ||
baseElement: UpdatingElement, | ||
}); | ||
describe('FormRegistrationMixins', () => { | ||
before(async () => { | ||
const FormRegistrarEl = class extends FormRegistrarMixin(UpdatingElement) {}; | ||
customElements.define('form-registrar', FormRegistrarEl); | ||
const FormRegisteringEl = class extends FormRegisteringMixin(UpdatingElement) {}; | ||
customElements.define('form-registering', FormRegisteringEl); | ||
}); | ||
it('can register a formElement', async () => { | ||
const el = await fixture(html` | ||
<form-registrar> | ||
<form-registering></form-registering> | ||
</form-registrar> | ||
`); | ||
await el.registrationReady; | ||
expect(el.formElements.length).to.equal(1); | ||
}); | ||
it('supports nested registrar', async () => { | ||
const el = await fixture(html` | ||
<form-registrar> | ||
<form-registrar> | ||
<form-registering></form-registering> | ||
</form-registrar> | ||
</form-registrar> | ||
`); | ||
await el.registrationReady; | ||
expect(el.formElements.length).to.equal(1); | ||
expect(el.querySelector('form-registrar').formElements.length).to.equal(1); | ||
}); | ||
it('forgets disconnected registrars', async () => { | ||
const el = await fixture(html` | ||
<form-registrar> | ||
<form-registrar> | ||
<form-registering></form-registering> | ||
</form-registrar> | ||
</form-registrar> | ||
`); | ||
const secondRegistrar = await fixture(html` | ||
<form-registrar> | ||
<form-registering></form-registering> | ||
</form-registrar> | ||
`); | ||
el.appendChild(secondRegistrar); | ||
expect(formRegistrarManager.__elements.length).to.equal(3); | ||
el.removeChild(secondRegistrar); | ||
expect(formRegistrarManager.__elements.length).to.equal(2); | ||
}); | ||
it('works for component that have a delayed render', async () => { | ||
const tagWrapperString = defineCE( | ||
class extends FormRegistrarMixin(LitElement) { | ||
async performUpdate() { | ||
await new Promise(resolve => setTimeout(() => resolve(), 10)); | ||
await super.performUpdate(); | ||
} | ||
render() { | ||
return html` | ||
<slot></slot> | ||
`; | ||
} | ||
}, | ||
); | ||
const tagWrapper = unsafeStatic(tagWrapperString); | ||
const registerSpy = sinon.spy(); | ||
const el = await fixture(html` | ||
<${tagWrapper} @form-element-register=${registerSpy}> | ||
<form-registering></form-registering> | ||
</${tagWrapper}> | ||
`); | ||
await el.registrationReady; | ||
expect(el.formElements.length).to.equal(1); | ||
}); | ||
it('requests update of the resetModelValue function of its parent formGroup', async () => { | ||
const ParentFormGroupClass = class extends FormRegistrarMixin(LitElement) { | ||
_updateResetModelValue() { | ||
this.resetModelValue = 'foo'; | ||
} | ||
}; | ||
const ChildFormGroupClass = class extends FormRegisteringMixin(LitElement) { | ||
constructor() { | ||
super(); | ||
this.__parentFormGroup = this.parentNode; | ||
} | ||
}; | ||
const parentClass = defineCE(ParentFormGroupClass); | ||
const formGroup = unsafeStatic(parentClass); | ||
const childClass = defineCE(ChildFormGroupClass); | ||
const childFormGroup = unsafeStatic(childClass); | ||
const parentFormEl = await fixture(html` | ||
<${formGroup}><${childFormGroup} id="child" name="child[]"></${childFormGroup}></${formGroup}> | ||
`); | ||
expect(parentFormEl.resetModelValue).to.equal('foo'); | ||
}); | ||
it('can dynamically add/remove elements', async () => { | ||
const el = await fixture(html` | ||
<form-registrar> | ||
<form-registering></form-registering> | ||
</form-registrar> | ||
`); | ||
const newField = await fixture(html` | ||
<form-registering></form-registering> | ||
`); | ||
expect(el.formElements.length).to.equal(1); | ||
el.appendChild(newField); | ||
expect(el.formElements.length).to.equal(2); | ||
el.removeChild(newField); | ||
expect(el.formElements.length).to.equal(1); | ||
}); | ||
runRegistrationSuite({ | ||
suffix: 'with LitElement, using shadow dom', | ||
baseElement: class ShadowElement extends LitElement { | ||
render() { | ||
return html` | ||
<slot></slot> | ||
`; | ||
} | ||
}, | ||
}); |
@@ -34,3 +34,3 @@ import { | ||
it(`puts a unique id "${tagString}-[hash]" on the native input`, async () => { | ||
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`); | ||
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`); | ||
expect(el.$$slot('input').id).to.equal(el._inputId); | ||
@@ -40,3 +40,3 @@ }); | ||
it('fires focus/blur event on host and native input if focused/blurred', async () => { | ||
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`); | ||
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`); | ||
const cbFocusHost = sinon.spy(); | ||
@@ -73,4 +73,13 @@ el.addEventListener('focus', cbFocusHost); | ||
it('offers simple getter "this.focused" returning true/false for the current focus state', async () => { | ||
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`); | ||
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}</${tagString}>`); | ||
const elDisabled = await fixture(html`<${tag} disabled>${inputSlot}</${tag}>`); | ||
expect(elDisabled.disabled).to.equal(true); | ||
@@ -81,3 +90,3 @@ expect(elDisabled.inputElement.disabled).to.equal(true); | ||
it('can be disabled via property', async () => { | ||
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`); | ||
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`); | ||
el.disabled = true; | ||
@@ -88,16 +97,4 @@ await el.updateComplete; | ||
// 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}</${tagString}>`); | ||
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}</${tagString}>`, | ||
); | ||
const el = await fixture(html`<${tag} value="Some value from attribute">${inputSlot}</${tag}>`); | ||
el.clear(); | ||
@@ -111,4 +108,15 @@ expect(el.value).to.equal(''); | ||
it('can be reset which restores original modelValue', async () => { | ||
const el = await fixture(html` | ||
<${tag} .modelValue="${'foo'}"> | ||
${inputSlot} | ||
</${tag}>`); | ||
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}</${tagString}>`); | ||
const el = await fixture(html`<${tag} value="one">${inputSlot}</${tag}>`); | ||
expect(el.$$slot('input').value).to.equal('one'); | ||
@@ -118,3 +126,3 @@ }); | ||
it('delegates value property', async () => { | ||
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`); | ||
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`); | ||
expect(el.$$slot('input').value).to.equal(''); | ||
@@ -126,18 +134,6 @@ el.value = '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}</${tagString}>`); | ||
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}</${tagString}>`); | ||
const el = await fixture(html`<${tag} value="filled">${inputSlot}</${tag}>`); | ||
expect(el.classList.contains('state-filled')).to.equal(true); | ||
@@ -153,3 +149,3 @@ el.value = ''; | ||
it('preserves the caret position on value change for native text fields (input|textarea)', async () => { | ||
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`); | ||
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`); | ||
await triggerFocusFor(el); | ||
@@ -167,3 +163,3 @@ await el.updateComplete; | ||
it('has a class "state-disabled"', async () => { | ||
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`); | ||
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`); | ||
expect(el.classList.contains('state-disabled')).to.equal(false); | ||
@@ -179,3 +175,3 @@ expect(el.inputElement.hasAttribute('disabled')).to.equal(false); | ||
const disabledel = await fixture(`<${tagString} disabled>${inputSlotString}</${tagString}>`); | ||
const disabledel = await fixture(html`<${tag} disabled>${inputSlot}</${tag}>`); | ||
expect(disabledel.classList.contains('state-disabled')).to.equal(true); | ||
@@ -200,8 +196,8 @@ expect(disabledel.inputElement.hasAttribute('disabled')).to.equal(true); | ||
~~~`, async () => { | ||
const el = await fixture(`<${tagString}> | ||
const el = await fixture(html`<${tag}> | ||
<label slot="label">My Name</label> | ||
${inputSlotString} | ||
${inputSlot} | ||
<span slot="help-text">Enter your Name</span> | ||
<span slot="feedback">No name entered</span> | ||
</${tagString}> | ||
</${tag}> | ||
`); | ||
@@ -217,4 +213,4 @@ const nativeInput = el.$$slot('input'); | ||
(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} | ||
<span slot="before" data-label>[before]</span> | ||
@@ -224,3 +220,3 @@ <span slot="after" data-label>[after]</span> | ||
<span slot="suffix" data-description>[suffix]</span> | ||
</${tagString}> | ||
</${tag}> | ||
`); | ||
@@ -240,29 +236,29 @@ | ||
addToAriaDescription()`, async () => { | ||
const wrapper = await fixture(` | ||
const wrapper = await fixture(html` | ||
<div id="wrapper"> | ||
<${tagString}> | ||
${inputSlotString} | ||
<${tag}> | ||
${inputSlot} | ||
<label slot="label">Added to label by default</label> | ||
<div slot="feedback">Added to description by default</div> | ||
</${tagString}> | ||
</${tag}> | ||
<div id="additionalLabel"> This also needs to be read whenever the input has focus</div> | ||
<div id="additionalDescription"> Same for this </div> | ||
</div>`); | ||
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'), | ||
); | ||
@@ -272,10 +268,10 @@ | ||
// 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'), | ||
); | ||
@@ -304,3 +300,3 @@ }); | ||
} | ||
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`); | ||
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`); | ||
const feedbackEl = el._feedbackElement; | ||
@@ -375,6 +371,6 @@ | ||
it('renders correctly all slot elements in light DOM', async () => { | ||
const el = await fixture(` | ||
<${tagString}> | ||
const el = await fixture(html` | ||
<${tag}> | ||
<label slot="label">[label]</label> | ||
${inputSlotString} | ||
${inputSlot} | ||
<span slot="help-text">[help-text]</span> | ||
@@ -386,3 +382,3 @@ <span slot="before">[before]</span> | ||
<span slot="feedback">[feedback]</span> | ||
</${tagString}> | ||
</${tag}> | ||
`); | ||
@@ -410,5 +406,5 @@ | ||
describe(`Delegation${nameSuffix}`, () => { | ||
describe('Delegation', () => { | ||
it('delegates property value', async () => { | ||
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`); | ||
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`); | ||
expect(el.inputElement.value).to.equal(''); | ||
@@ -415,0 +411,0 @@ el.value = 'one'; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
34
199314
2793