fix(fieldset): outside click only effects focused groups

This commit is contained in:
Thomas Allmer 2019-11-01 12:27:48 +01:00
parent c984a66d4b
commit 378e940ece
3 changed files with 132 additions and 61 deletions

View file

@ -101,38 +101,25 @@ export class LionFieldset extends FormRegistrarMixin(
this.__createTypeAbsenceValidators();
this._checkForOutsideClick = this._checkForOutsideClick.bind(this);
this.addEventListener('focusin', this._syncFocused);
this.addEventListener('focusout', this._onFocusOut);
this.addEventListener('validation-done', this.__validate);
this.addEventListener('dirty-changed', this._syncDirty);
}
connectedCallback() {
// eslint-disable-next-line wc/guard-super-call
super.connectedCallback();
this.addEventListener('focusin', this._updateTouchedClass);
this.addEventListener('focusout', this._onFocusOut);
this.addEventListener('focusin', this._syncFocused);
this.addEventListener('validation-done', this.__validate);
this.addEventListener('dirty-changed', this._syncDirty);
super.connectedCallback(); // eslint-disable-line wc/guard-super-call
this._setRole();
document.addEventListener('click', this._checkForOutsideClick);
}
_checkForOutsideClick(event) {
const outsideGroupClicked = !this.contains(event.target);
if (outsideGroupClicked) {
this.touched = true;
}
}
disconnectedCallback() {
// eslint-disable-next-line wc/guard-super-call
super.disconnectedCallback();
this.removeEventListener('validation-done', this.__validate);
this.removeEventListener('touched-changed', this._updateTouched);
this.removeEventListener('dirty-changed', this._syncDirty);
super.disconnectedCallback(); // eslint-disable-line wc/guard-super-call
document.removeEventListener('click', this._checkForOutsideClick);
if (this.__hasActiveOutsideClickHandling) {
document.removeEventListener('click', this._checkForOutsideClick);
this.__hasActiveOutsideClickHandling = false;
}
}
updated(changedProps) {
@ -162,6 +149,23 @@ export class LionFieldset extends FormRegistrarMixin(
if (changedProps.has('focused')) {
/** @deprecated use touched attribute instead */
this.classList[this.focused ? 'add' : 'remove']('state-focused');
if (this.focused === true) {
this.__setupOutsideClickHandling();
}
}
}
__setupOutsideClickHandling() {
if (!this.__hasActiveOutsideClickHandling) {
document.addEventListener('click', this._checkForOutsideClick);
this.__hasActiveOutsideClickHandling = true;
}
}
_checkForOutsideClick(event) {
const outsideGroupClicked = !this.contains(event.target);
if (outsideGroupClicked) {
this.touched = true;
}
}

View file

@ -84,52 +84,20 @@ storiesOf('Forms|Fieldset', module)
</lion-fieldset>
`,
)
.add('Validation 2 inputs', () => {
const input1IsTen = value => ({
input1IsTen: value.input1 === 'cats' && value.input2 === 'dogs',
});
localize.locale = 'en-GB';
try {
localize.addData('en-GB', 'lion-validate+input1IsTen', {
error: {
input1IsTen: 'Input 1 needs to be "cats" and Input 2 needs to be "dogs"',
},
});
} catch (error) {
// expected as it's a demo
}
return html`
<lion-fieldset .errorValidators=${[[input1IsTen]]}>
<lion-input
label="An all time YouTube favorite"
name="input1"
help-text="longer then 2 characters"
.errorValidators=${[minLengthValidator(3)]}
></lion-input>
<lion-input
label="Another all time YouTube favorite"
name="input2"
help-text="longer then 2 characters"
.errorValidators=${[minLengthValidator(3)]}
></lion-input>
</lion-fieldset>
`;
})
.add('Validation', () => {
function isFakeValidator() {
function isDemoValidator() {
return false;
}
const fakeValidator = (...factoryParams) => [
(...params) => ({ validator: isFakeValidator(...params) }),
const demoValidator = (...factoryParams) => [
(...params) => ({ validator: isDemoValidator(...params) }),
...factoryParams,
];
try {
localize.addData('en-GB', 'lion-validate+validator', {
error: {
validator: 'Fake error message',
validator: 'Demo error message',
},
});
} catch (error) {
@ -137,7 +105,7 @@ storiesOf('Forms|Fieldset', module)
}
return html`
<lion-fieldset id="someId" .errorValidators=${[fakeValidator()]}>
<lion-fieldset id="someId" .errorValidators=${[demoValidator()]}>
<lion-input name="input1" label="Label"></lion-input>
<button
@click=${() => {
@ -154,4 +122,90 @@ storiesOf('Forms|Fieldset', module)
Tab-able
</button>
`;
})
.add('Validation 2 inputs', () => {
const isCatsAndDogs = value => ({
isCatsAndDogs: value.input1 === 'cats' && value.input2 === 'dogs',
});
localize.locale = 'en-GB';
try {
localize.addData('en-GB', 'lion-validate+isCatsAndDogs', {
error: {
isCatsAndDogs:
'[Fieldset Error] Input 1 needs to be "cats" and Input 2 needs to be "dogs"',
},
});
} catch (error) {
// expected as it's a demo
}
return html`
<lion-fieldset .errorValidators=${[[isCatsAndDogs]]}>
<lion-input
label="An all time YouTube favorite"
name="input1"
help-text="longer then 2 characters"
.errorValidators=${[minLengthValidator(3)]}
></lion-input>
<lion-input
label="Another all time YouTube favorite"
name="input2"
help-text="longer then 2 characters"
.errorValidators=${[minLengthValidator(3)]}
></lion-input>
</lion-fieldset>
`;
})
.add('Validation 2 fieldsets', () => {
const isCats = value => ({
isCats: value.input1 === 'cats',
});
localize.locale = 'en-GB';
try {
localize.addData('en-GB', 'lion-validate+isCats', {
error: {
isCats: '[Fieldset Nr. 1 Error] Input 1 needs to be "cats"',
},
});
} catch (error) {
// expected as it's a demo
}
const isDogs = value => ({
isDogs: value.input1 === 'dogs',
});
localize.locale = 'en-GB';
try {
localize.addData('en-GB', 'lion-validate+isDogs', {
error: {
isDogs: '[Fieldset Nr. 2 Error] Input 1 needs to be "dogs"',
},
});
} catch (error) {
// expected as it's a demo
}
return html`
<lion-fieldset .errorValidators=${[[isCats]]}>
<label slot="label">Fieldset Nr. 1</label>
<lion-input
label="An all time YouTube favorite"
name="input1"
help-text="longer then 2 characters"
.errorValidators=${[minLengthValidator(3)]}
></lion-input>
</lion-fieldset>
<br />
<hr />
<br />
<lion-fieldset .errorValidators=${[[isDogs]]}>
<label slot="label">Fieldset Nr. 2</label>
<lion-input
label="An all time YouTube favorite"
name="input1"
help-text="longer then 2 characters"
.errorValidators=${[minLengthValidator(3)]}
></lion-input>
</lion-fieldset>
`;
});

View file

@ -421,17 +421,30 @@ describe('<lion-fieldset>', () => {
<${childTag} name="input2"></${childTag}>
</${tag}>
`);
const el2 = await fixture(html`
<${tag}>
<${childTag} name="input1"></${childTag}>
<${childTag} name="input2"></${childTag}>
</${tag}>
`);
await nextFrame();
const outside = await fixture(html`
<button>outside</button>
`);
outside.click();
expect(el.touched, 'unfocused fieldset should stays untouched').to.be.false;
el.children[1].focus();
el.children[2].focus();
expect(el.touched).to.be.false;
outside.click(); // blur the group via a click
outside.focus(); // a real mouse click moves focus as well
expect(el.touched).to.be.true;
expect(el2.touched).to.be.false;
});
it('potentially shows fieldset error message on interaction change', async () => {