Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@lion/field

Package Overview
Dependencies
Maintainers
1
Versions
103
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lion/field - npm Package Compare versions

Comparing version 0.1.26 to 0.1.27

16

CHANGELOG.md

@@ -6,2 +6,18 @@ # Change Log

## [0.1.27](https://github.com/ing-bank/lion/compare/@lion/field@0.1.26...@lion/field@0.1.27) (2019-07-15)
### Bug Fixes
* **field:** make InteractionState leaveEvent protected ([3981985](https://github.com/ing-bank/lion/commit/3981985))
* **field:** move FocusMixin out of InteractionStateMixin ([b398f40](https://github.com/ing-bank/lion/commit/b398f40))
* **field:** no DomHelpers for FormControl and apply all needed mixins ([bb1c702](https://github.com/ing-bank/lion/commit/bb1c702))
* **field:** remove CssClassMixin from InteractionStateMixin ([dcf8726](https://github.com/ing-bank/lion/commit/dcf8726))
* **field:** remove unused EventMixin from FormatMixin ([901fc8c](https://github.com/ing-bank/lion/commit/901fc8c))
* **field:** the FormatMixin should work without an inputElement ([2511bf6](https://github.com/ing-bank/lion/commit/2511bf6))
## [0.1.26](https://github.com/ing-bank/lion/compare/@lion/field@0.1.25...@lion/field@0.1.26) (2019-07-09)

@@ -8,0 +24,0 @@

6

package.json
{
"name": "@lion/field",
"version": "0.1.26",
"version": "0.1.27",
"description": "Fields are the most fundamental building block of the Form System",

@@ -36,3 +36,3 @@ "author": "ing-bank",

"@lion/core": "^0.1.9",
"@lion/validate": "^0.2.12"
"@lion/validate": "^0.2.13"
},

@@ -45,3 +45,3 @@ "devDependencies": {

},
"gitHead": "a598a47dabccc92e68f12411b7b613a8e5132762"
"gitHead": "688fcb6690d3cb29b0812056f5b27a1f9d0544b6"
}
/* eslint-disable class-methods-use-this */
import { dedupeMixin } from '@lion/core';
import { EventMixin } from '@lion/core/src/EventMixin.js';
import { ObserverMixin } from '@lion/core/src/ObserverMixin.js';

@@ -24,3 +23,3 @@ import { Unparseable } from '@lion/validate';

// eslint-disable-next-line no-unused-vars, no-shadow
class FormatMixin extends EventMixin(ObserverMixin(superclass)) {
class FormatMixin extends ObserverMixin(superclass) {
static get properties() {

@@ -235,3 +234,3 @@ return {

if (this.__isHandlingUserInput && this.errorState) {
if (this.__isHandlingUserInput && this.errorState && this.inputElement) {
return this.inputElement ? this.value : undefined;

@@ -341,4 +340,2 @@ }

};
this.inputElement.addEventListener(this.formatOn, this._reflectBackFormattedValueDebounced);
this.inputElement.addEventListener('input', this._proxyInputEvent);
this.addEventListener('user-input-changed', this._onUserInputChanged);

@@ -354,2 +351,10 @@ // Connect the value found in <input> to the formatting/parsing/serializing loop as a

this._reflectBackFormattedValueToUser();
if (this.inputElement) {
this.inputElement.addEventListener(
this.formatOn,
this._reflectBackFormattedValueDebounced,
);
this.inputElement.addEventListener('input', this._proxyInputEvent);
}
}

@@ -359,10 +364,12 @@

super.disconnectedCallback();
this.inputElement.removeEventListener('input', this._proxyInputEvent);
this.removeEventListener('user-input-changed', this._onUserInputChanged);
this.inputElement.removeEventListener(
this.formatOn,
this._reflectBackFormattedValueDebounced,
);
if (this.inputElement) {
this.inputElement.removeEventListener('input', this._proxyInputEvent);
this.inputElement.removeEventListener(
this.formatOn,
this._reflectBackFormattedValueDebounced,
);
}
}
},
);

@@ -1,2 +0,2 @@

import { html, css, nothing, dedupeMixin } from '@lion/core';
import { html, css, nothing, dedupeMixin, SlotMixin } from '@lion/core';
import { ObserverMixin } from '@lion/core/src/ObserverMixin.js';

@@ -17,3 +17,3 @@

// eslint-disable-next-line no-shadow, no-unused-vars
class FormControlMixin extends ObserverMixin(superclass) {
class FormControlMixin extends ObserverMixin(SlotMixin(superclass)) {
static get properties() {

@@ -79,6 +79,19 @@ return {

/** @deprecated will be this._inputNode in next breaking release */
get inputElement() {
return (this.$$slot && this.$$slot('input')) || this.querySelector('[slot=input]'); // eslint-disable-line
return this.__getDirectSlotChild('input');
}
get _labelNode() {
return this.__getDirectSlotChild('label');
}
get _helpTextNode() {
return this.__getDirectSlotChild('help-text');
}
get _feedbackNode() {
return this.__getDirectSlotChild('feedback');
}
constructor() {

@@ -112,25 +125,27 @@ super();

_enhanceLightDomA11y() {
if (this.inputElement) {
this.inputElement.id = this.inputElement.id || this._inputId;
const { inputElement, _labelNode, _helpTextNode, _feedbackNode } = this;
if (inputElement) {
inputElement.id = inputElement.id || this._inputId;
}
if (this.$$slot('label')) {
this.$$slot('label').setAttribute('for', this._inputId);
this.$$slot('label').id = this.$$slot('label').id || `label-${this._inputId}`;
const labelledById = ` ${this.$$slot('label').id}`;
if (_labelNode) {
_labelNode.setAttribute('for', this._inputId);
_labelNode.id = _labelNode.id || `label-${this._inputId}`;
const labelledById = ` ${_labelNode.id}`;
if (this._ariaLabelledby.indexOf(labelledById) === -1) {
this._ariaLabelledby += ` ${this.$$slot('label').id}`;
this._ariaLabelledby += ` ${_labelNode.id}`;
}
}
if (this.$$slot('help-text')) {
this.$$slot('help-text').id = this.$$slot('help-text').id || `help-text-${this._inputId}`;
const describeIdHelpText = ` ${this.$$slot('help-text').id}`;
if (_helpTextNode) {
_helpTextNode.id = _helpTextNode.id || `help-text-${this._inputId}`;
const describeIdHelpText = ` ${_helpTextNode.id}`;
if (this._ariaDescribedby.indexOf(describeIdHelpText) === -1) {
this._ariaDescribedby += ` ${this.$$slot('help-text').id}`;
this._ariaDescribedby += ` ${_helpTextNode.id}`;
}
}
if (this.$$slot('feedback')) {
this.$$slot('feedback').id = this.$$slot('feedback').id || `feedback-${this._inputId}`;
const describeIdFeedback = ` ${this.$$slot('feedback').id}`;
if (_feedbackNode) {
_feedbackNode.id = _feedbackNode.id || `feedback-${this._inputId}`;
const describeIdFeedback = ` ${_feedbackNode.id}`;
if (this._ariaDescribedby.indexOf(describeIdFeedback) === -1) {
this._ariaDescribedby += ` ${this.$$slot('feedback').id}`;
this._ariaDescribedby += ` ${_feedbackNode.id}`;
}

@@ -187,3 +202,3 @@ }

additionalSlots.forEach(additionalSlot => {
const element = this.$$slot(additionalSlot);
const element = this.__getDirectSlotChild(additionalSlot);
if (element) {

@@ -225,4 +240,4 @@ element.id = element.id || `${additionalSlot}-${this._inputId}`;

_onLabelChanged({ label }) {
if (this.$$slot && this.$$slot('label')) {
this.$$slot('label').textContent = label;
if (this._labelNode) {
this._labelNode.textContent = label;
}

@@ -232,4 +247,4 @@ }

_onHelpTextChanged({ helpText }) {
if (this.$$slot && this.$$slot('help-text')) {
this.$$slot('help-text').textContent = helpText;
if (this._helpTextNode) {
this._helpTextNode.textContent = helpText;
}

@@ -561,3 +576,3 @@ }

_getAriaDescriptionElements() {
return [this.$$slot('help-text'), this.$$slot('feedback')];
return [this._helpTextNode, this._feedbackNode];
}

@@ -584,3 +599,7 @@

}
__getDirectSlotChild(slotName) {
return [...this.children].find(el => el.slot === slotName);
}
},
);
import { dedupeMixin } from '@lion/core';
import { CssClassMixin } from '@lion/core/src/CssClassMixin.js';
import { ObserverMixin } from '@lion/core/src/ObserverMixin.js';
import { Unparseable } from '@lion/validate';
import { FocusMixin } from './FocusMixin.js';

@@ -19,3 +17,3 @@ /**

// eslint-disable-next-line no-unused-vars, no-shadow
class InteractionStateMixin extends CssClassMixin(FocusMixin(ObserverMixin(superclass))) {
class InteractionStateMixin extends ObserverMixin(superclass) {
static get properties() {

@@ -29,3 +27,3 @@ return {

type: Boolean,
nonEmptyToClass: 'state-touched',
reflect: true,
},

@@ -38,3 +36,3 @@

type: Boolean,
nonEmptyToClass: 'state-dirty',
reflect: true,
},

@@ -81,3 +79,3 @@

this.prefilled = false;
this.leaveEvent = 'blur';
this._leaveEvent = 'blur';
this._valueChangedEvent = 'model-value-changed';

@@ -96,3 +94,3 @@

}
this.addEventListener(this.leaveEvent, this._iStateOnLeave);
this.addEventListener(this._leaveEvent, this._iStateOnLeave);
this.addEventListener(this._valueChangedEvent, this._iStateOnValueChange);

@@ -106,6 +104,17 @@ this.initInteractionState();

}
this.removeEventListener(this.leaveEvent, this._iStateOnLeave);
this.removeEventListener(this._leaveEvent, this._iStateOnLeave);
this.removeEventListener(this._valueChangedEvent, this._iStateOnValueChange);
}
updated(changedProperties) {
super.updated(changedProperties);
// classes are added only for backward compatibility - they are deprecated
if (changedProperties.has('touched')) {
this.classList[this.touched ? 'add' : 'remove']('state-touched');
}
if (changedProperties.has('dirty')) {
this.classList[this.dirty ? 'add' : 'remove']('state-dirty');
}
}
/**

@@ -159,3 +168,17 @@ * Evaluations performed on connectedCallback. Since some components can be out of sync

}
/**
* @deprecated
*/
get leaveEvent() {
return this._leaveEvent;
}
/**
* @deprecated
*/
set leaveEvent(eventName) {
this._leaveEvent = eventName;
}
},
);

@@ -11,2 +11,3 @@ import { DelegateMixin, SlotMixin } from '@lion/core';

import { FormatMixin } from './FormatMixin.js';
import { FocusMixin } from './FocusMixin.js';

@@ -33,5 +34,7 @@ /**

InteractionStateMixin(
FormatMixin(
ValidateMixin(
CssClassMixin(ElementMixin(DelegateMixin(SlotMixin(ObserverMixin(LionLitElement))))),
FocusMixin(
FormatMixin(
ValidateMixin(
CssClassMixin(ElementMixin(DelegateMixin(SlotMixin(ObserverMixin(LionLitElement))))),
),
),

@@ -38,0 +41,0 @@ ),

import { expect, fixture, html, aTimeout, defineCE, unsafeStatic } from '@open-wc/testing';
import sinon from 'sinon';
import { LionLitElement } from '@lion/core/src/LionLitElement.js';
import { LitElement } from '@lion/core';
import { Unparseable } from '@lion/validate';

@@ -20,3 +20,3 @@ import { FormatMixin } from '../src/FormatMixin.js';

const tagString = defineCE(
class extends FormatMixin(LionLitElement) {
class extends FormatMixin(LitElement) {
render() {

@@ -180,2 +180,10 @@ return html`

it('works if there is no underlying inputElement', async () => {
const tagNoInputString = defineCE(class extends FormatMixin(LitElement) {});
const tagNoInput = unsafeStatic(tagNoInputString);
expect(async () => {
await fixture(html`<${tagNoInput}></${tagNoInput}>`);
}).to.not.throw();
});
describe('parsers/formatters/serializers', () => {

@@ -182,0 +190,0 @@ it('should call the parser|formatter|serializer provided by user', async () => {

@@ -1,4 +0,12 @@

import { expect, fixture, unsafeStatic, html, defineCE } from '@open-wc/testing';
import {
expect,
fixture,
unsafeStatic,
html,
defineCE,
triggerFocusFor,
triggerBlurFor,
} from '@open-wc/testing';
import sinon from 'sinon';
import { LionLitElement } from '@lion/core/src/LionLitElement.js';
import { LitElement } from '@lion/core';

@@ -8,6 +16,12 @@ import { InteractionStateMixin } from '../src/InteractionStateMixin.js';

describe('InteractionStateMixin', async () => {
let elem;
let tagString;
let tag;
before(() => {
elem = defineCE(
class IState extends InteractionStateMixin(LionLitElement) {
tagString = defineCE(
class IState extends InteractionStateMixin(LitElement) {
connectedCallback() {
super.connectedCallback();
this.tabIndex = 0;
}
set modelValue(v) {

@@ -23,89 +37,78 @@ this._modelValue = v;

}
get inputElement() {
return this.querySelector('input');
}
},
);
tag = unsafeStatic(tagString);
});
it('sets states to false on init', async () => {
const input = await fixture(`<${elem}><input slot="input"></${elem}>`);
expect(input.dirty).to.equal(false);
expect(input.touched).to.equal(false);
expect(input.prefilled).to.equal(false);
const el = await fixture(html`<${tag}></${tag}>`);
expect(el.dirty).to.be.false;
expect(el.touched).to.be.false;
expect(el.prefilled).to.be.false;
});
it('sets dirty when value changed', async () => {
const input = await fixture(`<${elem}><input slot="input"></${elem}>`);
input.modelValue = 'foobar';
expect(input.dirty).to.equal(true);
const el = await fixture(html`<${tag}></${tag}>`);
expect(el.dirty).to.be.false;
el.modelValue = 'foobar';
expect(el.dirty).to.be.true;
});
// Skipping, since this issue (not being able to set focus on element extending from LitElement)
// only occurs in WCT context (not in Storybook/Stackblitz).
// See: https://stackblitz.com/edit/lit-element-request-update-bug-g59tjq?file=blurry.js
// it.skip
it('sets touched to true when field left after focus', async () => {
// const formElement = await LionTest.htmlFixture(`<${elem}><input type="text" slot="input"></${elem}>`);
// await triggerFocusFor(formElement.inputElement); // focus/blur can't be delegated
// await triggerBlurFor(formElement.inputElement);
// expect(formElement.touched).to.equal(true);
const el = await fixture(html`<${tag}></${tag}>`);
await triggerFocusFor(el);
await triggerBlurFor(el);
expect(el.touched).to.be.true;
});
// classes are added only for backward compatibility - they are deprecated
it('sets a class "state-(touched|dirty)"', async () => {
const state = await fixture(`<${elem}><input slot="input"></${elem}>`);
state.touched = true;
await state.updateComplete;
expect(state.classList.contains('state-touched')).to.equal(true, 'has class "state-touched"');
const el = await fixture(html`<${tag}></${tag}>`);
el.touched = true;
await el.updateComplete;
expect(el.classList.contains('state-touched')).to.equal(true, 'has class "state-touched"');
state.dirty = true;
await state.updateComplete;
expect(state.classList.contains('state-dirty')).to.equal(true, 'has class "state-dirty"');
el.dirty = true;
await el.updateComplete;
expect(el.classList.contains('state-dirty')).to.equal(true, 'has class "state-dirty"');
});
it('fires "(touched|dirty)-state-changed" event when state changes', async () => {
const iState = await fixture(`<${elem}><input slot="input"></${elem}>`);
const cbTouched = sinon.spy();
const cbDirty = sinon.spy();
it('sets an attribute "touched', async () => {
const el = await fixture(html`<${tag}></${tag}>`);
el.touched = true;
await el.updateComplete;
expect(el.hasAttribute('touched')).to.be.true;
});
iState.addEventListener('touched-changed', cbTouched);
iState.addEventListener('dirty-changed', cbDirty);
it('sets an attribute "dirty', async () => {
const el = await fixture(html`<${tag}></${tag}>`);
el.dirty = true;
await el.updateComplete;
expect(el.hasAttribute('dirty')).to.be.true;
});
iState.touched = true;
expect(cbTouched.callCount).to.equal(1);
it('fires "(touched|dirty)-state-changed" event when state changes', async () => {
const touchedSpy = sinon.spy();
const dirtySpy = sinon.spy();
const el = await fixture(
html`<${tag} @touched-changed=${touchedSpy} @dirty-changed=${dirtySpy}></${tag}>`,
);
iState.dirty = true;
expect(cbDirty.callCount).to.equal(1);
});
el.touched = true;
expect(touchedSpy.callCount).to.equal(1);
// Skipping, since this issue (not being able to set focus on element extending from LitElement)
// only occurs in WCT context (not in Storybook/Stackblitz).
// See: https://stackblitz.com/edit/lit-element-request-update-bug-g59tjq?file=blurry.js
// it.skip
it('sets prefilled to true when field left and value non-empty', async () => {
// const iState = await LionTest.htmlFixture(`<${elem}><input slot="input"></${elem}>`);
// await triggerFocusFor(iState.inputElement);
// iState.modelValue = externalVariables.prefilledModelValue || '000';
// await triggerBlurFor(iState.inputElement);
// expect(iState.prefilled).to.equal(true);
// await triggerFocusFor(iState.inputElement);
// iState.modelValue = externalVariables.nonPrefilledModelValue || '';
// await triggerBlurFor(iState.inputElement);
// expect(iState.prefilled).to.equal(false);
el.dirty = true;
expect(dirtySpy.callCount).to.equal(1);
});
it('sets prefilled once instantiated', async () => {
const tag = unsafeStatic(elem);
const element = await fixture(html`
<${tag}
.modelValue=${'prefilled'}
><input slot="input"></${tag}>`);
expect(element.prefilled).to.equal(true);
const el = await fixture(html`
<${tag} .modelValue=${'prefilled'}></${tag}>
`);
expect(el.prefilled).to.be.true;
const nonPrefilled = await fixture(html`
<${tag}
.modelValue=''}
><input slot="input"></${tag}>`);
expect(nonPrefilled.prefilled).to.equal(false);
<${tag} .modelValue=${''}></${tag}>
`);
expect(nonPrefilled.prefilled).to.be.false;
});

@@ -116,55 +119,74 @@

Booleans, Strings)`, async () => {
const input = await fixture(`<${elem}><input slot="input"></${elem}>`);
const el = await fixture(html`<${tag}></${tag}>`);
const changeModelValueAndLeave = modelValue => {
input.dispatchEvent(new Event('focus', { bubbles: true }));
input.modelValue = modelValue;
input.dispatchEvent(new Event('blur', { bubbles: true }));
el.dispatchEvent(new Event('focus', { bubbles: true }));
el.modelValue = modelValue;
el.dispatchEvent(new Event('blur', { bubbles: true }));
};
// Prefilled
changeModelValueAndLeave(input, ['bla']);
expect(input.prefilled).to.equal(false, 'empty array should be considered "prefilled"');
changeModelValueAndLeave(input, { bla: 'bla' });
expect(input.prefilled).to.equal(false, 'empty object should be considered "prefilled"');
changeModelValueAndLeave(input, 0);
expect(input.prefilled).to.equal(false, 'numbers should be considered "prefilled"');
changeModelValueAndLeave(input, false);
expect(input.prefilled).to.equal(false, 'Booleans should be considered "prefilled"');
changeModelValueAndLeave(input, '');
expect(input.prefilled).to.equal(false, 'empty string should be considered "prefilled"');
changeModelValueAndLeave(['not-empty']);
expect(el.prefilled, 'not empty array should be "prefilled"').to.be.true;
changeModelValueAndLeave({ not: 'empty' });
expect(el.prefilled, 'not empty object should be "prefilled"').to.be.true;
changeModelValueAndLeave(0);
expect(el.prefilled, 'numbers should be "prefilled"').to.be.true;
changeModelValueAndLeave(false);
expect(el.prefilled, 'booleans should be "prefilled"').to.be.true;
// Not prefilled
changeModelValueAndLeave(input, []);
expect(input.prefilled).to.equal(false, 'empty array should not be considered "prefilled"');
changeModelValueAndLeave(input, {});
expect(input.prefilled).to.equal(false, 'empty object should not be considered "prefilled"');
changeModelValueAndLeave(input, '');
expect(input.prefilled).to.equal(false, 'empty string should not be considered "prefilled"');
changeModelValueAndLeave(input, null);
expect(input.prefilled).to.equal(false, 'null should not be considered "prefilled"');
changeModelValueAndLeave(input, undefined);
expect(input.prefilled).to.equal(false, 'undefined should not be considered "prefilled"');
changeModelValueAndLeave([]);
expect(el.prefilled, 'empty array should not be "prefilled"').to.be.false;
changeModelValueAndLeave({});
expect(el.prefilled, 'empty object should not be "prefilled"').to.be.false;
changeModelValueAndLeave('');
expect(el.prefilled, 'empty string should not be "prefilled"').to.be.false;
changeModelValueAndLeave(null);
expect(el.prefilled, 'null should not be "prefilled"').to.be.false;
changeModelValueAndLeave(undefined);
expect(el.prefilled, 'undefined should not be "prefilled"').to.be.false;
});
it('has a method resetInteractionState()', async () => {
const input = await fixture(`<${elem}><input slot="input"></${elem}>`);
input.dirty = true;
input.touched = true;
input.prefilled = true;
input.resetInteractionState();
expect(input.dirty).to.equal(false);
expect(input.touched).to.equal(false);
expect(input.prefilled).to.equal(false);
const el = await fixture(html`<${tag}></${tag}>`);
el.dirty = true;
el.touched = true;
el.prefilled = true;
el.resetInteractionState();
expect(el.dirty).to.be.false;
expect(el.touched).to.be.false;
expect(el.prefilled).to.be.false;
input.dirty = true;
input.touched = true;
input.prefilled = false;
input.modelValue = 'Some value';
input.resetInteractionState();
expect(input.dirty).to.equal(false);
expect(input.touched).to.equal(false);
expect(input.prefilled).to.equal(true);
el.dirty = true;
el.touched = true;
el.prefilled = false;
el.modelValue = 'Some value';
el.resetInteractionState();
expect(el.dirty).to.be.false;
expect(el.touched).to.be.false;
expect(el.prefilled).to.be.true;
});
describe('SubClassers', () => {
it('can override the `_leaveEvent`', async () => {
const tagLeaveString = defineCE(
class IState extends InteractionStateMixin(LitElement) {
constructor() {
super();
this._leaveEvent = 'custom-blur';
}
},
);
const tagLeave = unsafeStatic(tagLeaveString);
const el = await fixture(html`<${tagLeave}></${tagLeave}>`);
el.dispatchEvent(new Event('custom-blur'));
expect(el.touched).to.be.true;
});
it('can override the deprecated `leaveEvent`', async () => {
const el = await fixture(html`<${tag} .leaveEvent=${'custom-blur'}></${tag}>`);
expect(el._leaveEvent).to.equal('custom-blur');
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc