diff --git a/packages/form-system/package.json b/packages/form-system/package.json
index 3b30205c9..2b3ffc269 100644
--- a/packages/form-system/package.json
+++ b/packages/form-system/package.json
@@ -35,6 +35,7 @@
"@lion/checkbox": "^0.1.51",
"@lion/checkbox-group": "^0.1.57",
"@lion/core": "^0.1.13",
+ "@lion/field": "^0.2.3",
"@lion/fieldset": "^0.1.50",
"@lion/form": "^0.1.56",
"@lion/input": "^0.1.50",
@@ -42,9 +43,11 @@
"@lion/input-date": "^0.1.51",
"@lion/input-email": "^0.1.50",
"@lion/input-iban": "^0.1.52",
+ "@lion/localize": "^0.4.14",
"@lion/radio": "^0.1.51",
"@lion/radio-group": "^0.1.57",
"@lion/textarea": "^0.1.53",
+ "@lion/validate": "^0.2.29",
"@open-wc/demoing-storybook": "^0.2.0",
"@open-wc/testing": "^2.0.6"
}
diff --git a/packages/form-system/stories/formatting.stories.js b/packages/form-system/stories/formatting.stories.js
new file mode 100644
index 000000000..9be528afa
--- /dev/null
+++ b/packages/form-system/stories/formatting.stories.js
@@ -0,0 +1,131 @@
+import { storiesOf, html } from '@open-wc/demoing-storybook';
+import { Unparseable } from '@lion/validate';
+import '@lion/input/lion-input.js';
+import './helper-wc/h-output.js';
+
+function newDateValid(d) {
+ const result = d ? new Date(d) : new Date();
+ return !isNaN(result.getTime()) ? result : null; // eslint-disable-line no-restricted-globals
+}
+
+storiesOf('Form Fundaments|Formatting and Parsing', module)
+ .add(
+ 'model value',
+ () => html`
+
+ Note: we always use lion-input to demonstrate, but all things that extend lion-input have
+ this functionality!
+
+ {
+ console.log(target);
+ }}"
+ >
+
+
+ `,
+ )
+ .add(
+ 'parser',
+ () => html`
+
+
+
+ `,
+ )
+ .add(
+ 'formatter',
+ () => html`
+
+
+
+ `,
+ )
+ /* .add(
+ 'preprocessor',
+ () => html`
+
+
+
+ `,
+ ) */
+ .add(
+ '(de)serializer',
+ () => html`
+
+
+
+ `,
+ )
+ .add(
+ 'Unparseable',
+ () => html`
+ {
+ if (modelValue instanceof Unparseable) {
+ console.log(`End user attempted to create a valid entry and most likely is in
+ the process of doing so. We can retrieve the intermediate state via modelValue.viewValue`);
+ } else if (errorState) {
+ console.log(`We know now end user entered a valid type, but some constraints
+ (for instance min date) were not met`);
+ } else {
+ console.log(`Now we know end user entered a valid input: the field is valid
+ and modelValue can be used in Application context for further processing`);
+ }
+ }}"
+ >
+
+
+
+
+ `,
+ )
+ .add(
+ 'Unparseable restore',
+ () => html`
+
+
+
+ `,
+ );
diff --git a/packages/form-system/stories/helper-wc/h-output.js b/packages/form-system/stories/helper-wc/h-output.js
new file mode 100644
index 000000000..34a6a8902
--- /dev/null
+++ b/packages/form-system/stories/helper-wc/h-output.js
@@ -0,0 +1,105 @@
+import { LitElement, html, css } from '@lion/core';
+import { LionField } from '@lion/field';
+
+export class HelperOutput extends LitElement {
+ static get properties() {
+ return {
+ field: Object,
+ show: Array,
+ };
+ }
+
+ static get styles() {
+ return [
+ css`
+ :host {
+ display: block;
+ margin-top: 16px;
+ }
+
+ table,
+ th,
+ td {
+ border: 1px solid #ccc;
+ padding: 4px;
+ font-size: 12px;
+ }
+
+ table {
+ border-collapse: collapse;
+ }
+
+ caption {
+ text-align: left;
+ }
+ `,
+ ];
+ }
+
+ firstUpdated(c) {
+ super.firstUpdated(c);
+ if (!this.field) {
+ // Fuzzy logic, but... practical
+ const prev = this.previousElementSibling;
+ if (prev instanceof LionField) {
+ this.field = prev;
+ }
+ }
+ this.__rerender = this.__rerender.bind(this);
+ this.field.addEventListener('model-value-changed', this.__rerender);
+ this.field.addEventListener('mousemove', this.__rerender);
+ this.field.addEventListener('blur', this.__rerender);
+ this.field.addEventListener('focusin', this.__rerender);
+ this.field.addEventListener('focusout', this.__rerender);
+
+ if (this.field.inputElement.form) {
+ this.field.inputElement.form.addEventListener('submit', this.__rerender);
+ }
+ }
+
+ __rerender() {
+ setTimeout(() => {
+ const f = this.field;
+ this.field = null;
+ this.field = f;
+ });
+ }
+
+ __renderProp(p) {
+ if (typeof p === 'boolean') {
+ return p === true ? '✓' : '';
+ }
+ if (typeof p === 'undefined') {
+ return '?';
+ }
+ return p;
+ }
+
+ render() {
+ const field = this.field || {};
+ return html`
+
+
+ Interaction States
+
+
+ ${this.show.map(
+ prop => html`
+ ${prop}
+ `,
+ )}
+
+
+
+ ${this.show.map(
+ prop => html`
+ ${this.__renderProp(field[prop])}
+ `,
+ )}
+
+
+ `;
+ }
+}
+
+customElements.define('h-output', HelperOutput);
diff --git a/packages/form-system/stories/interactionStates.stories.js b/packages/form-system/stories/interactionStates.stories.js
new file mode 100644
index 000000000..fdebf0a5b
--- /dev/null
+++ b/packages/form-system/stories/interactionStates.stories.js
@@ -0,0 +1,105 @@
+import { storiesOf, html } from '@open-wc/demoing-storybook';
+import { render } from '@lion/core';
+import { localize } from '@lion/localize';
+import '@lion/checkbox/lion-checkbox.js';
+import '@lion/checkbox-group/lion-checkbox-group.js';
+import '@lion/form/lion-form.js';
+import '@lion/input/lion-input.js';
+import './helper-wc/h-output.js';
+
+function renderOffline(litHtmlTemplate) {
+ const offlineRenderContainer = document.createElement('div');
+ render(litHtmlTemplate, offlineRenderContainer);
+ return offlineRenderContainer.firstElementChild;
+}
+
+function addTranslations(ns, data) {
+ if (!localize._isNamespaceInCache('en-GB', ns)) {
+ localize.addData('en-GB', ns, data);
+ }
+}
+
+storiesOf('Form Fundaments|Interaction States', module)
+ .add(
+ 'States',
+ () => html`
+
+
+
+
+
+
+ `,
+ )
+ .add('Feedback condition', () => {
+ // 1. Initialize variables...
+ // properties on InteractionStateMixin we want to check conditions for
+ const props = ['touched', 'dirty', 'prefilled', 'focused', 'filled', 'submitted'];
+
+ // Here we will store the conditions (trigger for validation feedback)
+ // provided via the UI of the demo
+ let conditions = [];
+
+ // 2. Create a validator...
+ // Define a demo validator that should only be visible on an odd amount of characters
+ const oddValidator = [modelValue => ({ odd: modelValue.length % 2 !== 0 })];
+
+ addTranslations('lion-validate+odd', {
+ error: {
+ odd: '[ Error feedback ] : Add or remove one character',
+ },
+ });
+
+ // 3. Create field overriding .showErrorCondition...
+ // Here we will store a reference to the Field element that overrides the default condition
+ // (function `showErrorCondition`) for triggering validation feedback of `.errorValidators`
+ const fieldElement = renderOffline(html`
+
+
+
+ `);
+
+ function fetchConditionsAndReevaluate({ currentTarget: { modelValue } }) {
+ if (!modelValue['props[]']) {
+ return;
+ }
+ // Create props list like: ['touched', 'submitted']
+ conditions = modelValue['props[]'].filter(p => p.checked).map(p => p.value);
+ // Reevaluate
+ fieldElement.validate();
+ }
+
+ return html`
+
+
+
+
+
+
+
+ Set conditions for validation feedback visibility
+
+
+
+ ${props.map(
+ p => html`
+
+ `,
+ )}
+
+ `;
+ });
diff --git a/stories/index.stories.js b/stories/index.stories.js
index dbf8e0500..bd8ebdfa8 100755
--- a/stories/index.stories.js
+++ b/stories/index.stories.js
@@ -14,7 +14,10 @@ import '../packages/fieldset/stories/index.stories.js';
import '../packages/checkbox-group/stories/index.stories.js';
import '../packages/radio-group/stories/index.stories.js';
import '../packages/form/stories/index.stories.js';
+
import '../packages/form-system/stories/index.stories.js';
+import '../packages/form-system/stories/formatting.stories.js';
+import '../packages/form-system/stories/interactionStates.stories.js';
import '../packages/icon/stories/index.stories.js';
import '../packages/ajax/stories/index.stories.js';