chore: add tutorial Input extesnion

This commit is contained in:
Thijs Louisse 2021-05-01 18:20:29 +02:00 committed by Thomas Allmer
parent 9538aa5aa6
commit cd51f93143
3 changed files with 71 additions and 74 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/input-date': patch
---
configure in constructor, so property effects use preconfigured value

View file

@ -1,74 +0,0 @@
# How To >> Creating a custom field ||20
```js script
import { html, render } from '@lion/core';
import { Validator } from '@lion/form-core';
import '@lion/input/define';
import '../../docs/systems/form/assets/h-output.js';
```
Custom fields can be created in just a few steps. All you need is an interaction element (like for instance a slider, a listbox or a combobox) and connect it to the [input components](../../components/inputs/input/overview.md).
## Prerequisite: an interaction element
An interaction element provides the means for the end user to enter a certain value, just like native elements provide in this (think of `<input>`, `<textarea>` and `<select>`). An example of a non native element is the [slider design pattern](https://www.w3.org/TR/wai-aria-practices-1.1/#slider) described here.
For this tutorial, we assume we have a component `<my-slider>` that exposes its value via property `mySliderValue` and sends an event `my-slider-changed` on every value change. To make it focusable, it has a tabindex=“0” applied.
## Connecting the interaction element to the field
Now we want to integrate the slider in our form framework to enrich the user interface, get
validation support and get all the other [benefits of LionField](../../docs/systems/form/overview.md). We start by creating a component `<lion-slider>` that extends from `LionField`. Then we follow the steps below:
### 1. Add your interaction element as input slot'
Here you return the element the user interacts with. By configuring it as a slot, it will end up in light DOM, ensuring the best accessibility for the end user.
### 2. Proxy event `my-slider-changed` to `user-input-changed` event
The `user-input-changed` event is listened to by the FormatMixin: it should be regarded as the equivalent of the `input` event of the platform, but for custom built interaction elements.
### 3. Proxy property `<my-slider>.mySliderValue` to `<lion-slider>.value`
Every time the `user-input-changed` fires, the value of `<my-slider>` is synchronized with the [`modelValue`](../../docs/systems/form/model-value.md) of `<my-slider>`. Now the cycle is complete: the modelValue connects your interaction element to all logic inside the LionField.
Steps as described can be implemented with the following javascript:
```js
import { LionField } from '@lion/form-core';
import './my-slider.js';
export class LionSlider extends LionField {
// 1. Add your interaction element as input slot'
get slots() {
return {
...super.slots,
input: () => document.createElement(my-slider),
};
}
// 2. Proxy event `my-slider-changed` to `user-input-changed` event
connectedCallback() {
super.connectedCallback();
this.addEventListener('my-slider-changed', this._proxyChangeEvent);
}
_proxyChangeEvent() {
this._inputNode.dispatchEvent(
new CustomEvent('user-input-changed', { bubbles: true, composed: true }),
);
}
// 3. Proxy property `<my-slider>.mySliderValue` to `<lion-slider>.value`
get value() {
return Array.from(this.children).find(child => child.slot === 'input').mySliderValue;
}
set value(newV) {
Array.from(this.children).find(child => child.slot === 'input').mySliderValue = newV;
}
}
```
That was all. Now you can enhance your slider by writing custom validators for it or by writing a parser to get a custom modelValue type.

View file

@ -0,0 +1,66 @@
# How To >> Extend a native Input ||20
```js script
import { html } from '@lion/core';
import { LionInput } from '@lion/input';
import { LionInputDate } from '@lion/input-date';
import { loadDefaultFeedbackMessages } from '@lion/validate-messages';
import '@lion/input/define';
import '../../docs/systems/form/assets/h-output.js';
loadDefaultFeedbackMessages();
```
Input fields can be created by extending [LionInput](../../components/inputs/input/overview.md).
> In case you want to wrap a custom form element, follow [Create a custom Field](./create-a-custom-field.md).
For this tutorial, we create an input that wraps native `input[type=datetime-local]`.
This is as simple as adding a type:
```js preview-story
export const extendLionInput = () => {
class LionInputDatetime extends LionInput {
constructor() {
super();
this.type = 'datetime-local';
}
}
customElements.define('lion-input-datetime', LionInputDatetime);
return html`<lion-input-datetime label="With Date string"></lion-input-datetime>
<h-output .show="${['modelValue', 'touched', 'dirty', 'focused']}"></h-output>`;
};
```
However, we might want to have a more advanced modelValue. In the example above, our modelValue is
a serialized datetime string.
If we want our modelValue to be of type Date, we should do the following:
```js preview-story
export const extendLionInputDate = () => {
function toIsoDatetime(d) {
return d && new Date(d.getTime() - d.getTimezoneOffset() * 60000).toISOString().split('.')[0];
}
function fromIsoDatetime(value) {
return new Date(value);
}
class LionInputDatetimeWithObject extends LionInputDate {
constructor() {
super();
this.type = 'datetime-local';
this.parser = fromIsoDatetime;
this.deserializer = fromIsoDatetime;
this.serializer = toIsoDatetime;
this.formatter = toIsoDatetime;
}
}
customElements.define('lion-input-datetime-with-object', LionInputDatetimeWithObject);
return html`<lion-input-datetime-with-object
label="With Date object"
></lion-input-datetime-with-object>
<h-output .show="${['modelValue', 'touched', 'dirty', 'focused']}"></h-output>`;
};
```