Socket
Socket
Sign inDemoInstall

@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.4.1 to 0.5.0

11

CHANGELOG.md

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

# [0.5.0](https://github.com/ing-bank/lion/compare/@lion/field@0.4.1...@lion/field@0.5.0) (2019-11-18)
### Features
* finalize validation and adopt it everywhere ([396deb2](https://github.com/ing-bank/lion/commit/396deb2e3b4243f102a5c98e9b0518fa0f31a6b1))
## [0.4.1](https://github.com/ing-bank/lion/compare/@lion/field@0.4.0...@lion/field@0.4.1) (2019-11-15)

@@ -8,0 +19,0 @@

4

docs/modelValue.md

@@ -64,4 +64,4 @@ # ModelValue

```js
function handleChange({ target: { modelValue, errorState } }) {
if (!(modelValue instanceof Unparseable) && !errorState) {
function handleChange({ target: { modelValue, hasFeedbackFor } }) {
if (!(modelValue instanceof Unparseable) && !(hasFeedbackFor.include('error))) {
// do my thing

@@ -68,0 +68,0 @@ }

{
"name": "@lion/field",
"version": "0.4.1",
"version": "0.5.0",
"description": "Fields are the most fundamental building block of the Form System",

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

"@lion/core": "^0.3.0",
"@lion/validate": "^0.3.1"
"@lion/validate": "^0.4.0"
},

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

},
"gitHead": "e1485188a03c063819623288f3231cfecca023ec"
"gitHead": "da30ee3bb13d26f873d633ed960163bec9cba164"
}

@@ -13,3 +13,3 @@ /* eslint-disable class-methods-use-this */

// - consider `formatOn` as an overridable function, by default something like:
// `(!__isHandlingUserInput || !errorState) && !focused`
// `(!__isHandlingUserInput || !hasError) && !focused`
// This would allow for more advanced scenarios, like formatting an input whenever it becomes valid.

@@ -249,3 +249,3 @@ // This would make formattedValue as a concept obsolete, since for maximum flexibility, the

__callFormatter() {
// - Why check for this.errorState?
// - Why check for this.hasError?
// We only want to format values that are considered valid. For best UX,

@@ -257,7 +257,13 @@ // we only 'reward' valid inputs.

// the value, no matter what.
// This means, whenever we are in errorState and modelValue is set
// This means, whenever we are in hasError and modelValue is set
// imperatively, we DO want to format a value (it is the only way to get meaningful
// input into `._inputNode` with modelValue as input)
if (this.__isHandlingUserInput && this.errorState && this._inputNode) {
if (
this.__isHandlingUserInput &&
this.hasFeedbackFor &&
this.hasFeedbackFor.length &&
this.hasFeedbackFor.includes('error') &&
this._inputNode
) {
return this._inputNode ? this.value : undefined;

@@ -264,0 +270,0 @@ }

@@ -467,44 +467,2 @@ import { html, css, nothing, dedupeMixin, SlotMixin } from '@lion/core';

// Extend validity showing conditions of ValidateMixin
showErrorCondition(newStates) {
return super.showErrorCondition(newStates) && this._interactionStateFeedbackCondition();
}
showWarningCondition(newStates) {
return super.showWarningCondition(newStates) && this._interactionStateFeedbackCondition();
}
showInfoCondition(newStates) {
return super.showInfoCondition(newStates) && this._interactionStateFeedbackCondition();
}
showSuccessCondition(newStates, oldStates) {
return (
super.showSuccessCondition(newStates, oldStates) &&
this._interactionStateFeedbackCondition()
);
}
_interactionStateFeedbackCondition() {
/**
* Show the validity feedback when one of the following conditions is met:
*
* - submitted
* If the form is submitted, always show the error message.
*
* - prefilled
* the user already filled in something, or the value is prefilled
* when the form is initially rendered.
*
* - touched && dirty && !prefilled
* When a user starts typing for the first time in a field with for instance `required`
* validation, error message should not be shown until a field becomes `touched`
* (a user leaves(blurs) a field).
* When a user enters a field without altering the value(making it `dirty`),
* an error message shouldn't be shown either.
*
*/
return (this.touched && this.dirty) || this.prefilled || this.submitted;
}
// aria-labelledby and aria-describedby helpers

@@ -511,0 +469,0 @@ // TODO: consider extracting to generic ariaLabel helper mixin

@@ -228,10 +228,12 @@ import { SlotMixin, LitElement } from '@lion/core';

// eslint-disable-next-line class-methods-use-this
__isRequired(modelValue) {
return {
required:
(typeof modelValue === 'string' && modelValue !== '') ||
(typeof modelValue !== 'string' && typeof modelValue !== 'undefined'), // TODO: && modelValue !== null ?
};
set fieldName(value) {
this.__fieldName = value;
}
get fieldName() {
const label =
this.label ||
(this.querySelector('[slot=label]') && this.querySelector('[slot=label]').textContent);
return this.__fieldName || label || this.name;
}
}

@@ -5,3 +5,3 @@ import { expect, fixture, html, aTimeout, defineCE, unsafeStatic } from '@open-wc/testing';

import { LitElement } from '@lion/core';
import { Unparseable } from '@lion/validate';
import { Unparseable, Validator } from '@lion/validate';
import { FormatMixin } from '../src/FormatMixin.js';

@@ -325,3 +325,3 @@

it('will only call the formatter for valid values on `user-input-changed` ', async () => {
it.skip('will only call the formatter for valid values on `user-input-changed` ', async () => {
const formatterSpy = sinon.spy(value => `foo: ${value}`);

@@ -343,16 +343,26 @@

el.errorState = true;
// Ensure errorState is always true by putting a validator on it that always returns false.
// Setting errorState = true is not enough if the element has errorValidators (uses ValidateMixin)
// that set errorState back to false when the user input is mimicked.
const alwaysInvalidator = () => ({ 'always-invalid': false });
el.errorValidators = [alwaysInvalidator];
el.hasError = true;
// Ensure hasError is always true by putting a validator on it that always returns false.
// Setting hasError = true is not enough if the element has errorValidators (uses ValidateMixin)
// that set hasError back to false when the user input is mimicked.
const AlwaysInvalid = class extends Validator {
constructor(...args) {
super(...args);
this.name = 'AlwaysInvalid';
}
execute() {
return true;
}
};
el.validators = [new AlwaysInvalid()];
mimicUserInput(el, generatedViewValueAlt);
expect(formatterSpy.callCount).to.equal(1);
// Due to errorState, the formatter should not have ran.
// Due to hasError, the formatter should not have ran.
expect(el.formattedValue).to.equal(generatedViewValueAlt);
el.errorState = false;
el.errorValidators = [];
el.hasError = false;
el.validators = [];
mimicUserInput(el, generatedViewValue);

@@ -359,0 +369,0 @@ expect(formatterSpy.callCount).to.equal(2);

@@ -12,2 +12,3 @@ import {

import sinon from 'sinon';
import { Validator, Required } from '@lion/validate';
import { localize } from '@lion/localize';

@@ -39,2 +40,20 @@ import { localizeTearDown } from '@lion/localize/test-helpers.js';

it(`has a fieldName based on the label`, async () => {
const el1 = await fixture(html`<${tag} label="foo">${inputSlot}</${tag}>`);
expect(el1.fieldName).to.equal(el1._labelNode.textContent);
const el2 = await fixture(html`<${tag}><label slot="label">bar</label>${inputSlot}</${tag}>`);
expect(el2.fieldName).to.equal(el2._labelNode.textContent);
});
it(`has a fieldName based on the name if no label exists`, async () => {
const el = await fixture(html`<${tag} name="foo">${inputSlot}</${tag}>`);
expect(el.fieldName).to.equal(el.name);
});
it(`can override fieldName`, async () => {
const el = await fixture(html`<${tag} label="foo" .fieldName="${'bar'}">${inputSlot}</${tag}>`);
expect(el.__fieldName).to.equal(el.fieldName);
});
it('fires focus/blur event on host and native input if focused/blurred', async () => {

@@ -289,39 +308,64 @@ const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`);

it('shows validity states(error|warning|info|success) when interaction criteria met ', async () => {
// TODO: in order to make this test work as an integration test, we chose a modelValue
// that is compatible with lion-input-email.
// However, when we can put priorities to validators (making sure error message of hasX is
// shown instead of a predefined validator like isEmail), we should fix this.
function hasX(str) {
return { hasX: str.indexOf('x') > -1 };
}
const el = await fixture(html`<${tag}>${inputSlot}</${tag}>`);
const feedbackEl = el._feedbackElement;
it('should conditionally show error', async () => {
const HasX = class extends Validator {
constructor() {
super();
this.name = 'HasX';
}
el.modelValue = 'a@b.nl';
el.errorValidators = [[hasX]];
execute(value) {
const result = value.indexOf('x') === -1;
return result;
}
};
const el = await fixture(html`
<${tag}
.validators=${[new HasX()]}
.modelValue=${'a@b.nl'}
>
${inputSlot}
</${tag}>
`);
expect(el.error.hasX).to.equal(true);
expect(feedbackEl.innerText.trim()).to.equal(
'',
'shows no feedback, although the element has an error',
);
el.dirty = true;
el.touched = true;
el.modelValue = 'ab@c.nl'; // retrigger validation
await el.updateComplete;
const executeScenario = async (_sceneEl, scenario) => {
const sceneEl = _sceneEl;
sceneEl.resetInteractionState();
sceneEl.touched = scenario.el.touched;
sceneEl.dirty = scenario.el.dirty;
sceneEl.prefilled = scenario.el.prefilled;
sceneEl.submitted = scenario.el.submitted;
expect(feedbackEl.innerText.trim()).to.equal(
'This is error message for hasX',
'shows feedback, because touched=true and dirty=true',
);
await sceneEl.updateComplete;
await sceneEl.feedbackComplete;
expect(sceneEl.showsFeedbackFor).to.deep.equal(scenario.wantedShowsFeedbackFor);
};
el.touched = false;
el.dirty = false;
el.prefilled = true;
await el.updateComplete;
expect(feedbackEl.innerText.trim()).to.equal(
'This is error message for hasX',
'shows feedback, because prefilled=true',
);
await executeScenario(el, {
index: 0,
el: { touched: true, dirty: true, prefilled: false, submitted: false },
wantedShowsFeedbackFor: ['error'],
});
await executeScenario(el, {
index: 1,
el: { touched: false, dirty: false, prefilled: true, submitted: false },
wantedShowsFeedbackFor: ['error'],
});
await executeScenario(el, {
index: 2,
el: { touched: false, dirty: false, prefilled: false, submitted: true },
wantedShowsFeedbackFor: ['error'],
});
await executeScenario(el, {
index: 3,
el: { touched: false, dirty: true, prefilled: false, submitted: false },
wantedShowsFeedbackFor: [],
});
await executeScenario(el, {
index: 4,
el: { touched: true, dirty: false, prefilled: false, submitted: false },
wantedShowsFeedbackFor: [],
});
});

@@ -332,8 +376,10 @@

<${tag}
.errorValidators=${[['required']]}
.validators=${[new Required()]}
>${inputSlot}</${tag}>
`);
expect(el.error.required).to.be.true;
expect(el.hasFeedbackFor).to.deep.equal(['error']);
expect(el.validationStates.error).to.have.a.property('Required');
el.modelValue = 'cat';
expect(el.error.required).to.be.undefined;
expect(el.hasFeedbackFor).to.deep.equal([]);
expect(el.validationStates.error).not.to.have.a.property('Required');
});

@@ -343,5 +389,13 @@

const formatterSpy = sinon.spy(value => `foo: ${value}`);
function isBarValidator(value) {
return { isBar: value === 'bar' };
}
const Bar = class extends Validator {
constructor(...args) {
super(...args);
this.name = 'Bar';
}
execute(value) {
const hasError = value !== 'bar';
return hasError;
}
};
const el = await fixture(html`

@@ -351,3 +405,3 @@ <${tag}

.formatter=${formatterSpy}
.errorValidators=${[[isBarValidator]]}
.validators=${[new Bar()]}
>${inputSlot}</${tag}>

@@ -354,0 +408,0 @@ `);

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