chore(field): custom fields tutorial

This commit is contained in:
Thijs Louisse 2019-04-29 09:55:49 +02:00 committed by Thomas Allmer
parent d5e2dfe38e
commit 6a0579cd91
3 changed files with 114 additions and 29 deletions

View file

@ -1,53 +1,64 @@
# Form Fundaments
[//]: # (AUTO INSERT HEADER PREPUBLISH)
Fields are the most fundamental building block of the Form System. They are the basis of
both `field`s and `fieldset`s.
`Form control`s are the most fundamental building block of the Forms. They are the basis of
both `field`s and `fieldset`s, and the `form` itself.
## Fields
## What are fields?
Fields are the actual form controls the end user interacts with.
They extend the `LionField` class, which on its turn uses the `FormControlMixin`.
Fields provide a normalized, predictable API for platform components and customly made form controls.
They extend the `Field` class, which on its turn uses the `FormControlMixin`.
Fields provide a normalized, predictable API for platform components and custom made form controls.
On top of this, they feature:
- data synchronization with models
- formatting of view values
- advanced validation possibilities
- creation of advanced user interaction scenarios via `interaction states`
- provision of labels, help texts in an easy, declaritive manner
- provision of labels and help texts in an easy, declarative manner
- better focus management
- accessibility out of the box
- advanced styling possibilities: map your own Design System to the internal HTML structure
### Platform wrappers
- `LionInput`, a wrapper for `<input>`
- `LionTextarea`, a wrapper for `<textarea>`
- `LionSelect`, a wrapper for `<select>`
### Platform fields (wrappers)
- [`LionInput`](../input/), a wrapper for `<input>`
- [`LionTextarea`](../textarea/), a wrapper for `<textarea>`
- [`LionSelect`](../select/), a wrapper for `<select>`
- [`LionRadio`](../radio/), a wrapper for `<input type="radio">`
- [`LionCheckbox`](../checkbox/), a wrapper for `<input type="checkbox">`
### Custom fields (wrappers)
Whenever a native form control doesn't exist or is not sufficient, a
[custom form field](./docs/CustomFieldsTutorial.md) should be created. One could think of components
like:
### Custom wrappers
Whenever a native form control doesn't exist or is not sufficient, a custom form control should
be created. One could think of components like:
- slider
- combobox
- autocomplete
- etc...
## What are fieldsets?
## Fieldsets
Fieldsets are groups of fields. They can be considered fields on their own as well, since they
partly share the normalized api via `FormControlMixin`.
Fieldsets are the basis for:
- `LionFieldset`
- `LionForm`
- `LionRadioGroup`
- `LionCheckboxGroup`
- [`LionFieldset`](../fieldset/)
- [`LionForm`](../form/)
- [`LionRadioGroup`](../radio-group/)
- [`LionCheckboxGroup`](../checkbox-group/)
# Other Resources
- `FormControlMixin` (TODO: document)
- `LionField` (TODO: document)
- [`Model values`](./docs/modelValue.md)
- [`FormatMixin`](./docs/FormatMixin.md)
- `InteractionStateMixin` (TODO: document)
- `ValidateMixin` (TODO: document)
- `FocusMixin` (TODO: document)
- `FieldCustomMixin` (TODO: document)
## Other Resources
<!-- TODO: - [`FormControlMixin`] () -->
<!-- TODO: - [`LionField`] () -->
- [Model Value](./docs/ModelValue.md)
- [Formatting and parsing](./docs/FormattingAndParsing.md)
- [Interaction states](./docs/InteractionStates.md)
- [Validation System](../validate/docs/ValidationSystem.md)
- [Custom Fields](./docs/CustomFieldsTutorial.md)
<!-- TODO: - [`FocusMixin`] (/FocusMixin.md) -->

View file

@ -0,0 +1,76 @@
# Creating a custom field
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 [Field](../README.md)
functionality.
## 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/2017/NOTE-wai-aria-practices-1.1-20171214/#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](../README.md).
We start of 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`](./modelValue.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/field';
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.inputElement.dispatchEvent(
new CustomEvent('user-input-changed', { bubbles: true, composed: true }),
);
}
// 3. Proxy property `<my-slider>.mySliderValue` to `<lion-slider>.value`
get value() {
   return this.$$slot('input').mySliderValue;
}
set value(newV) {
   this.$$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

@ -1,2 +0,0 @@
# FieldCustomMixin
TODO: document