Merge pull request #241 from ing-bank/fix/formRegIssues
Fix/form reg issues
This commit is contained in:
commit
dbff4bfc55
8 changed files with 390 additions and 332 deletions
|
|
@ -43,6 +43,7 @@
|
|||
"test:update-snapshots": "karma start --update-snapshots",
|
||||
"test:prune-snapshots": "karma start --prune-snapshots",
|
||||
"test:bs": "karma start karma.bs.config.js --coverage",
|
||||
"test:bs:legacy": "karma start --legacy karma.bs.config.js --coverage",
|
||||
"lint": "run-p lint:*",
|
||||
"lint:eclint": "git ls-files | xargs eclint check",
|
||||
"lint:eslint": "eslint --ext .js,.html .",
|
||||
|
|
|
|||
|
|
@ -29,19 +29,14 @@ export const FormRegisteringMixin = dedupeMixin(
|
|||
|
||||
__setupRegistrationHook() {
|
||||
if (formRegistrarManager.ready) {
|
||||
this._registerFormElement();
|
||||
this._dispatchRegistration();
|
||||
} else {
|
||||
formRegistrarManager.addEventListener('all-forms-open-for-registration', () => {
|
||||
this._registerFormElement();
|
||||
this._dispatchRegistration();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_registerFormElement() {
|
||||
this._dispatchRegistration();
|
||||
this._requestParentFormGroupUpdateOfResetModelValue();
|
||||
}
|
||||
|
||||
_dispatchRegistration() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('form-element-register', {
|
||||
|
|
@ -56,16 +51,5 @@ export const FormRegisteringMixin = dedupeMixin(
|
|||
this.__parentFormGroup.removeFormElement(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -117,14 +122,6 @@ export class LionField extends FormControlMixin(
|
|||
|
||||
disconnectedCallback() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -164,6 +161,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
|
||||
|
|
|
|||
121
packages/field/test-suites/FormRegistrationMixins.suite.js
Normal file
121
packages/field/test-suites/FormRegistrationMixins.suite.js
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import { expect, fixture, html, defineCE, unsafeStatic } from '@open-wc/testing';
|
||||
import { LitElement } from '@lion/core';
|
||||
|
||||
import { FormRegistrarMixin } from '../src/FormRegistrarMixin.js';
|
||||
import { FormRegisteringMixin } from '../src/FormRegisteringMixin.js';
|
||||
import { formRegistrarManager } from '../src/formRegistrarManager.js';
|
||||
|
||||
export const runRegistrationSuite = customConfig => {
|
||||
const cfg = {
|
||||
baseElement: HTMLElement,
|
||||
suffix: null,
|
||||
...customConfig,
|
||||
};
|
||||
|
||||
describe(`FormRegistrationMixins${cfg.suffix ? ` (${cfg.suffix})` : ''}`, () => {
|
||||
let parentTag;
|
||||
let childTag;
|
||||
|
||||
before(async () => {
|
||||
if (!cfg.parentTagString) {
|
||||
cfg.parentTagString = defineCE(class extends FormRegistrarMixin(cfg.baseElement) {});
|
||||
}
|
||||
if (!cfg.childTagString) {
|
||||
cfg.childTagString = defineCE(class extends FormRegisteringMixin(cfg.baseElement) {});
|
||||
}
|
||||
|
||||
parentTag = unsafeStatic(cfg.parentTagString);
|
||||
childTag = unsafeStatic(cfg.childTagString);
|
||||
});
|
||||
|
||||
it('can register a formElement', async () => {
|
||||
const el = await fixture(html`
|
||||
<${parentTag}>
|
||||
<${childTag}></${childTag}>
|
||||
</${parentTag}>
|
||||
`);
|
||||
await el.registrationReady;
|
||||
expect(el.formElements.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('supports nested registration parents', async () => {
|
||||
const el = await fixture(html`
|
||||
<${parentTag}>
|
||||
<${parentTag}>
|
||||
<${childTag}></${childTag}>
|
||||
<${childTag}></${childTag}>
|
||||
</${parentTag}>
|
||||
</${parentTag}>
|
||||
`);
|
||||
await el.registrationReady;
|
||||
expect(el.formElements.length).to.equal(1);
|
||||
expect(el.querySelector(cfg.parentTagString).formElements.length).to.equal(2);
|
||||
});
|
||||
|
||||
it('forgets disconnected registrars', async () => {
|
||||
const el = await fixture(html`
|
||||
<${parentTag}>
|
||||
<${parentTag}>
|
||||
<${childTag}</${childTag}
|
||||
</${parentTag}>
|
||||
</${parentTag}>
|
||||
`);
|
||||
|
||||
const secondRegistrar = await fixture(html`
|
||||
<${parentTag}>
|
||||
<${childTag}</${childTag}
|
||||
</${parentTag}>
|
||||
`);
|
||||
|
||||
el.appendChild(secondRegistrar);
|
||||
expect(formRegistrarManager.__elements.length).to.equal(3);
|
||||
|
||||
el.removeChild(secondRegistrar);
|
||||
expect(formRegistrarManager.__elements.length).to.equal(2);
|
||||
});
|
||||
|
||||
it('works for components 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 el = await fixture(html`
|
||||
<${tagWrapper}>
|
||||
<${childTag}></${childTag}>
|
||||
</${tagWrapper}>
|
||||
`);
|
||||
await el.registrationReady;
|
||||
expect(el.formElements.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('can dynamically add/remove elements', async () => {
|
||||
const el = await fixture(html`
|
||||
<${parentTag}>
|
||||
<${childTag}></${childTag}>
|
||||
</${parentTag}>
|
||||
`);
|
||||
const newField = await fixture(html`
|
||||
<${childTag}></${childTag}>
|
||||
`);
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
@ -1,129 +1,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';
|
||||
|
||||
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();
|
||||
}
|
||||
runRegistrationSuite({
|
||||
suffix: 'with UpdatingElement',
|
||||
baseElement: UpdatingElement,
|
||||
});
|
||||
|
||||
runRegistrationSuite({
|
||||
suffix: 'with LitElement, using shadow dom',
|
||||
baseElement: class ShadowElement extends LitElement {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@ beforeEach(() => {
|
|||
|
||||
describe('<lion-field>', () => {
|
||||
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);
|
||||
});
|
||||
|
||||
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();
|
||||
el.addEventListener('focus', cbFocusHost);
|
||||
const cbFocusNativeInput = sinon.spy();
|
||||
|
|
@ -68,33 +68,30 @@ describe('<lion-field>', () => {
|
|||
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}</${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);
|
||||
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;
|
||||
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}</${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();
|
||||
expect(el.value).to.equal('');
|
||||
el.value = 'Some value from property';
|
||||
|
|
@ -103,35 +100,34 @@ describe('<lion-field>', () => {
|
|||
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');
|
||||
});
|
||||
|
||||
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('');
|
||||
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}</${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);
|
||||
el.value = '';
|
||||
await el.updateComplete;
|
||||
|
|
@ -142,7 +138,7 @@ describe('<lion-field>', () => {
|
|||
});
|
||||
|
||||
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);
|
||||
await el.updateComplete;
|
||||
el.inputElement.value = 'hello world';
|
||||
|
|
@ -155,7 +151,7 @@ describe('<lion-field>', () => {
|
|||
|
||||
// TODO: add pointerEvents test for disabled
|
||||
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);
|
||||
expect(el.inputElement.hasAttribute('disabled')).to.equal(false);
|
||||
|
||||
|
|
@ -166,7 +162,7 @@ describe('<lion-field>', () => {
|
|||
expect(el.classList.contains('state-disabled')).to.equal(true);
|
||||
expect(el.inputElement.hasAttribute('disabled')).to.equal(true);
|
||||
|
||||
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);
|
||||
expect(disabledel.inputElement.hasAttribute('disabled')).to.equal(true);
|
||||
});
|
||||
|
|
@ -186,12 +182,12 @@ describe('<lion-field>', () => {
|
|||
<div slot="feedback" id="feedback-[id]">[feedback] </span>
|
||||
</lion-field>
|
||||
~~~`, 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}>
|
||||
`);
|
||||
const nativeInput = el.$$slot('input');
|
||||
|
||||
|
|
@ -202,13 +198,13 @@ describe('<lion-field>', () => {
|
|||
|
||||
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}
|
||||
<span slot="before" data-label>[before]</span>
|
||||
<span slot="after" data-label>[after]</span>
|
||||
<span slot="prefix" data-description>[prefix]</span>
|
||||
<span slot="suffix" data-description>[suffix]</span>
|
||||
</${tagString}>
|
||||
</${tag}>
|
||||
`);
|
||||
|
||||
const nativeInput = el.$$slot('input');
|
||||
|
|
@ -223,45 +219,45 @@ describe('<lion-field>', () => {
|
|||
// 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`
|
||||
<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'),
|
||||
);
|
||||
|
||||
// 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 +281,7 @@ describe('<lion-field>', () => {
|
|||
function hasX(str) {
|
||||
return { hasX: str.indexOf('x') > -1 };
|
||||
}
|
||||
const el = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`);
|
||||
const feedbackEl = el._feedbackElement;
|
||||
|
||||
el.modelValue = 'a@b.nl';
|
||||
|
|
@ -355,17 +351,17 @@ describe('<lion-field>', () => {
|
|||
|
||||
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}>
|
||||
<label slot="label">[label]</label>
|
||||
${inputSlotString}
|
||||
${inputSlot}
|
||||
<span slot="help-text">[help-text]</span>
|
||||
<span slot="before">[before]</span>
|
||||
<span slot="after">[after]</span>
|
||||
<span slot="prefix">[prefix]</span>
|
||||
<span slot="suffix">[suffix]</span>
|
||||
<span slot="feedback">[feedback]</span>
|
||||
</${tagString}>
|
||||
</${tag}>
|
||||
`);
|
||||
|
||||
const names = [
|
||||
|
|
@ -388,9 +384,9 @@ describe('<lion-field>', () => {
|
|||
});
|
||||
});
|
||||
|
||||
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('');
|
||||
el.value = 'one';
|
||||
expect(el.value).to.equal('one');
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ const pascalCase = str => str.charAt(0).toUpperCase() + str.slice(1);
|
|||
/**
|
||||
* LionFieldset: fieldset wrapper providing extra features and integration with lion-field elements.
|
||||
*
|
||||
* @customElement
|
||||
* @customElement lion-fieldset
|
||||
* @extends LionLitElement
|
||||
*/
|
||||
export class LionFieldset extends FormRegistrarMixin(
|
||||
|
|
@ -176,7 +176,14 @@ export class LionFieldset extends FormRegistrarMixin(
|
|||
}
|
||||
|
||||
resetGroup() {
|
||||
this.modelValue = this.resetModelValue;
|
||||
this.formElementsArray.forEach(child => {
|
||||
if (typeof child.resetGroup === 'function') {
|
||||
child.resetGroup();
|
||||
} else if (typeof child.reset === 'function') {
|
||||
child.reset();
|
||||
}
|
||||
});
|
||||
|
||||
this.resetInteractionState();
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +252,7 @@ export class LionFieldset extends FormRegistrarMixin(
|
|||
}
|
||||
|
||||
/**
|
||||
* Get's triggered by event 'validatin-done' which enabled us to handle 2 different situations
|
||||
* Gets triggered by event 'validation-done' which enabled us to handle 2 different situations
|
||||
* - react on modelValue change, which says something about the validity as a whole
|
||||
* (at least two checkboxes for instance) and nothing about the children's values
|
||||
* - children validatity states have changed, so fieldset needs to update itself based on that
|
||||
|
|
@ -348,23 +355,16 @@ export class LionFieldset extends FormRegistrarMixin(
|
|||
}
|
||||
|
||||
/**
|
||||
* Updates the resetModelValue of this fieldset and asks it's parent fieldset/group to also
|
||||
* update.
|
||||
* This is needed as the upgrade order is not guaranteed. We have 3 main cases:
|
||||
* 1. if `street-name` gets updated last then `address` and `details` needs to update their
|
||||
* resetModelValue to also incorporate the correct value of `street-name`/`address`.
|
||||
* 2. If `address` get updated last then it already has the correct `street-name` so it
|
||||
* requests an update only for `details`.
|
||||
* 3. If `details` get updated last nothing happens here as all data are up to date
|
||||
*
|
||||
* @example
|
||||
* <lion-fieldset name="details">
|
||||
* <lion-fieldset name="address">
|
||||
* <lion-input name="street-name" .modelValue=${'street 1'}>
|
||||
* Gathers initial model values of all children. Used
|
||||
* when resetGroup() is called.
|
||||
*/
|
||||
_updateResetModelValue() {
|
||||
this.resetModelValue = this.modelValue;
|
||||
this._requestParentFormGroupUpdateOfResetModelValue();
|
||||
get _initialModelValue() {
|
||||
return this._getFromAllFormElements('_initialModelValue');
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
get resetModelValue() {
|
||||
return this._initialModelValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -14,12 +14,14 @@ import '../lion-fieldset.js';
|
|||
|
||||
const tagString = 'lion-fieldset';
|
||||
const tag = unsafeStatic(tagString);
|
||||
const inputSlotString = `
|
||||
<lion-input name="gender[]"></lion-input>
|
||||
<lion-input name="gender[]"></lion-input>
|
||||
<lion-input name="color"></lion-input>
|
||||
<lion-input name="hobbies[]"></lion-input>
|
||||
<lion-input name="hobbies[]"></lion-input>
|
||||
const childTagString = 'lion-input';
|
||||
const childTag = unsafeStatic(childTagString);
|
||||
const inputSlots = html`
|
||||
<${childTag} name="gender[]"></${childTag}>
|
||||
<${childTag} name="gender[]"></${childTag}>
|
||||
<${childTag} name="color"></${childTag}>
|
||||
<${childTag} name="hobbies[]"></${childTag}>
|
||||
<${childTag} name="hobbies[]"></${childTag}>
|
||||
`;
|
||||
const nonPrefilledModelValue = '';
|
||||
const prefilledModelValue = 'prefill';
|
||||
|
|
@ -30,7 +32,7 @@ beforeEach(() => {
|
|||
|
||||
describe('<lion-fieldset>', () => {
|
||||
it(`${tagString} has an up to date list of every form element in #formElements`, async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
expect(Object.keys(fieldset.formElements).length).to.equal(3);
|
||||
expect(fieldset.formElements['hobbies[]'].length).to.equal(2);
|
||||
|
|
@ -40,12 +42,12 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it(`supports in html wrapped form elements`, async () => {
|
||||
const el = await fixture(`
|
||||
<lion-fieldset>
|
||||
const el = await fixture(html`
|
||||
<${tag}>
|
||||
<div>
|
||||
<lion-input name="foo"></lion-input>
|
||||
<${childTag} name="foo"></${childTag}>
|
||||
</div>
|
||||
</lion-fieldset>
|
||||
</${tag}>
|
||||
`);
|
||||
await nextFrame();
|
||||
expect(el.formElementsArray.length).to.equal(1);
|
||||
|
|
@ -54,7 +56,7 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('handles names with ending [] as an array', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
fieldset.formElements['gender[]'][0].modelValue = { value: 'male' };
|
||||
fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' };
|
||||
|
|
@ -75,7 +77,7 @@ describe('<lion-fieldset>', () => {
|
|||
console.info = () => {};
|
||||
|
||||
let error = false;
|
||||
const el = await fixture(`<lion-fieldset></lion-fieldset>`);
|
||||
const el = await fixture(html`<${tag}></${tag}>`);
|
||||
try {
|
||||
// we test the api directly as errors thrown from a web component are in a
|
||||
// different context and we can not catch them here => register fake elements
|
||||
|
|
@ -94,7 +96,7 @@ describe('<lion-fieldset>', () => {
|
|||
console.info = () => {};
|
||||
|
||||
let error = false;
|
||||
const el = await fixture(`<lion-fieldset name="foo"></lion-fieldset>`);
|
||||
const el = await fixture(html`<${tag} name="foo"></${tag}>`);
|
||||
try {
|
||||
// we test the api directly as errors thrown from a web component are in a
|
||||
// different context and we can not catch them here => register fake elements
|
||||
|
|
@ -113,7 +115,7 @@ describe('<lion-fieldset>', () => {
|
|||
console.info = () => {};
|
||||
|
||||
let error = false;
|
||||
const el = await fixture(`<lion-fieldset></lion-fieldset>`);
|
||||
const el = await fixture(html`<${tag}></${tag}>`);
|
||||
try {
|
||||
// we test the api directly as errors thrown from a web component are in a
|
||||
// different context and we can not catch them here => register fake elements
|
||||
|
|
@ -132,8 +134,8 @@ describe('<lion-fieldset>', () => {
|
|||
/* eslint-enable no-console */
|
||||
|
||||
it('can dynamically add/remove elements', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const newField = await fixture(`<lion-input name="lastName"></lion-input>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
const newField = await fixture(html`<${childTag} name="lastName"></${childTag}>`);
|
||||
|
||||
expect(Object.keys(fieldset.formElements).length).to.equal(3);
|
||||
|
||||
|
|
@ -145,11 +147,11 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('can read/write all values (of every input) via this.modelValue', async () => {
|
||||
const fieldset = await fixture(`
|
||||
<lion-fieldset>
|
||||
<lion-input name="lastName"></lion-input>
|
||||
<${tagString} name="newfieldset">${inputSlotString}</${tagString}>
|
||||
</lion-fieldset>
|
||||
const fieldset = await fixture(html`
|
||||
<${tag}>
|
||||
<${childTag} name="lastName"></${childTag}>
|
||||
<${tag} name="newfieldset">${inputSlots}</${tag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await fieldset.registrationReady;
|
||||
const newFieldset = fieldset.querySelector('lion-fieldset');
|
||||
|
|
@ -190,10 +192,10 @@ describe('<lion-fieldset>', () => {
|
|||
|
||||
it('does not throw if setter data of this.modelValue can not be handled', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-fieldset>
|
||||
<lion-input name="firstName" .modelValue=${'foo'}></lion-input>
|
||||
<lion-input name="lastName" .modelValue=${'bar'}></lion-input>
|
||||
</lion-fieldset>
|
||||
<${tag}>
|
||||
<${childTag} name="firstName" .modelValue=${'foo'}></${childTag}>
|
||||
<${childTag} name="lastName" .modelValue=${'bar'}></${childTag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await nextFrame();
|
||||
const initState = {
|
||||
|
|
@ -210,7 +212,7 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('disables/enables all its formElements if it becomes disabled/enabled', async () => {
|
||||
const el = await fixture(`<${tagString} disabled>${inputSlotString}</${tagString}>`);
|
||||
const el = await fixture(html`<${tag} disabled>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
expect(el.formElements.color.disabled).to.equal(true);
|
||||
expect(el.formElements['hobbies[]'][0].disabled).to.equal(true);
|
||||
|
|
@ -225,7 +227,7 @@ describe('<lion-fieldset>', () => {
|
|||
|
||||
it('does not propagate/override initial disabled value on nested form elements', async () => {
|
||||
const el = await fixture(
|
||||
`<${tagString}><${tagString} name="sub" disabled>${inputSlotString}</${tagString}></${tagString}>`,
|
||||
html`<${tag}><${tag} name="sub" disabled>${inputSlots}</${tag}></${tag}>`,
|
||||
);
|
||||
await el.updateComplete;
|
||||
expect(el.disabled).to.equal(false);
|
||||
|
|
@ -237,7 +239,7 @@ describe('<lion-fieldset>', () => {
|
|||
|
||||
// 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}>`);
|
||||
const el = await fixture(html`<${tag} disabled>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
expect(el.classList.contains('state-disabled')).to.equal(true);
|
||||
el.disabled = false;
|
||||
|
|
@ -252,10 +254,10 @@ describe('<lion-fieldset>', () => {
|
|||
}
|
||||
const el = await fixture(html`
|
||||
<${tag}>
|
||||
<lion-input name="color"
|
||||
<${childTag} name="color"
|
||||
.errorValidators=${[[isCat]]}
|
||||
.modelValue=${'blue'}
|
||||
></lion-input>
|
||||
></${childTag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await nextFrame();
|
||||
|
|
@ -263,7 +265,7 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('validates when a value changes', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
const spy = sinon.spy(fieldset, 'validate');
|
||||
fieldset.formElements.color.modelValue = { checked: true, value: 'red' };
|
||||
|
|
@ -277,10 +279,10 @@ describe('<lion-fieldset>', () => {
|
|||
|
||||
const el = await fixture(html`
|
||||
<${tag}>
|
||||
<lion-input name="color"
|
||||
<${childTag} name="color"
|
||||
.errorValidators=${[[isCat]]}
|
||||
.modelValue=${'blue'}
|
||||
></lion-input>
|
||||
></${childTag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await nextFrame();
|
||||
|
|
@ -297,12 +299,12 @@ describe('<lion-fieldset>', () => {
|
|||
}
|
||||
const el = await fixture(html`
|
||||
<${tag} .errorValidators=${[[hasEvenNumberOfChildren]]}>
|
||||
<lion-input id="c1" name="c1"></lion-input>
|
||||
<${childTag} id="c1" name="c1"></${childTag}>
|
||||
</${tag}>
|
||||
`);
|
||||
const child2 = await fixture(
|
||||
html`
|
||||
<lion-input name="c2"></lion-input>
|
||||
<${childTag} name="c2"></${childTag}>
|
||||
`,
|
||||
);
|
||||
|
||||
|
|
@ -326,7 +328,7 @@ describe('<lion-fieldset>', () => {
|
|||
|
||||
describe('interaction states', () => {
|
||||
it('has false states (dirty, touched, prefilled) on init', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
expect(fieldset.dirty).to.equal(false, 'dirty');
|
||||
expect(fieldset.touched).to.equal(false, 'touched');
|
||||
|
|
@ -334,14 +336,14 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('sets dirty when value changed', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
fieldset.formElements['hobbies[]'][0].modelValue = { checked: true, value: 'football' };
|
||||
expect(fieldset.dirty).to.equal(true);
|
||||
});
|
||||
|
||||
it('sets touched when field left after focus', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
await triggerFocusFor(fieldset.formElements['gender[]'][0].inputElement);
|
||||
await triggerBlurFor(fieldset.formElements['gender[]'][0].inputElement);
|
||||
|
|
@ -349,7 +351,7 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('sets a class "state-(touched|dirty)"', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
fieldset.formElements.color.touched = true;
|
||||
await fieldset.updateComplete;
|
||||
|
|
@ -364,7 +366,7 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('sets prefilled when field left and value non-empty', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' };
|
||||
fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'football' };
|
||||
|
|
@ -385,17 +387,17 @@ describe('<lion-fieldset>', () => {
|
|||
|
||||
it('sets prefilled once instantiated', async () => {
|
||||
// no prefilled when nothing has value
|
||||
const fieldsetNotPrefilled = await fixture(html`<${tag}>${inputSlotString}</${tag}>`);
|
||||
const fieldsetNotPrefilled = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
expect(fieldsetNotPrefilled.prefilled).to.equal(false, 'not prefilled on init');
|
||||
|
||||
// prefilled when at least one child has value
|
||||
const fieldsetPrefilled = await fixture(html`
|
||||
<${tag}>
|
||||
<lion-input name="gender[]" .modelValue=${prefilledModelValue}></lion-input>
|
||||
<lion-input name="gender[]"></lion-input>
|
||||
<lion-input name="color"></lion-input>
|
||||
<lion-input name="hobbies[]"></lion-input>
|
||||
<lion-input name="hobbies[]"></lion-input>
|
||||
<${childTag} name="gender[]" .modelValue=${prefilledModelValue}></${childTag}>
|
||||
<${childTag} name="gender[]"></${childTag}>
|
||||
<${childTag} name="color"></${childTag}>
|
||||
<${childTag} name="hobbies[]"></${childTag}>
|
||||
<${childTag} name="hobbies[]"></${childTag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await nextFrame();
|
||||
|
|
@ -405,7 +407,7 @@ describe('<lion-fieldset>', () => {
|
|||
|
||||
describe('serialize', () => {
|
||||
it('use form elements serializedValue', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
fieldset.formElements['hobbies[]'][0].serializer = v => `${v.value}-serialized`;
|
||||
fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'Bar' };
|
||||
|
|
@ -422,7 +424,7 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('form elements which are not disabled', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
fieldset.formElements.color.modelValue = { checked: false, value: 'blue' };
|
||||
fieldset.formElements['hobbies[]'][0].modelValue = { checked: true, value: 'football' };
|
||||
|
|
@ -445,11 +447,11 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('allows for nested fieldsets', async () => {
|
||||
const fieldset = await fixture(`
|
||||
<lion-fieldset name="userData">
|
||||
<lion-input name="comment"></lion-input>
|
||||
<${tagString} name="newfieldset">${inputSlotString}</${tagString}>
|
||||
</lion-fieldset>
|
||||
const fieldset = await fixture(html`
|
||||
<${tag} name="userData">
|
||||
<${childTag} name="comment"></${childTag}>
|
||||
<${tag} name="newfieldset">${inputSlots}</${tag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await nextFrame();
|
||||
const newFieldset = fieldset.querySelector('lion-fieldset');
|
||||
|
|
@ -472,11 +474,11 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('will exclude form elements within an disabled fieldset', async () => {
|
||||
const fieldset = await fixture(`
|
||||
<lion-fieldset name="userData">
|
||||
<lion-input name="comment"></lion-input>
|
||||
<${tagString} name="newfieldset">${inputSlotString}</${tagString}>
|
||||
</lion-fieldset>
|
||||
const fieldset = await fixture(html`
|
||||
<${tag} name="userData">
|
||||
<${childTag} name="comment"></${childTag}>
|
||||
<${tag} name="newfieldset">${inputSlots}</${tag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await nextFrame();
|
||||
|
||||
|
|
@ -509,7 +511,7 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('treats names with ending [] as arrays', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' };
|
||||
fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' };
|
||||
|
|
@ -524,11 +526,11 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('does not serialize undefined values (nb radios/checkboxes are always serialized)', async () => {
|
||||
const fieldset = await fixture(`
|
||||
<lion-fieldset>
|
||||
<lion-input name="custom[]"></lion-input>
|
||||
<lion-input name="custom[]"></lion-input>
|
||||
</lion-fieldset>
|
||||
const fieldset = await fixture(html`
|
||||
<${tag}>
|
||||
<${childTag} name="custom[]"></${childTag}>
|
||||
<${childTag} name="custom[]"></${childTag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await nextFrame();
|
||||
fieldset.formElements['custom[]'][0].modelValue = 'custom 1';
|
||||
|
|
@ -543,9 +545,9 @@ describe('<lion-fieldset>', () => {
|
|||
describe('reset', () => {
|
||||
it('restores default values if changes were made', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-fieldset>
|
||||
<lion-input id="firstName" name="firstName" .modelValue="${'Foo'}"></lion-input>
|
||||
</lion-fieldset>
|
||||
<${tag}>
|
||||
<${childTag} id="firstName" name="firstName" .modelValue="${'Foo'}"></${childTag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await el.querySelector('lion-input').updateComplete;
|
||||
|
||||
|
|
@ -562,9 +564,9 @@ describe('<lion-fieldset>', () => {
|
|||
|
||||
it('restores default values of arrays if changes were made', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-fieldset>
|
||||
<lion-input id="firstName" name="firstName[]" .modelValue="${'Foo'}"></lion-input>
|
||||
</lion-fieldset>
|
||||
<${tag}>
|
||||
<${childTag} id="firstName" name="firstName[]" .modelValue="${'Foo'}"></${childTag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await el.querySelector('lion-input').updateComplete;
|
||||
|
||||
|
|
@ -581,11 +583,11 @@ describe('<lion-fieldset>', () => {
|
|||
|
||||
it('restores default values of a nested fieldset if changes were made', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-fieldset>
|
||||
<lion-fieldset id="name" name="name[]">
|
||||
<lion-input id="firstName" name="firstName" .modelValue="${'Foo'}"></lion-input>
|
||||
</lion-fieldset>
|
||||
</lion-fieldset>
|
||||
<${tag}>
|
||||
<${tag} id="name" name="name[]">
|
||||
<${childTag} id="firstName" name="firstName" .modelValue="${'Foo'}"></${childTag}>
|
||||
</${tag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await Promise.all([
|
||||
el.querySelector('lion-fieldset').updateComplete,
|
||||
|
|
@ -607,7 +609,7 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('clears interaction state', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
// Safety check initially
|
||||
fieldset._setValueForAllFormElements('dirty', true);
|
||||
|
|
@ -636,7 +638,7 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('clears submitted state', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
fieldset.submitted = true;
|
||||
fieldset.resetGroup();
|
||||
|
|
@ -656,8 +658,8 @@ describe('<lion-fieldset>', () => {
|
|||
|
||||
const el = await fixture(html`
|
||||
<${tag} .errorValidators=${[[containsA]]}>
|
||||
<lion-input name="color" .errorValidators=${[[isCat]]}></lion-input>
|
||||
<lion-input name="color2"></lion-input>
|
||||
<${childTag} name="color" .errorValidators=${[[isCat]]}></${childTag}>
|
||||
<${childTag} name="color2"></${childTag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await el.registrationReady;
|
||||
|
|
@ -678,6 +680,68 @@ describe('<lion-fieldset>', () => {
|
|||
expect(el.error.containsA).to.be.true;
|
||||
expect(el.formElements.color.errorState).to.be.false;
|
||||
});
|
||||
|
||||
it('has access to `_initialModelValue` based on initial children states', async () => {
|
||||
const el = await fixture(html`
|
||||
<${tag}>
|
||||
<${childTag} name="child[]" .modelValue="${'foo1'}">
|
||||
</${childTag}>
|
||||
<${childTag} name="child[]" .modelValue="${'bar1'}">
|
||||
</${childTag}>
|
||||
</${tag}>
|
||||
`);
|
||||
await el.updateComplete;
|
||||
el.modelValue['child[]'] = ['foo2', 'bar2'];
|
||||
expect(el._initialModelValue['child[]']).to.eql(['foo1', 'bar1']);
|
||||
});
|
||||
|
||||
it('does not wrongly recompute `_initialModelValue` after dynamic changes of children', async () => {
|
||||
const el = await fixture(html`
|
||||
<${tag}>
|
||||
<${childTag} name="child[]" .modelValue="${'foo1'}">
|
||||
</${childTag}>
|
||||
</${tag}>
|
||||
`);
|
||||
el.modelValue['child[]'] = ['foo2'];
|
||||
const childEl = await fixture(html`
|
||||
<${childTag} name="child[]" .modelValue="${'bar1'}">
|
||||
</${childTag}>
|
||||
`);
|
||||
el.appendChild(childEl);
|
||||
expect(el._initialModelValue['child[]']).to.eql(['foo1', 'bar1']);
|
||||
});
|
||||
|
||||
describe('resetGroup method', () => {
|
||||
it('calls resetGroup on children fieldsets', async () => {
|
||||
const el = await fixture(html`
|
||||
<${tag} name="parentFieldset">
|
||||
<${tag} name="childFieldset">
|
||||
<${childTag} name="child[]" .modelValue="${'foo1'}">
|
||||
</${childTag}>
|
||||
</${tag}>
|
||||
</${tag}>
|
||||
`);
|
||||
const childFieldsetEl = el.querySelector(tagString);
|
||||
const resetGroupSpy = sinon.spy(childFieldsetEl, 'resetGroup');
|
||||
el.resetGroup();
|
||||
expect(resetGroupSpy.callCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('calls reset on children fields', async () => {
|
||||
const el = await fixture(html`
|
||||
<${tag} name="parentFieldset">
|
||||
<${tag} name="childFieldset">
|
||||
<${childTag} name="child[]" .modelValue="${'foo1'}">
|
||||
</${childTag}>
|
||||
</${tag}>
|
||||
</${tag}>
|
||||
`);
|
||||
const childFieldsetEl = el.querySelector(childTagString);
|
||||
const resetSpy = sinon.spy(childFieldsetEl, 'reset');
|
||||
el.resetGroup();
|
||||
expect(resetSpy.callCount).to.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('a11y', () => {
|
||||
|
|
@ -686,7 +750,7 @@ describe('<lion-fieldset>', () => {
|
|||
// });
|
||||
|
||||
it('has role="group" set', async () => {
|
||||
const fieldset = await fixture(`<${tagString}>${inputSlotString}</${tagString}>`);
|
||||
const fieldset = await fixture(html`<${tag}>${inputSlots}</${tag}>`);
|
||||
await nextFrame();
|
||||
fieldset.formElements['hobbies[]'][0].modelValue = { checked: false, value: 'chess' };
|
||||
fieldset.formElements['hobbies[]'][1].modelValue = { checked: false, value: 'rugby' };
|
||||
|
|
@ -698,11 +762,11 @@ describe('<lion-fieldset>', () => {
|
|||
});
|
||||
|
||||
it('has an aria-labelledby from element with slot="label"', async () => {
|
||||
const el = await fixture(`
|
||||
<${tagString}>
|
||||
const el = await fixture(html`
|
||||
<${tag}>
|
||||
<label slot="label">My Label</label>
|
||||
${inputSlotString}
|
||||
</${tagString}>
|
||||
${inputSlots}
|
||||
</${tag}>
|
||||
`);
|
||||
const label = el.querySelector('[slot="label"]');
|
||||
expect(el.hasAttribute('aria-labelledby')).to.equal(true);
|
||||
|
|
@ -724,40 +788,40 @@ describe('<lion-fieldset>', () => {
|
|||
childAriaFixture = async (
|
||||
msgSlotType = 'feedback', // eslint-disable-line no-shadow
|
||||
) => {
|
||||
const dom = await fixture(`
|
||||
<lion-fieldset name="l1_g">
|
||||
<lion-input name="l1_fa">
|
||||
const dom = await fixture(html`
|
||||
<${tag} name="l1_g">
|
||||
<${childTag} name="l1_fa">
|
||||
<div slot="${msgSlotType}" id="msg_l1_fa"></div>
|
||||
<!-- field referred by: #msg_l1_fa (local), #msg_l1_g (parent/group) -->
|
||||
</lion-input>
|
||||
</${childTag}>
|
||||
|
||||
<lion-input name="l1_fb">
|
||||
<${childTag} name="l1_fb">
|
||||
<div slot="${msgSlotType}" id="msg_l1_fb"></div>
|
||||
<!-- field referred by: #msg_l1_fb (local), #msg_l1_g (parent/group) -->
|
||||
</lion-input>
|
||||
</${childTag}>
|
||||
|
||||
<!-- [ INNER FIELDSET ] -->
|
||||
|
||||
<lion-fieldset name="l2_g">
|
||||
<lion-input name="l2_fa">
|
||||
<${tag} name="l2_g">
|
||||
<${childTag} name="l2_fa">
|
||||
<div slot="${msgSlotType}" id="msg_l2_fa"></div>
|
||||
<!-- field referred by: #msg_l2_fa (local), #msg_l2_g (parent/group), #msg_l1_g (grandparent/group.group) -->
|
||||
</lion-input>
|
||||
</${childTag}>
|
||||
|
||||
<lion-input name="l2_fb">
|
||||
<${childTag} name="l2_fb">
|
||||
<div slot="${msgSlotType}" id="msg_l2_fb"></div>
|
||||
<!-- field referred by: #msg_l2_fb (local), #msg_l2_g (parent/group), #msg_l1_g (grandparent/group.group) -->
|
||||
</lion-input>
|
||||
</${childTag}>
|
||||
|
||||
<div slot="${msgSlotType}" id="msg_l2_g"></div>
|
||||
<!-- group referred by: #msg_l2_g (local), #msg_l1_g (parent/group) -->
|
||||
</lion-fieldset>
|
||||
</${tag}>
|
||||
|
||||
<!-- [ / INNER FIELDSET ] -->
|
||||
|
||||
<div slot="${msgSlotType}" id="msg_l1_g"></div>
|
||||
<!-- group referred by: #msg_l1_g (local) -->
|
||||
</lion-fieldset>
|
||||
</${tag}>
|
||||
`);
|
||||
return dom;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue