chore: fix style of markdown files

This commit is contained in:
Mikhail Bashkirov 2019-07-24 15:05:12 +02:00
parent c23fc22061
commit 2de378391c
42 changed files with 454 additions and 308 deletions

View file

@ -3,10 +3,12 @@
Check out ways to contribute to Lion Web Components: Check out ways to contribute to Lion Web Components:
## Existing components: we love pull requests ♥ ## Existing components: we love pull requests ♥
Help out the whole lion community by sending your merge requests and issues. Help out the whole lion community by sending your merge requests and issues.
Check out how to set it up: Check out how to set it up:
Setup: Setup:
```bash ```bash
# Clone the repo: # Clone the repo:
git clone https://github.com/ing-bank/lion.git git clone https://github.com/ing-bank/lion.git
@ -20,6 +22,7 @@ git checkout -b fix/buttonSize
``` ```
Make sure everything works as expected: Make sure everything works as expected:
```bash ```bash
# Linting # Linting
npm run lint npm run lint
@ -32,7 +35,9 @@ npm run storybook
``` ```
Create a Pull Request: Create a Pull Request:
- At https://github.com/ing-bank/lion click on fork (at the right top)
- At <https://github.com/ing-bank/lion> click on fork (at the right top)
```bash ```bash
# add fork to your remotes # add fork to your remotes
git remote add fork git@github.com:<your-user>/lion.git git remote add fork git@github.com:<your-user>/lion.git
@ -40,9 +45,10 @@ git remote add fork git@github.com:<your-user>/lion.git
# push new branch to your fork # push new branch to your fork
git push -u fork fix/buttonSize git push -u fork fix/buttonSize
``` ```
- Go to your fork and create a Pull Request :) - Go to your fork and create a Pull Request :)
Some things that will increase the chance that your merge request is accepted: Some things that will increase the chance that your merge request is accepted:
* Write tests. - Write tests.
* Write a [good commit message](https://www.conventionalcommits.org/). - Write a [good commit message](https://www.conventionalcommits.org/).

View file

@ -1,3 +1,5 @@
# Lion Web Components
> ## 🛠 Status: Pilot Phase > ## 🛠 Status: Pilot Phase
> >
> Lion Web Components are still in an early alpha stage; they should not be considered production ready yet. > Lion Web Components are still in an early alpha stage; they should not be considered production ready yet.
@ -8,9 +10,7 @@
> - not publicly promote or link us yet: (no tweets, blog posts or other forms of communication about Lion Web Components) > - not publicly promote or link us yet: (no tweets, blog posts or other forms of communication about Lion Web Components)
> - not publicly promote or link products derived from/based on Lion Web Components > - not publicly promote or link products derived from/based on Lion Web Components
> >
> As soon as Pilot Phase ends we will let you know (feel free to subscribe to this issue https://github.com/ing-bank/lion/issues/1) > As soon as Pilot Phase ends we will let you know (feel free to subscribe to this issue <https://github.com/ing-bank/lion/issues/1>)
# Lion Web Components
Lion web components is a set of highly performant, accessible and flexible Web Components. Lion web components is a set of highly performant, accessible and flexible Web Components.
They provide an unopinionated, white label layer that can be extended to your own layer of components. They provide an unopinionated, white label layer that can be extended to your own layer of components.

View file

@ -6,27 +6,30 @@
It is a promise based system for fetching data, based on [axios](https://github.com/axios/axios) It is a promise based system for fetching data, based on [axios](https://github.com/axios/axios)
## Features ## Features
- only JS functions, no (unnecessarily expensive) web components - only JS functions, no (unnecessarily expensive) web components
- supports GET, POST, PUT, DELETE, REQUEST, PATCH and HEAD methods - supports GET, POST, PUT, DELETE, REQUEST, PATCH and HEAD methods
- can be used with or without XSRF token - can be used with or without XSRF token
## How to use ## How to use
### Installation ### Installation
```sh ```sh
npm i --save @lion/ajax npm i --save @lion/ajax
``` ```
### Example ### Example
```js ```js
import { ajax } from '@lion/ajax'; import { ajax } from '@lion/ajax';
ajax.get('data.json') ajax
.then((response) => { .get('data.json')
.then(response => {
console.log(response); console.log(response);
}) })
.catch((error) => { .catch(error => {
console.log(error); console.log(error);
}); });
``` ```
@ -34,15 +37,17 @@ ajax.get('data.json')
### Create own instances for custom options ### Create own instances for custom options
#### Cancel #### Cancel
```js ```js
import { AjaxClass } from '@lion/ajax'; import { AjaxClass } from '@lion/ajax';
const myAjax = AjaxClass.getNewInstance({ cancelable: true }); const myAjax = AjaxClass.getNewInstance({ cancelable: true });
myAjax.get('data.json') myAjax
.then((response) => { .get('data.json')
.then(response => {
document.querySelector('#canceled').innerHTML = JSON.stringify(response.data); document.querySelector('#canceled').innerHTML = JSON.stringify(response.data);
}) })
.catch((error) => { .catch(error => {
document.querySelector('#canceled').innerHTML = `I got cancelled: ${error.message}`; document.querySelector('#canceled').innerHTML = `I got cancelled: ${error.message}`;
}); });
setTimeout(() => { setTimeout(() => {
@ -51,22 +56,25 @@ setTimeout(() => {
``` ```
#### Cancel previous on new request #### Cancel previous on new request
```js ```js
import { AjaxClass } from '@lion/ajax' import { AjaxClass } from '@lion/ajax';
const myAjax = AjaxClass.getNewInstance({ cancelPreviousOnNewRequest: true }); const myAjax = AjaxClass.getNewInstance({ cancelPreviousOnNewRequest: true });
myAjax.get('data.json') myAjax
.then((response) => { .get('data.json')
.then(response => {
document.querySelector('#request1').innerHTML = 'Request 1: ' + JSON.stringify(response.data); document.querySelector('#request1').innerHTML = 'Request 1: ' + JSON.stringify(response.data);
}) })
.catch((error) => { .catch(error => {
document.querySelector('#request1').innerHTML = `Request 1: I got cancelled: ${error.message}`; document.querySelector('#request1').innerHTML = `Request 1: I got cancelled: ${error.message}`;
}); });
myAjax.get('data2.json') myAjax
.then((response) => { .get('data2.json')
.then(response => {
document.querySelector('#request2').innerHTML = 'Request 2: ' + JSON.stringify(response.data); document.querySelector('#request2').innerHTML = 'Request 2: ' + JSON.stringify(response.data);
}) })
.catch((error) => { .catch(error => {
document.querySelector('#request2').innerHTML = `Request 2: I got cancelled: ${error.message}`; document.querySelector('#request2').innerHTML = `Request 2: I got cancelled: ${error.message}`;
}); });
``` ```

View file

@ -7,12 +7,14 @@
## Features ## Features
### Disabled ### Disabled
You can also set a button as disabled with the `disabled` property. You can also set a button as disabled with the `disabled` property.
## How to use ## How to use
### Installation ### Installation
```
```sh
npm i --save @lion/button npm i --save @lion/button
``` ```

View file

@ -7,12 +7,14 @@
You should use [lion-checkbox](../checkbox/)'s inside this element. You should use [lion-checkbox](../checkbox/)'s inside this element.
## Features ## Features
Since it extends from [lion-fieldset](../fieldset/), it has all the features a fieldset has. Since it extends from [lion-fieldset](../fieldset/), it has all the features a fieldset has.
## How to use ## How to use
### Installation ### Installation
```
```sh
npm i --save @lion/checkbox @lion/checkbox-group npm i --save @lion/checkbox @lion/checkbox-group
``` ```

View file

@ -5,6 +5,7 @@
`lion-checkbox` component is a sub-element to be used in [lion-checkbox-group](../checkbox-group/) elements. Its purpose is to provide a way for users to check **multiple** options amongst a set of choices, or to function as a single toggle. `lion-checkbox` component is a sub-element to be used in [lion-checkbox-group](../checkbox-group/) elements. Its purpose is to provide a way for users to check **multiple** options amongst a set of choices, or to function as a single toggle.
## Features ## Features
- Get or set the checked state (boolean) - `choiceChecked()` - Get or set the checked state (boolean) - `choiceChecked()`
- Get or set the value of the choice - `choiceValue()` - Get or set the value of the choice - `choiceValue()`
- Pre-select an option by setting the `checked` boolean attribute - Pre-select an option by setting the `checked` boolean attribute
@ -12,8 +13,9 @@
## How to use ## How to use
### Installation ### Installation
```
npm i --save @lion/checkbox; ```sh
npm i --save @lion/checkbox
``` ```
```js ```js

View file

@ -28,7 +28,7 @@ The more generic the mixin is, the higher the chance of being appliend more than
This is an example of how to make a conventional ES mixin deduping. This is an example of how to make a conventional ES mixin deduping.
```javascript ```js
const BaseMixin = dedupeMixin((superClass) => { const BaseMixin = dedupeMixin((superClass) => {
return class extends superClass { ... }; return class extends superClass { ... };
}); });

View file

@ -1,4 +1,3 @@
# Form Fundaments # Form Fundaments
[//]: # 'AUTO INSERT HEADER PREPUBLISH' [//]: # 'AUTO INSERT HEADER PREPUBLISH'
@ -34,7 +33,7 @@ On top of this, they feature:
Whenever a native form control doesn't exist or is not sufficient, a 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 [custom form field](./docs/CustomFieldsTutorial.md) should be created. One could think of components
like: like:
- slider - slider
- combobox - combobox
@ -56,9 +55,11 @@ Fieldsets are the basis for:
<!-- TODO: - [`FormControlMixin`] () --> <!-- TODO: - [`FormControlMixin`] () -->
<!-- TODO: - [`LionField`] () --> <!-- TODO: - [`LionField`] () -->
- [Model Value](./docs/ModelValue.md) - [Model Value](./docs/ModelValue.md)
- [Formatting and parsing](./docs/FormattingAndParsing.md) - [Formatting and parsing](./docs/FormattingAndParsing.md)
- [Interaction states](./docs/InteractionStates.md) - [Interaction states](./docs/InteractionStates.md)
- [Validation System](../validate/docs/ValidationSystem.md) - [Validation System](../validate/docs/ValidationSystem.md)
- [Custom Fields](./docs/CustomFieldsTutorial.md) - [Custom Fields](./docs/CustomFieldsTutorial.md)
<!-- TODO: - [`FocusMixin`] (/FocusMixin.md) --> <!-- TODO: - [`FocusMixin`] (/FocusMixin.md) -->

View file

@ -1,9 +1,11 @@
# Creating a custom field # Creating a custom field
Custom fields can be created in just a few steps. All you need is an interaction element 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) (like for instance a slider, a listbox or a combobox) and connect it to the [Field](../README.md)
functionality. functionality.
## Prerequisite: an interaction element ## Prerequisite: an interaction element
An interaction element provides the means for the end user to enter a certain value, just like 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>`). native elements provide in this (think of `<input>`, `<textarea>` and `<select>`).
An example of a non native element is the An example of a non native element is the
@ -15,24 +17,27 @@ For this tutorial, we assume we have a component `<my-slider>` that exposes its
it has a tabindex=“0” applied. it has a tabindex=“0” applied.
## Connecting the interaction element to the field ## Connecting the interaction element to the field
Now we want to integrate the slider in our form framework to enrich the user interface, get 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). 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`. We start of by creating a component `<lion-slider>` that extends from `LionField`.
Then we follow the steps below: Then we follow the steps below:
- ### 1. Add your interaction element as input slot' - ### 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. 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 - ### 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. 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` - ### 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.
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: Steps as described can be implemented with the following javascript:

View file

@ -52,9 +52,11 @@ Fieldsets are the basis for:
<!-- TODO: - [`FormControlMixin`] () --> <!-- TODO: - [`FormControlMixin`] () -->
<!-- TODO: - [`LionField`] () --> <!-- TODO: - [`LionField`] () -->
- [Model Value](./ModelValue.md) - [Model Value](./ModelValue.md)
- [Formatting and parsing](./FormattingAndParsing.md) - [Formatting and parsing](./FormattingAndParsing.md)
- [Interaction states](./InteractionStates.md) - [Interaction states](./InteractionStates.md)
- [Validation System](../../validate/docs/ValidationSystem.md) - [Validation System](../../validate/docs/ValidationSystem.md)
- [FieldCustomMixin](./FieldCustomMixin.md) - [FieldCustomMixin](./FieldCustomMixin.md)
<!-- TODO: - [`FocusMixin`] (/FocusMixin.md) --> <!-- TODO: - [`FocusMixin`] (/FocusMixin.md) -->

View file

@ -1,10 +1,12 @@
# FormatMixin # FormatMixin
The FormatMixin keeps track of the `modelValue`, `formattedValue` and `serializedValue`. The FormatMixin keeps track of the `modelValue`, `formattedValue` and `serializedValue`.
It is designed to work in conjunction with `LionField`. It is designed to work in conjunction with `LionField`.
## Concepts of different values ## Concepts of different values
### model value ### model value
The model value is the result of the parser function. It will be stored as `.modelValue` The model value is the result of the parser function. It will be stored as `.modelValue`
and should be considered the internal value used for validation and reasoning/logic. and should be considered the internal value used for validation and reasoning/logic.
The model value is 'ready for consumption' by the outside world (think of a Date object The model value is 'ready for consumption' by the outside world (think of a Date object
@ -12,10 +14,12 @@ or a float). It can(and is recommended to) be used as both input value and
output value of the `LionField`. output value of the `LionField`.
Examples: Examples:
- For a date input: a String '20/01/1999' will be converted to `new Date('1999/01/20')` - For a date input: a String '20/01/1999' will be converted to `new Date('1999/01/20')`
- For a number input: a formatted String '1.234,56' will be converted to a Number: `1234.56` - For a number input: a formatted String '1.234,56' will be converted to a Number: `1234.56`
### view value ### view value
The view value is the result of the formatter function. The view value is the result of the formatter function.
It will be stored as `.formattedValue` and synchronized to `.value` (a viewValue setter that It will be stored as `.formattedValue` and synchronized to `.value` (a viewValue setter that
allows to synchronize to `.inputElement`). allows to synchronize to `.inputElement`).
@ -23,25 +27,28 @@ Synchronization happens conditionally and is (by default) the result of a blur.
(like error state/validity and whether the a model value was set programatically) also play a role. (like error state/validity and whether the a model value was set programatically) also play a role.
Examples: Examples:
- For a date input, this would be '20/01/1999' (dependent on locale). - For a date input, this would be '20/01/1999' (dependent on locale).
- For a number input, this could be '1,234.56' (a String representation of modelValue - For a number input, this could be '1,234.56' (a String representation of modelValue
1234.56) 1234.56)
### serialized value ### serialized value
This is the serialized version of the model value. This is the serialized version of the model value.
It exists for maximal compatibility with the platform API. It exists for maximal compatibility with the platform API.
The serialized value can be an interface in context where data binding is not supported The serialized value can be an interface in context where data binding is not supported
and a serialized string needs to be set. and a serialized string needs to be set.
Examples: Examples:
- For a date input, this would be the iso format of a date, e.g. '1999-01-20'. - For a date input, this would be the iso format of a date, e.g. '1999-01-20'.
- For a number input this would be the String representation of a float ('1234.56' instead - For a number input this would be the String representation of a float ('1234.56' instead
of 1234.56) of 1234.56)
When no parser is available, the value is usually the same as the formattedValue
(being inputElement.value)
When no parser is available, the value is usually the same as the formattedValue (being inputElement.value)
## Formatters, parsers and (de)serializers ## Formatters, parsers and (de)serializers
In order to create advanced user experiences (automatically formatting a user input or an input In order to create advanced user experiences (automatically formatting a user input or an input
set imperatively by an Application Developer). set imperatively by an Application Developer).
@ -49,7 +56,9 @@ Below some concrete examples can be found of implementations of formatters and p
extrapolating the example of a date input. extrapolating the example of a date input.
### Formatters ### Formatters
A formatter should return a `formattedValue`: A formatter should return a `formattedValue`:
```js ```js
function formatDate(modelValue, options) { function formatDate(modelValue, options) {
if (!(modelValue instanceof Date)) { if (!(modelValue instanceof Date)) {
@ -58,27 +67,35 @@ function formatDate(modelValue, options) {
return formatDateLocalized(modelValue, options); return formatDateLocalized(modelValue, options);
} }
``` ```
Notice the options object, which holds a fallback value that shows what should be presented on Notice the options object, which holds a fallback value that shows what should be presented on
screen when the user input resulted in an invalid modelValue screen when the user input resulted in an invalid modelValue
### Parsers ### Parsers
A parser should return a `modelValue`: A parser should return a `modelValue`:
```js ```js
function parseDate(formattedValue, options) { function parseDate(formattedValue, options) {
return formattedValue === '' ? undefined : parseDateLocalized(formattedValue); return formattedValue === '' ? undefined : parseDateLocalized(formattedValue);
} }
``` ```
Notice that when it's not possible to create a valid modelValue based on the formattedValue, Notice that when it's not possible to create a valid modelValue based on the formattedValue,
one should return `undefined`. one should return `undefined`.
### Serializers and deserializers ### Serializers and deserializers
A serializer should return a `serializedValue`: A serializer should return a `serializedValue`:
```js ```js
function serializeDate(modelValue, options) { function serializeDate(modelValue, options) {
return modelValue.toISOString(); return modelValue.toISOString();
} }
``` ```
A deserializer should return a `modelValue`: A deserializer should return a `modelValue`:
```js ```js
function deserializeDate(serializeValue, options) { function deserializeDate(serializeValue, options) {
return new Date(serializeValue); return new Date(serializeValue);
@ -86,11 +103,13 @@ function deserializeDate(serializeValue, options) {
``` ```
### FieldCustomMixin ### FieldCustomMixin
When creating your own custom input, please use `FieldCustomMixin` as a basis for this. When creating your own custom input, please use `FieldCustomMixin` as a basis for this.
Concrete examples can be found at [`<lion-input-date>`](../../input-date) and Concrete examples can be found at [`<lion-input-date>`](../../input-date) and
[`<lion-input-amount>`](../../input-amount). [`<lion-input-amount>`](../../input-amount).
## Flow diagram ## Flow diagram
The following flow diagram is based on both end user input and interaction programmed by the The following flow diagram is based on both end user input and interaction programmed by the
developer. It shows how the 'computation loop' for modelValue, formattedValue and serializedValue developer. It shows how the 'computation loop' for modelValue, formattedValue and serializedValue
is triggered. is triggered.

View file

@ -27,7 +27,7 @@ Examples:
- For a date input, this would be '20/01/1999' (dependent on locale). - For a date input, this would be '20/01/1999' (dependent on locale).
- For a number input, this could be '1,234.56' (a String representation of modelValue - For a number input, this could be '1,234.56' (a String representation of modelValue
1234.56) 1234.56)
### serializedValue ### serializedValue
@ -41,8 +41,8 @@ Examples:
- For a date input, this would be the iso format of a date, e.g. '1999-01-20'. - For a date input, this would be the iso format of a date, e.g. '1999-01-20'.
- For a number input this would be the String representation of a float ('1234.56' instead - For a number input this would be the String representation of a float ('1234.56' instead
of 1234.56) of 1234.56)
When no parser is available, the value is usually the same as the formattedValue
(being inputElement.value) When no parser is available, the value is usually the same as the formattedValue (being inputElement.value)
## Formatters, parsers and (de)serializers ## Formatters, parsers and (de)serializers

View file

@ -40,14 +40,14 @@ static _isPrefilled(modelValue) {
We show the validity feedback when one of the following conditions is met: We show the validity feedback when one of the following conditions is met:
- prefilled: - prefilled:
The user already filled in something, or the value is prefilled The user already filled in something, or the value is prefilled
when the form is initially rendered. when the form is initially rendered.
- touched && dirty && !prefilled: - touched && dirty && !prefilled:
When a user starts typing for the first time in a field with for instance `required` validation, 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). 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` but not `touched`), When a user enters a field without altering the value (making it `dirty` but not `touched`),
an error message shouldn't be shown either. an error message shouldn't be shown either.
- submitted: - submitted:
If the form is submitted, always show the error message. If the form is submitted, always show the error message.

View file

@ -6,26 +6,30 @@ of the form, also for all derived states: interaction, validation, visibility an
computed from a modelValue change. computed from a modelValue change.
## Single source of truth ## Single source of truth
ModelValues are designed to provide the Application Developer a single way of programmatical ModelValues are designed to provide the Application Developer a single way of programmatical
interaction with the form for an Application Developer. interaction with the form for an Application Developer.
### One single concept for Application Developers ### One single concept for Application Developers
Application Developers need to only care about interacting with the modelValue on a form control Application Developers need to only care about interacting with the modelValue on a form control
level, via: level, via:
- `.modelValue` - `.modelValue`
- `@model-value-changed` - `@model-value-changed`
> Internal/private concepts like viewValue, formattedValue, serializedValue are therefore not > Internal/private concepts like viewValue, formattedValue, serializedValue are therefore not
recommended as a means of interaction. > recommended as a means of interaction.
### One single concept for internals ### One single concept for internals
Internally, all derived states are computed from model-value-changed events. Internally, all derived states are computed from model-value-changed events.
Since the modelValue is computed 'realtime' and reflects all user interaction, visibility and Since the modelValue is computed 'realtime' and reflects all user interaction, visibility and
validation states, we can guarantee a system that enables the best User Experience validation states, we can guarantee a system that enables the best User Experience
(see Interaction States). (see Interaction States).
## Unparseable modelValues ## Unparseable modelValues
A modelValue can demand a certain type (Date, Number, Iban etc.). A correct type will always be A modelValue can demand a certain type (Date, Number, Iban etc.). A correct type will always be
translatable into a String representation (the value presented to the end user) via the `formatter`. translatable into a String representation (the value presented to the end user) via the `formatter`.
When the type is not valid (usually as a consequence of a user typing in an invalid or incomplete When the type is not valid (usually as a consequence of a user typing in an invalid or incomplete
@ -37,13 +41,16 @@ popular frameworks like Angular and Vue.
The Unparseable type is an addition on top of this that mainly is added for the following two The Unparseable type is an addition on top of this that mainly is added for the following two
purposes: purposes:
- restoring user sessions - restoring user sessions
- realtime updated with all value changes - realtime updated with all value changes
### Restoring user sessions ### Restoring user sessions
As a modelValue is always a single source of truth As a modelValue is always a single source of truth
### Realtime updated with all value changes ### Realtime updated with all value changes
As an Application Developer, you will be notified when a user tries to write the correct type of As an Application Developer, you will be notified when a user tries to write the correct type of
a value. This might be handy for giving feedback to the user. a value. This might be handy for giving feedback to the user.

View file

@ -22,7 +22,7 @@ Two specific types of fieldsets:
### Installation ### Installation
```sh ```sh
npm i --save @lion/fieldset; npm i --save @lion/fieldset
``` ```
```js ```js

View file

@ -5,6 +5,7 @@
The Form System allows you to create complex forms with various validation in an easy way. The Form System allows you to create complex forms with various validation in an easy way.
## Features ## Features
- built in [validate](../validate) for error/warning/info/success - built in [validate](../validate) for error/warning/info/success
- formatting of values - formatting of values
- accessible - accessible

View file

@ -28,13 +28,19 @@ import '@lion/form/lion-form.js';
### Example ### Example
```html ```html
<lion-form><form> <lion-form>
<form>
<lion-fieldset name="fullName"> <lion-fieldset name="fullName">
<lion-input label="First Name" name="firstName" .modelValue=${model.firstName}></lion-input> <lion-input label="First Name" name="firstName" .modelValue="${model.firstName}"></lion-input>
<lion-input label="Last Name" name="lastName" .modelValue=${model.lastName}></lion-input> <lion-input label="Last Name" name="lastName" .modelValue="${model.lastName}"></lion-input>
</lion-fieldset> </lion-fieldset>
<lion-textarea label="Description" name="description" .modelValue=${model.description}></lion-textarea> <lion-textarea
</form></lion-form> label="Description"
name="description"
.modelValue="${model.description}"
></lion-textarea>
</form>
</lion-form>
``` ```
Note that the example above is rendered using [lit-html](https://github.com/Polymer/lit-html) Note that the example above is rendered using [lit-html](https://github.com/Polymer/lit-html)

View file

@ -28,8 +28,9 @@ Include the import for both the custom element the icons you want:
``` ```
Use it in your lit-html template: Use it in your lit-html template:
```html ```html
<lion-icon .svg=${bugSvg}></lion-icon> <lion-icon .svg="${bugSvg}"></lion-icon>
``` ```
### Icon format ### Icon format
@ -50,7 +51,7 @@ Make sure you have `focusable="false"` in the icon file to prevent bugs in IE/Ed
You may add an `aria-label` to provide information to visually impaired users: You may add an `aria-label` to provide information to visually impaired users:
```html ```html
<lion-icon .svg=${arrowLeftSvg} aria-label="Pointing left"></lion-icon> <lion-icon .svg="${arrowLeftSvg}" aria-label="Pointing left"></lion-icon>
``` ```
A `lion-icon` without an `aria-label` attribute will be automatically be given an `aria-hidden` attribute. A `lion-icon` without an `aria-label` attribute will be automatically be given an `aria-hidden` attribute.
@ -83,7 +84,7 @@ A `lion-icon` may be styled like a regular HTML element:
stroke: lightsteelblue; stroke: lightsteelblue;
} }
</style> </style>
<lion-icon .icon=${arrowSvg} class="strong"></lion-icon> <lion-icon .icon="${arrowSvg}" class="strong"></lion-icon>
``` ```
See [SVG and CSS](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_and_CSS) on MDN web docs for more information. See [SVG and CSS](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_and_CSS) on MDN web docs for more information.

View file

@ -5,6 +5,7 @@
`lion-input-amount` component is based on the generic text input field. Its purpose is to provide a way for users to fill in an amount. `lion-input-amount` component is based on the generic text input field. Its purpose is to provide a way for users to fill in an amount.
## Features ## Features
- based on [lion-input](../input) - based on [lion-input](../input)
- makes use of [formatNumber](../localize/docs/number.md) for formatting and parsing. - makes use of [formatNumber](../localize/docs/number.md) for formatting and parsing.
- option to show currency as a suffix - option to show currency as a suffix
@ -20,7 +21,8 @@
## How to use ## How to use
### Installation ### Installation
```
```sh
npm i --save @lion/input-amount npm i --save @lion/input-amount
``` ```

View file

@ -4,8 +4,8 @@
`lion-input-date` component is based on the generic text input field. Its purpose is to provide a way for users to fill in a date. `lion-input-date` component is based on the generic text input field. Its purpose is to provide a way for users to fill in a date.
## Features ## Features
- based on [lion-input](../input) - based on [lion-input](../input)
- makes use of [formatDate](../localize/docs/date.md) for formatting and parsing. - makes use of [formatDate](../localize/docs/date.md) for formatting and parsing.
- option to overwrite locale to change the formatting and parsing - option to overwrite locale to change the formatting and parsing
@ -19,7 +19,8 @@
## How to use ## How to use
### Installation ### Installation
```
```sh
npm i --save @lion/input-date npm i --save @lion/input-date
``` ```

View file

@ -4,6 +4,7 @@
For an input field with a big range, such as `birthday-input`, a datepicker is not the ultimate tool, so use the standard [lion-input-date](../input-date). For an input field with a big range, such as `birthday-input`, a datepicker is not the ultimate tool, so use the standard [lion-input-date](../input-date).
## Features ## Features
- input field with a datepicker to help to choose a date - input field with a datepicker to help to choose a date
- based on [lion-input-date](../input-date) - based on [lion-input-date](../input-date)
- makes use of [lion-calendar](../calendar) inside the datepicker - makes use of [lion-calendar](../calendar) inside the datepicker
@ -19,7 +20,8 @@ For an input field with a big range, such as `birthday-input`, a datepicker is n
## How to use ## How to use
### Installation ### Installation
```
```sh
npm i --save @lion/input-datepicker npm i --save @lion/input-datepicker
``` ```

View file

@ -4,8 +4,8 @@
`lion-input-email` component is based on the generic text input field. Its purpose is to provide a way for users to fill in an email. `lion-input-email` component is based on the generic text input field. Its purpose is to provide a way for users to fill in an email.
## Features ## Features
- based on [lion-input](../input) - based on [lion-input](../input)
- default label in different languages - default label in different languages
- makes use of email [validators](../validate/docs/DefaultValidators.md) with corresponding error messages in different languages - makes use of email [validators](../validate/docs/DefaultValidators.md) with corresponding error messages in different languages
@ -14,7 +14,8 @@
## How to use ## How to use
### Installation ### Installation
```
```sh
npm i --save @lion/input-email npm i --save @lion/input-email
``` ```
@ -25,8 +26,5 @@ import '@lion/input-email/lion-input-email.js';
### Example ### Example
```html ```html
<lion-input-email <lion-input-email name="email" .errorValidators="${[['required']]}"></lion-input-email>
name="email"
.errorValidators="${[['required']]}"
></lion-input-email>
``` ```

View file

@ -5,6 +5,7 @@
`lion-input-iban` component is based on the generic text input field. Its purpose is to provide a way for users to fill in an iban. `lion-input-iban` component is based on the generic text input field. Its purpose is to provide a way for users to fill in an iban.
## Features ## Features
- based on [lion-input](../input) - based on [lion-input](../input)
- default label in different languages - default label in different languages
- makes use of IBAN specific [validate](../validate) with corresponding error messages in different languages - makes use of IBAN specific [validate](../validate) with corresponding error messages in different languages
@ -14,7 +15,8 @@
## How to use ## How to use
### Installation ### Installation
```
```sh
npm i --save @lion/input-amount npm i --save @lion/input-amount
``` ```

View file

@ -5,6 +5,7 @@
`lion-input` component is a webcomponent that enhances the functionality of the native `<input>` element. `lion-input` component is a webcomponent that enhances the functionality of the native `<input>` element.
## Features ## Features
- based on [field](../field/) - based on [field](../field/)
- extra visual elements can be added via `slots` - extra visual elements can be added via `slots`
- **label**: can also be provided via the `label` attribute, but the slot can be used to change the `html` and `CSS` of the label. - **label**: can also be provided via the `label` attribute, but the slot can be used to change the `html` and `CSS` of the label.
@ -21,8 +22,9 @@
## How to use ## How to use
### Installation ### Installation
```
npm i --save @lion/input; ```sh
npm i --save @lion/input
``` ```
```js ```js
@ -44,6 +46,7 @@ import { maxLengthValidator } from '@lion/validate';
``` ```
Making use of slots: Making use of slots:
```html ```html
<lion-input name="amount"> <lion-input name="amount">
<label slot="label">Amount</label> <label slot="label">Amount</label>

View file

@ -12,7 +12,7 @@ The core of the system is a `LocalizeManager` instance which is responsible for
It is exposed as a `localize` singleton instance. It is exposed as a `localize` singleton instance.
This ensures that the data can be cached in the single place and reused across different components and same component instances. This ensures that the data can be cached in the single place and reused across different components and same component instances.
```javascript ```js
import { localize } from '@lion/localize'; import { localize } from '@lion/localize';
// localize is the instance of LocalizeManager // localize is the instance of LocalizeManager
``` ```
@ -23,6 +23,7 @@ Component developers will have a unified way to integrate with localization syst
## Usage for component developers ## Usage for component developers
As a component developer you get: As a component developer you get:
- unified data structure for different locales; - unified data structure for different locales;
- promisified helper to load data; - promisified helper to load data;
- notification about page locale changes; - notification about page locale changes;
@ -64,14 +65,14 @@ The approach with ES modules is great because it allows to simply reuse basic lo
- `/path/to/my-family-component/translations/en-GB.js` - `/path/to/my-family-component/translations/en-GB.js`
```js ```js
import en from './en.js' import en from './en.js';
export default en; export default en;
``` ```
- `/path/to/my-family-component/translations/en-US.js` - `/path/to/my-family-component/translations/en-US.js`
```js ```js
import en from './en.js' import en from './en.js';
export default { export default {
...en, ...en,
@ -81,7 +82,7 @@ The approach with ES modules is great because it allows to simply reuse basic lo
To load this data the method `loadNamespace()` which returns a promise can be used. To load this data the method `loadNamespace()` which returns a promise can be used.
```javascript ```js
localize.loadNamespace(namespace).then(() => { localize.loadNamespace(namespace).then(() => {
// do smth when data is loaded // do smth when data is loaded
}); });
@ -92,58 +93,60 @@ Let's look at both cases in depth.
1. Using explicit loader functions: 1. Using explicit loader functions:
```javascript ```js
// use the dynamic import to load static assets // use the dynamic import to load static assets
localize.loadNamespace({ localize.loadNamespace({
'my-hello-component': (locale) => { 'my-hello-component': locale => {
// resolves to a module with the module.default `{ greeting: 'Hallo {name}!' }` // resolves to a module with the module.default `{ greeting: 'Hallo {name}!' }`
return import(`./translations/${locale}.js`); return import(`./translations/${locale}.js`);
}, },
}); });
``` ```
Usage of dynamic imports is recommended if you want to be able to create smart bundles later on for a certain locale. Usage of dynamic imports is recommended if you want to be able to create smart bundles later on for a certain locale.
The module must have a `default` export as shown above to be handled properly. The module must have a `default` export as shown above to be handled properly.
But in fact you are not limited in the way how exactly the data is loaded. But in fact you are not limited in the way how exactly the data is loaded.
If you want to fetch it from some API this is also possible. If you want to fetch it from some API this is also possible.
```javascript ```js
// fetch from an API // fetch from an API
localize.loadNamespace({ localize.loadNamespace({
'my-hello-component': async (locale) => { 'my-hello-component': async locale => {
const response = await fetch(`http://api.example.com/?namespace=my-hello-component&locale=${locale}`); const response = await fetch(
`http://api.example.com/?namespace=my-hello-component&locale=${locale}`,
);
return response.json(); // resolves to the JSON object `{ greeting: 'Hallo {name}!' }` return response.json(); // resolves to the JSON object `{ greeting: 'Hallo {name}!' }`
}, },
}); });
``` ```
But it does not make much sense to have such a loader function for each of your namespaces. But it does not make much sense to have such a loader function for each of your namespaces.
And this is there the second option comes in handy. And this is there the second option comes in handy.
2. Using the loaders preconfigured via `setupNamespaceLoader()`: 1. Using the loaders preconfigured via `setupNamespaceLoader()`:
```javascript ```js
// using the regexp to match all component names staring with 'my-' // using the regexp to match all component names staring with 'my-'
localize.setupNamespaceLoader(/my-.+/, async (locale, namespace) => { localize.setupNamespaceLoader(/my-.+/, async (locale, namespace) => {
const response = await fetch(`http://api.example.com/?namespace=${namespace}&locale=${locale}`); const response = await fetch(`http://api.example.com/?namespace=${namespace}&locale=${locale}`);
return response.json(); return response.json();
}); });
Promise.all([ Promise.all([
localize.loadNamespace('my-hello-component'); localize.loadNamespace('my-hello-component');
localize.loadNamespace('my-goodbuy-component'); localize.loadNamespace('my-goodbuy-component');
]) ])
``` ```
Thus there is a loder function for all components having a certain prefix in a name. Thus there is a loder function for all components having a certain prefix in a name.
The locale which will be loaded by default is accesed via the `localize.locale`. The locale which will be loaded by default is accesed via the `localize.locale`.
The single source of truth for page's locale is `<html lang="my-LOCALE">`. The single source of truth for page's locale is `<html lang="my-LOCALE">`.
At the same time the interaction should happen via `localize.locale` getter/setter to be able to notify and react to the change. At the same time the interaction should happen via `localize.locale` getter/setter to be able to notify and react to the change.
```javascript ```js
localize.addEventListener('localeChanged', () => { localize.addEventListener('localeChanged', () => {
// do smth when data is loaded for a new locale // do smth when data is loaded for a new locale
}); });
@ -160,7 +163,7 @@ When all necessary data is loaded and you want to show localized content on the
`localize.msg` comes into play here. `localize.msg` comes into play here.
It expects a key in the format of `namespace:name` and can also receive variables as a second argument. It expects a key in the format of `namespace:name` and can also receive variables as a second argument.
```javascript ```js
_onNameChanged() { _onNameChanged() {
// inserts 'Hello John!' into the element with id="name" // inserts 'Hello John!' into the element with id="name"
const name = localize.msg('my-hello-component:greeting', { name: 'John' }); const name = localize.msg('my-hello-component:greeting', { name: 'John' });
@ -174,12 +177,13 @@ _onNameChanged() {
This mixin was created to significantly simplify integration with LionLitElement. This mixin was created to significantly simplify integration with LionLitElement.
It provides several capabilities: It provides several capabilities:
- automatic loading of specified namespaces; - automatic loading of specified namespaces;
- life-cycle callbacks for localization events; - life-cycle callbacks for localization events;
- alias `_m` for `localize.msg`; - alias `_m` for `localize.msg`;
- promisified alias `_msgAsync` for `localize.msg` resolved when data is loaded. - promisified alias `_msgAsync` for `localize.msg` resolved when data is loaded.
```javascript ```js
class MyHelloComponent extends LocalizeMixin(LionLitElement) { class MyHelloComponent extends LocalizeMixin(LionLitElement) {
static get localizeNamespaces() { static get localizeNamespaces() {
// using an explicit loader function // using an explicit loader function
@ -220,12 +224,13 @@ Refer to demos to see a full example.
This is an extension of LocalizeMixin for usage with LionLitElement and LitRenderMixin. This is an extension of LocalizeMixin for usage with LionLitElement and LitRenderMixin.
It provides extra capabilities on top of LocalizeMixin: It provides extra capabilities on top of LocalizeMixin:
- smart wrapper `msg` for `localize.msg`; - smart wrapper `msg` for `localize.msg`;
- automatic update of DOM after locale was changed. - automatic update of DOM after locale was changed.
With the help of this mixin writing a component can be as easy as defining namespaces in `localizeNamespaces` and writing lit-html template using `this.msgLit()`: With the help of this mixin writing a component can be as easy as defining namespaces in `localizeNamespaces` and writing lit-html template using `this.msgLit()`:
```javascript ```js
render() { render() {
return html` return html`
<div>${this.name ? this.msgLit('my-hello-component:greeting', { name: this.name }) : ''}</div> <div>${this.name ? this.msgLit('my-hello-component:greeting', { name: this.name }) : ''}</div>
@ -238,6 +243,7 @@ Refer to demos to see a full example.
## Usage for application developers ## Usage for application developers
As an application developer you get: As an application developer you get:
- ability to inline localization data for any locales and namespaces to prevent async loading and improve rendering speed in critical cases; - ability to inline localization data for any locales and namespaces to prevent async loading and improve rendering speed in critical cases;
- smart defaults for data loading; - smart defaults for data loading;
- simple customization of paths where the data is loaded from for common use cases; - simple customization of paths where the data is loaded from for common use cases;
@ -247,11 +253,15 @@ As an application developer you get:
If you want to optimize the page rendering and you can inline some of your localization data upfront then there is a simple way to do it: If you want to optimize the page rendering and you can inline some of your localization data upfront then there is a simple way to do it:
```javascript ```js
// my-inlined-data.js // my-inlined-data.js
import { localize } from 'lion-localize/localize.js'; import { localize } from 'lion-localize/localize.js';
localize.addData('en-GB', 'my-namespace', {/* data */}); localize.addData('en-GB', 'my-namespace', {
localize.addData('nl-NL', 'my-namespace', {/* data */}); /* data */
});
localize.addData('nl-NL', 'my-namespace', {
/* data */
});
// my-app.js // my-app.js
import './my-inlined-data.js'; // must be on top to be executed before any other code using the data import './my-inlined-data.js'; // must be on top to be executed before any other code using the data
@ -268,10 +278,12 @@ But as we have already covered in the documentation for component developers the
The configuration is done via `setupNamespaceLoader()`. The configuration is done via `setupNamespaceLoader()`.
This is sort of a router for the data and is typically needed to fetch it from an API. This is sort of a router for the data and is typically needed to fetch it from an API.
```javascript ```js
// for one specific component // for one specific component
localize.setupNamespaceLoader('my-hello-component', async (locale) => { localize.setupNamespaceLoader('my-hello-component', async locale => {
const response = await fetch(`http://api.example.com/?namespace=my-hello-component&locale=${locale}`); const response = await fetch(
`http://api.example.com/?namespace=my-hello-component&locale=${locale}`,
);
return response.json(); return response.json();
}); });

View file

@ -3,14 +3,16 @@
The amount formatter returns a number based on the locale by using [Intl NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) specification. The amount formatter returns a number based on the locale by using [Intl NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) specification.
## Features ## Features
- **formatAmountHtml**: returns a formatted amount based on locale to be used in lit-html - **formatAmountHtml**: returns a formatted amount based on locale to be used in lit-html
- **formatAmountHtmlString**: returns a formatted amount based on locale as a string - **formatAmountHtmlString**: returns a formatted amount based on locale as a string
## How to use ## How to use
### Installation ### Installation
```
npm i --save @lion/localize; ```sh
npm i --save @lion/localize
``` ```
### Example ### Example

View file

@ -3,6 +3,7 @@
The date formatter returns a date based on the locale by using [Intl DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat) specification. The date formatter returns a date based on the locale by using [Intl DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat) specification.
## Features ## Features
- **formatDate**: returns a formatted date based on locale - **formatDate**: returns a formatted date based on locale
- **parseDate**: returns a date Object - **parseDate**: returns a date Object
- **getDateFormatBasedOnLocale**: returns the date format based on locale - **getDateFormatBasedOnLocale**: returns the date format based on locale
@ -10,8 +11,9 @@ The date formatter returns a date based on the locale by using [Intl DateTimeFor
## How to use ## How to use
### Installation ### Installation
```
npm i --save @lion/localize; ```sh
npm i --save @lion/localize
``` ```
### Example ### Example
@ -19,12 +21,15 @@ npm i --save @lion/localize;
```js ```js
import { parseDate, formatDate } from '@lion/localize'; import { parseDate, formatDate } from '@lion/localize';
function dateExampleFunction () { function dateExampleFunction() {
const parsedDate = parseDate('21-05-2012'); const parsedDate = parseDate('21-05-2012');
const options = { const options = {
weekday: 'long', year: 'numeric', month: 'long', day: '2-digit', weekday: 'long',
year: 'numeric',
month: 'long',
day: '2-digit',
}; };
return formatDate(parsedDate, options) // 'Monday, 21 May 2012' for British locale return formatDate(parsedDate, options); // 'Monday, 21 May 2012' for British locale
} }
``` ```

View file

@ -9,16 +9,17 @@ The localization system helps to manage localization data, split into locales an
The LocalizeMixin is the one you want to use in your web components. By adding this mixing, you can internationalize your component. The LocalizeMixin is the one you want to use in your web components. By adding this mixing, you can internationalize your component.
`LocalizeMixin` has the following features: `LocalizeMixin` has the following features:
- Use translated text in your template (`msgLit()`) - Use translated text in your template (`msgLit()`)
- Get the localize namespaces of the component (`localizeNamespaces`) - Get the localize namespaces of the component (`localizeNamespaces`)
- Await loading of localize namespaces (`localizeNamespacesLoaded`) - Await loading of localize namespaces (`localizeNamespacesLoaded`)
Advanced: Advanced:
- Set whether your component should wait for localize namespaces before rendering your component's template (`waitForLocalizeNamespaces`) - Set whether your component should wait for localize namespaces before rendering your component's template (`waitForLocalizeNamespaces`)
- Lit-element's `performUpdate()` is overridden in this mixin to perform updates async or delayed sync based on the `waitForLocalizeNamespaces` getter - Lit-element's `performUpdate()` is overridden in this mixin to perform updates async or delayed sync based on the `waitForLocalizeNamespaces` getter
- Lifecycle methods `onLocaleReady()`, `onLocaleChanged()`, `onLocaleUpdated()` - Lifecycle methods `onLocaleReady()`, `onLocaleChanged()`, `onLocaleUpdated()`
## How to use ## How to use
### In a webcomponent ### In a webcomponent
@ -52,6 +53,7 @@ The `namespace` can be one of two types: an object with an explicit loader funct
> When calling `this.msgLit()`, what comes after `:` may contain dots only if they are intended as a separator for objects. For more details, please check [messageformat](https://messageformat.github.io/messageformat/), which is the underlying library that we use. > When calling `this.msgLit()`, what comes after `:` may contain dots only if they are intended as a separator for objects. For more details, please check [messageformat](https://messageformat.github.io/messageformat/), which is the underlying library that we use.
An example of the a preconfigured loader: An example of the a preconfigured loader:
```js ```js
static get localizeNamespaces() { static get localizeNamespaces() {
return ['my-hello-component', ...super.localizeNamespaces]; return ['my-hello-component', ...super.localizeNamespaces];
@ -85,6 +87,7 @@ class MyHelloComponent extends LocalizeMixin(LionLitElement) {
``` ```
It is also possible to pass data to your translation: It is also possible to pass data to your translation:
```js ```js
render() { render() {
return html` return html`
@ -98,6 +101,7 @@ render() {
Usage of dynamic imports is recommended if you want to be able to create smart bundles later on for a certain locale. Usage of dynamic imports is recommended if you want to be able to create smart bundles later on for a certain locale.
### Not using a webcomponent ### Not using a webcomponent
For example, if you simply want to make a reusable template, you can also use localization using the singleton instance of LocalizeManager called `localize`. For example, if you simply want to make a reusable template, you can also use localization using the singleton instance of LocalizeManager called `localize`.
```js ```js
@ -112,6 +116,7 @@ export function myTemplate(someData) {
`; `;
} }
``` ```
This template is meant for importing in your webcomponent which uses this localize namespace. This template is meant for importing in your webcomponent which uses this localize namespace.
### Translation files ### Translation files
@ -121,7 +126,6 @@ Typically the locale is an ES module which is by convention put into the `/trans
Localization data modules for `my-hello-component` might look like these: Localization data modules for `my-hello-component` might look like these:
- `/path/to/my-family-component/translations/en.js` - `/path/to/my-family-component/translations/en.js`
```js ```js
@ -134,14 +138,14 @@ Localization data modules for `my-hello-component` might look like these:
- `/path/to/my-family-component/translations/en-GB.js` - `/path/to/my-family-component/translations/en-GB.js`
```js ```js
import en from './en.js' import en from './en.js';
export default en; export default en;
``` ```
- `/path/to/my-family-component/translations/en-US.js` - `/path/to/my-family-component/translations/en-US.js`
```js ```js
import en from './en.js' import en from './en.js';
export default { export default {
...en, ...en,
@ -158,8 +162,10 @@ If you want to fetch translation data from some API this is also possible.
```js ```js
// fetch from an API // fetch from an API
localize.loadNamespace({ localize.loadNamespace({
'my-hello-component': async (locale) => { 'my-hello-component': async locale => {
const response = await fetch(`http://api.example.com/?namespace=my-hello-component&locale=${locale}`); const response = await fetch(
`http://api.example.com/?namespace=my-hello-component&locale=${locale}`,
);
return response.json(); // resolves to the JSON object `{ greeting: 'Hallo {name}!' }` return response.json(); // resolves to the JSON object `{ greeting: 'Hallo {name}!' }`
}, },
}); });

View file

@ -3,6 +3,7 @@
The number formatter returns a number based on the locale by using [Intl NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) specification. The number formatter returns a number based on the locale by using [Intl NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) specification.
## Features ## Features
- **formatNumber**: returns a formatted number based on locale - **formatNumber**: returns a formatted number based on locale
- **formatNumberToParts**: returns a formatted number in parts based on locale - **formatNumberToParts**: returns a formatted number in parts based on locale
- **getFractionDigits**: returns the fraction digit for a certain currency - **getFractionDigits**: returns the fraction digit for a certain currency
@ -12,8 +13,9 @@ The number formatter returns a number based on the locale by using [Intl NumberF
## How to use ## How to use
### Installation ### Installation
```
npm i --save @lion/localize; ```sh
npm i --save @lion/localize
``` ```
### Example ### Example
@ -21,9 +23,9 @@ npm i --save @lion/localize;
```js ```js
import { formatNumber } from '@lion/localize'; import { formatNumber } from '@lion/localize';
function numberExampleFunction () { function numberExampleFunction() {
const number = 2000; const number = 2000;
const options = { style: 'currency', currency: 'EUR', currencyDisplay: 'code' }; const options = { style: 'currency', currency: 'EUR', currencyDisplay: 'code' };
return formatNumber(number, options) // 'EUR 2,000.00' for British locale return formatNumber(number, options); // 'EUR 2,000.00' for British locale
} }
``` ```

View file

@ -6,6 +6,7 @@ Supports different types of overlays like dialogs, toasts, tooltips, dropdown, e
Manages their position on the screen relative to other elements, including other overlays. Manages their position on the screen relative to other elements, including other overlays.
## Features ## Features
- [**Overlays Manager**](./docs/OverlaysManager.md), a global repository keeping track of all different types of overlays. - [**Overlays Manager**](./docs/OverlaysManager.md), a global repository keeping track of all different types of overlays.
- [**Overlays Occurrences**](./docs/OverlayOccurrences.md), outline of all possible occurrences of overlays. Divided into two main types: - [**Overlays Occurrences**](./docs/OverlayOccurrences.md), outline of all possible occurrences of overlays. Divided into two main types:
- [**Global Overlay Controller**](./docs/GlobalOverlayController.md), controller for overlays relative to the viewport. - [**Global Overlay Controller**](./docs/GlobalOverlayController.md), controller for overlays relative to the viewport.
@ -14,18 +15,20 @@ Manages their position on the screen relative to other elements, including other
## How to use ## How to use
### Installation ### Installation
```sh ```sh
npm i --save @lion/overlays npm i --save @lion/overlays
``` ```
### Example ### Example
```js ```js
import { overlays } from '@lion/overlays'; import { overlays } from '@lion/overlays';
const myCtrl = overlays.add( const myCtrl = overlays.add(
new OverlayTypeController({ new OverlayTypeController({
/* options */ /* options */
}) }),
); );
// name OverlayTypeController is for illustration purpose only // name OverlayTypeController is for illustration purpose only
// please read below about existing classes for different types of overlays // please read below about existing classes for different types of overlays

View file

@ -8,22 +8,25 @@ All supported types of global overlays are described below.
## How to use ## How to use
### Installation ### Installation
```sh ```sh
npm i --save @lion/overlays npm i --save @lion/overlays
``` ```
### Example ### Example
```js ```js
import { overlays } from '@lion/overlays'; import { overlays } from '@lion/overlays';
const myCtrl = overlays.add( const myCtrl = overlays.add(
new GlobalOverlayController({ new GlobalOverlayController({
/* options */ /* options */
}) }),
); );
``` ```
### ModalDialogController ### ModalDialogController
A specific extension of GlobalOverlayController configured to create accessible modal dialogs. A specific extension of GlobalOverlayController configured to create accessible modal dialogs.
```js ```js

View file

@ -8,21 +8,22 @@ All supported types of local overlays are described below.
## How to use ## How to use
### Installation ### Installation
```sh ```sh
npm i --save @lion/overlays npm i --save @lion/overlays
``` ```
### Example ### Example
```js ```js
import { overlays } from '@lion/overlays'; import { overlays } from '@lion/overlays';
const myCtrl = overlays.add( const myCtrl = overlays.add(
new LocalOverlayController({ new LocalOverlayController({
/* options */ /* options */
}) }),
); );
``` ```
This is currently WIP. This is currently WIP.
Stay tuned for updates on new types of overlays. Stay tuned for updates on new types of overlays.

View file

@ -1,19 +1,22 @@
# LocalOverlayPositioning # LocalOverlayPositioning
## Featuring - [Popper.js](https://popper.js.org/) ## Featuring - [Popper.js](https://popper.js.org/)
Our local overlays use the open-source Popper.js library for positioning the content relative to the reference element, which we usually refer to as the invoker, in the context of local overlays. Our local overlays use the open-source Popper.js library for positioning the content relative to the reference element, which we usually refer to as the invoker, in the context of local overlays.
## Features ## Features
- Everything Popper.js! - Everything Popper.js!
- Currently eagerly loads popper in the constructor of LocalOverlayController. Loading during idle time / using prefetch would be better, this is still WIP. - Currently eagerly loads popper in the constructor of LocalOverlayController. Loading during idle time / using prefetch would be better, this is still WIP.
> Popper strictly is scoped on positioning. **It does not change the dimensions of the popper element nor the reference element**. This also means that if you use the arrow feature, you are in charge of styling it properly, use the x-placement attribute for this. > Popper strictly is scoped on positioning. **It does not change the dimensions of the popper element nor the reference element**. This also means that if you use the arrow feature, you are in charge of styling it properly, use the x-placement attribute for this.
## How to use ## How to use
For installation, see [LocalOverlayController](./LocalOverlayController.md)'s `How to use` section. For installation, see [LocalOverlayController](./LocalOverlayController.md)'s `How to use` section.
The API for LocalOverlay without Popper looks like this (`overlays` being the OverlayManager singleton): The API for LocalOverlay without Popper looks like this (`overlays` being the OverlayManager singleton):
```js ```js
const localOverlay = overlays.add( const localOverlay = overlays.add(
new LocalOverlayController({ new LocalOverlayController({
@ -28,7 +31,9 @@ const localOverlay = overlays.add(
}); });
); );
``` ```
This will use the defaults we set for Popper configuration. To override the default options, you add a `popperConfig` object to the properties of the object you pass to `the LocalOverlayController` like so: This will use the defaults we set for Popper configuration. To override the default options, you add a `popperConfig` object to the properties of the object you pass to `the LocalOverlayController` like so:
```js ```js
const localOverlay = overlays.add( const localOverlay = overlays.add(
new LocalOverlayController({ new LocalOverlayController({
@ -73,8 +78,10 @@ const localOverlay = overlays.add(
}); });
); );
``` ```
The popperConfig is 1 to 1 aligned with Popper.js' API. For more detailed information and more advanced options, visit the [Popper.js documentation](https://popper.js.org/popper-documentation.html) to learn about the usage. The popperConfig is 1 to 1 aligned with Popper.js' API. For more detailed information and more advanced options, visit the [Popper.js documentation](https://popper.js.org/popper-documentation.html) to learn about the usage.
## Future additions ## Future additions
- Coming soon: Webcomponent implementation of LocalOverlay with a default arrow, styled out of the box to at least have proper rotations and positions. - Coming soon: Webcomponent implementation of LocalOverlay with a default arrow, styled out of the box to at least have proper rotations and positions.
- Default overflow and/or max-width behavior when content is too wide or high for the viewport. - Default overflow and/or max-width behavior when content is too wide or high for the viewport.

View file

@ -1,29 +1,33 @@
# Overlay System: Implementation # Overlay System: Implementation
This document provides an outline of all possible occurrences of overlays found in applications in This document provides an outline of all possible occurrences of overlays found in applications in
general and thus provided by Lion. general and thus provided by Lion.
For all concepts referred to in this document, please read [Overlay System Scope](./OverlaySystemScope.md). For all concepts referred to in this document, please read [Overlay System Scope](./OverlaySystemScope.md).
## Local and global overlay controllers ## Local and global overlay controllers
Currently, we have a global and a local overlay controller, as two separate entities. Currently, we have a global and a local overlay controller, as two separate entities.
Based on provided config, they handle all positioning logic, accessibility and interaction patterns. Based on provided config, they handle all positioning logic, accessibility and interaction patterns.
All of their configuration options will be described below as part of the _Responsive overlay_ section. All of their configuration options will be described below as part of the _Responsive overlay_ section.
### Connection points and placement contexts ### Connection points and placement contexts
It's currently not clear where the border between global and local overlays lie. They seem to be It's currently not clear where the border between global and local overlays lie. They seem to be
separated based on their 'dom connection point' (body vs 'page cursor'(usually invoker sibling)). separated based on their 'dom connection point' (body vs 'page cursor'(usually invoker sibling)).
However, there is no required relationship here: we can create a modal dialog from However, there is no required relationship here: we can create a modal dialog from
local context('page cursor') as well. local context('page cursor') as well.
Only, we would have a few concerns when creating global overlays from a local connection point: Only, we would have a few concerns when creating global overlays from a local connection point:
- Accessibility will be harder to implement. When wai-aria 1.0 needs to be supported, all siblings - Accessibility will be harder to implement. When wai-aria 1.0 needs to be supported, all siblings
need to have aria-hidden="true" and all parents role="presentation". Not always straightforward need to have aria-hidden="true" and all parents role="presentation". Not always straightforward
in shadow dom. If we only need to support wai-aria 1.1, we could use aria-modal="true" on the in shadow dom. If we only need to support wai-aria 1.1, we could use aria-modal="true" on the
element with role="dialog". (we basically need to test our supported browsers and screen readers element with role="dialog". (we basically need to test our supported browsers and screen readers
for compatibility with aria-modal). for compatibility with aria-modal).
- Stacking context need to be managed: the whole 'z-index chain' should win (it's a battle between - Stacking context need to be managed: the whole 'z-index chain' should win (it's a battle between
parents in the hierarchy). This would require some complex code to cover all edge cases. parents in the hierarchy). This would require some complex code to cover all edge cases.
- Side effects of parents adding transforms or clipping become a risk. This is hard to detect and - Side effects of parents adding transforms or clipping become a risk. This is hard to detect and
'counter'. 'counter'.
When the dom connection point is 'body', content projection will not work, but a template that When the dom connection point is 'body', content projection will not work, but a template that
can be rendered without being dependent on its context will be required. can be rendered without being dependent on its context will be required.
@ -41,7 +45,8 @@ focused cell in table mode))
For maximum flexibility, it should be up to the developer to decide how overlays should be rendered, For maximum flexibility, it should be up to the developer to decide how overlays should be rendered,
per instance of an overlay. per instance of an overlay.
## Responsive overlay ### Responsive overlay
Based on screen size, we might want to switch the appearance of an overlay. Based on screen size, we might want to switch the appearance of an overlay.
For instance: an application menu can be displayed as a dropdown on desktop, For instance: an application menu can be displayed as a dropdown on desktop,
but as a bottom sheet on mobile. but as a bottom sheet on mobile.
@ -61,7 +66,8 @@ Some options are mutually exclusive, in which case their dependent options and r
mentioned. mentioned.
Note: a more generic and precise term for all mentionings of `invoker` below would actually be Note: a more generic and precise term for all mentionings of `invoker` below would actually be
`relative positioning element`. `relative positioning element`.
```
```text
- {Element} elementToFocusAfterHide - the element that should be called `.focus()` on after dialog closes - {Element} elementToFocusAfterHide - the element that should be called `.focus()` on after dialog closes
- {Boolean} hasBackdrop - whether it should have a backdrop (currently exclusive to globalOverlayController) - {Boolean} hasBackdrop - whether it should have a backdrop (currently exclusive to globalOverlayController)
- {Boolean} isBlocking - hides other overlays when mutiple are opened (currently exclusive to globalOverlayController) - {Boolean} isBlocking - hides other overlays when mutiple are opened (currently exclusive to globalOverlayController)
@ -77,7 +83,8 @@ Note: a more generic and precise term for all mentionings of `invoker` below wou
``` ```
These options are suggested to be added to the current ones: These options are suggested to be added to the current ones:
```
```text
- {Boolean} isModal - sets aria-modal and/or aria-hidden="true" on siblings - {Boolean} isModal - sets aria-modal and/or aria-hidden="true" on siblings
- {Boolean} isGlobal - determines the connection point in DOM (body vs handled by user) TODO: rename to renderToBody? - {Boolean} isGlobal - determines the connection point in DOM (body vs handled by user) TODO: rename to renderToBody?
- {Boolean} isTooltip - has a totally different interaction - and accessibility pattern from all - {Boolean} isTooltip - has a totally different interaction - and accessibility pattern from all
@ -99,7 +106,8 @@ other overlays, so needed for internals.
``` ```
What we should think about more properly is a global placement option (positioned relative to window instead of invoker) What we should think about more properly is a global placement option (positioned relative to window instead of invoker)
```
```text
// TODO: namings very much under construction (we should also reconsider 'placement' names, see: https://github.com/ing-bank/lion/pull/61) // TODO: namings very much under construction (we should also reconsider 'placement' names, see: https://github.com/ing-bank/lion/pull/61)
// Something like the drawing Joren made: https://github.com/ing-bank/lion/issues/36#issuecomment-491855381 // Something like the drawing Joren made: https://github.com/ing-bank/lion/issues/36#issuecomment-491855381
- {String} viewportPlacement - consists of a vertical alignment part and an horizontal alignment part, - {String} viewportPlacement - consists of a vertical alignment part and an horizontal alignment part,
@ -110,12 +118,14 @@ What we should think about more properly is a global placement option (positione
``` ```
## Controllers/behaviors ## Controllers/behaviors
Controllers/behaviors provide preconfigured configuration objects for the global/local Controllers/behaviors provide preconfigured configuration objects for the global/local
overlay controllers. overlay controllers.
They provide an imperative and very flexible api for creating overlays and should be used by They provide an imperative and very flexible api for creating overlays and should be used by
Subclassers, inside webcomponents. Subclassers, inside webcomponents.
#### Dialog ### Dialog Controller
```js ```js
{ {
isGlobal: true, isGlobal: true,
@ -130,7 +140,8 @@ Subclassers, inside webcomponents.
} }
``` ```
#### Tooltip ### Tooltip Controller
```js ```js
{ {
isTooltip: true, isTooltip: true,
@ -139,16 +150,20 @@ Subclassers, inside webcomponents.
} }
``` ```
#### Popover ### Popover Controller
```js ```js
{ {
handlesUserInteraction: true, handlesUserInteraction: true,
handlesAccessibility: true, handlesAccessibility: true,
} }
``` ```
#### Dropdown
### Dropdown Controller
It will be quite common to override placement to 'bottom-fullwidth'. It will be quite common to override placement to 'bottom-fullwidth'.
Also, it would be quite common to add a pointerNode. Also, it would be quite common to add a pointerNode.
```js ```js
{ {
placement: 'bottom', placement: 'bottom',
@ -156,63 +171,79 @@ Also, it would be quite common to add a pointerNode.
handlesAccessibility: true, handlesAccessibility: true,
} }
``` ```
#### Toast
### Toast Controller
TODO: TODO:
- add an option for role="alertdialog" ? - add an option for role="alertdialog" ?
- add an option for a 'hide timer' and belonging a11y features for this - add an option for a 'hide timer' and belonging a11y features for this
```js ```js
{ {
...Dialog, ...Dialog,
viewportPlacement: 'top-right', (?) viewportPlacement: 'top-right', (?)
} }
``` ```
#### Sheet (bottom, top, left, right)
### Sheet Controller (bottom, top, left, right)
```js ```js
{ {
...Dialog, ...Dialog,
viewportPlacement: '{top|bottom|left|right}-fullwidth', (?) viewportPlacement: '{top|bottom|left|right}-fullwidth', (?)
} }
``` ```
#### Select
### Select Controller
No need for a config, will probably invoke ResponsiveOverlayCtrl and switches No need for a config, will probably invoke ResponsiveOverlayCtrl and switches
config based on media query from Dropdown to BottomSheet/CenteredDialog config based on media query from Dropdown to BottomSheet/CenteredDialog
#### Combobox/autocomplete ### Combobox/autocomplete Controller
No need for a config, will probably invoke ResponsiveOverlayCtrl and switches No need for a config, will probably invoke ResponsiveOverlayCtrl and switches
config based on media query from Dropdown to BottomSheet/CenteredDialog config based on media query from Dropdown to BottomSheet/CenteredDialog
#### Application menu ### Application menu Controller
No need for cfg, will probably invoke ResponsiveOverlayCtrl and switches No need for cfg, will probably invoke ResponsiveOverlayCtrl and switches
config based on media query from Dropdown to BottomSheet/CenteredDialog config based on media query from Dropdown to BottomSheet/CenteredDialog
## Web components ## Web components
Web components provide a declaritive, developer friendly interface with a prewconfigured styling Web components provide a declaritive, developer friendly interface with a prewconfigured styling
that fits the Design System and makes it really easy for Application Developers to build that fits the Design System and makes it really easy for Application Developers to build
user interfaces. user interfaces.
Web components should use Web components should use
The ground layers for the webcomponents in Lion are the following: The ground layers for the webcomponents in Lion are the following:
#### Dialog ### Dialog Component
Imperative might be better here? We can add a web component later if needed. Imperative might be better here? We can add a web component later if needed.
#### Tooltip ### Tooltip Component
```html ```html
<lion-tooltip> <lion-tooltip>
<button slot="invoker">hover/focus</button> <button slot="invoker">hover/focus</button>
<div slot="content">This will be shown</div> <div slot="content">This will be shown</div>
</lion-tooltip> </lion-tooltip>
``` ```
#### Popover
### Popover Component
```html ```html
<lion-popover> <lion-popover>
<button slot="invoker">click/space/enter</button> <button slot="invoker">click/space/enter</button>
<div slot="content">This will be shown</div> <div slot="content">This will be shown</div>
</lion-popover> </lion-popover>
``` ```
#### Dropdown
### Dropdown Component
Like the name suggests, the default placement will be button Like the name suggests, the default placement will be button
```html ```html
<lion-dropdown> <lion-dropdown>
<button slot="invoker">click/space/enter</button> <button slot="invoker">click/space/enter</button>
@ -223,17 +254,18 @@ Like the name suggests, the default placement will be button
</ul> </ul>
</lion-dropdown> </lion-dropdown>
``` ```
#### Toast
Imperative might be better here? ### Toast Component
#### Sheet (bottom, top, left, right)
Imperative might be better here? Imperative might be better here?
### Sheet Component (bottom, top, left, right)
### Web components implementing generic overlays Imperative might be better here?
#### Select, Combobox/autocomplete, Application menu ## Web components implementing generic overlays
### Select, Combobox/autocomplete, Application menu
Those will be separate web components with a lot of form and a11y logic that will be described Those will be separate web components with a lot of form and a11y logic that will be described
in detail in different sections. in detail in different sections.

View file

@ -10,11 +10,13 @@ document flow.
An overlay manager is a global repository keeping track of all different types of overlays. An overlay manager is a global repository keeping track of all different types of overlays.
The need for a global housekeeping mainly arises when multiple overlays are opened simultaneously. The need for a global housekeeping mainly arises when multiple overlays are opened simultaneously.
As opposed to a single overlay, the overlay manager stores knowledge about: As opposed to a single overlay, the overlay manager stores knowledge about:
- whether the scroll behaviour of the body element can be manipulated - whether the scroll behaviour of the body element can be manipulated
- what space is available in the window for drawing new overlays - what space is available in the window for drawing new overlays
The manager is in charge of rendering an overlay to the DOM. Therefore, a developer should be able The manager is in charge of rendering an overlay to the DOM. Therefore, a developer should be able
to control: to control:
- Its physical position (where the dialog is attached). This can either be: - Its physical position (where the dialog is attached). This can either be:
- globally: at root level of the DOM. This guarantees a total control over its painting, since - globally: at root level of the DOM. This guarantees a total control over its painting, since
the stacking context can be controlled from here and interfering parents (that set overflow the stacking context can be controlled from here and interfering parents (that set overflow
@ -35,12 +37,13 @@ browser/screen reader support (aria 1.0 vs 1.1). We strive for an optimum here b
For every overlay, the manager has access to the overlay element and the invoker (and possible For every overlay, the manager has access to the overlay element and the invoker (and possible
other elements that are needed for focus delegation as described in other elements that are needed for focus delegation as described in
https://www.w3.org/TR/wai-aria-practices/#dialog_modal (notes). <https://www.w3.org/TR/wai-aria-practices/#dialog_modal> (notes).
## Defining different types of overlays ## Defining different types of overlays
When browsing through the average ui library, one can encounter multiple names for occurrences of When browsing through the average ui library, one can encounter multiple names for occurrences of
overlays. Here is a list of names encountered throughout the years: overlays. Here is a list of names encountered throughout the years:
- dialog - dialog
- modal - modal
- popover - popover
@ -67,11 +70,11 @@ in framework C, might actually be just a dialog. Etc etc…
In order to avoid confusion and be as specification compliant as possible, its always a good idea In order to avoid confusion and be as specification compliant as possible, its always a good idea
to consult the W3C. This website shows a full list with specifications of accessible web widgets: to consult the W3C. This website shows a full list with specifications of accessible web widgets:
https://www.w3.org/TR/wai-aria-practices/. <https://www.w3.org/TR/wai-aria-practices/>.
A great overview of all widget-, structure- and role relations can be found in the ontology diagram A great overview of all widget-, structure- and role relations can be found in the ontology diagram
below: below:
![rdf_model](/uploads/5aa251bd4a7a1ec36241d20e0af8cbb3/rdf_model.png) ![rdf_model](/uploads/5aa251bd4a7a1ec36241d20e0af8cbb3/rdf_model.png)
https://www.w3.org/WAI/PF/aria-1.1/rdf_model.svg <https://www.w3.org/WAI/PF/aria-1.1/rdf_model.svg>
Out of all the overlay names mentioned above, we can only identify the dialog and the tooltip as Out of all the overlay names mentioned above, we can only identify the dialog and the tooltip as
official roles. official roles.
@ -80,57 +83,59 @@ Lets take a closer look at their definitions...
### Dialog ### Dialog
The dialog is described as follows by the W3C: The dialog is described as follows by the W3C:
> “A dialog is a window overlaid on either the primary window or another dialog window. Windows
under a modal dialog are inert. That is, users cannot interact with content outside an active
dialog window. Inert content outside an active dialog is typically visually obscured or dimmed so
it is difficult to discern, and in some implementations, attempts to interact with the inert
content cause the dialog to close.
Like non-modal dialogs, modal dialogs contain their tab sequence. That is, Tab and Shift + Tab do
not move focus outside the dialog. However, unlike most non-modal dialogs, modal dialogs do not
provide means for moving keyboard focus outside the dialog window without closing the dialog.”
- specification: https://www.w3.org/TR/wai-aria-1.1/#dialog > “A dialog is a window overlaid on either the primary window or another dialog window. Windows
- widget description: https://www.w3.org/TR/wai-aria-practices/#dialog_modal > under a modal dialog are inert. That is, users cannot interact with content outside an active
> dialog window. Inert content outside an active dialog is typically visually obscured or dimmed so
> it is difficult to discern, and in some implementations, attempts to interact with the inert
> content cause the dialog to close.
> Like non-modal dialogs, modal dialogs contain their tab sequence. That is, Tab and Shift + Tab do
> not move focus outside the dialog. However, unlike most non-modal dialogs, modal dialogs do not
> provide means for moving keyboard focus outside the dialog window without closing the dialog.”
- specification: <https://www.w3.org/TR/wai-aria-1.1/#dialog>
- widget description: <https://www.w3.org/TR/wai-aria-practices/#dialog_modal>
### Tooltip ### Tooltip
According to W3C, a tooltip is described by the following: According to W3C, a tooltip is described by the following:
> “A tooltip is a popup that displays information related to an element when the element receives
keyboard focus or the mouse hovers over it. It typically appears after a small delay and disappears
when Escape is pressed or on mouse out.
> Tooltip widgets do not receive focus. A hover that contains focusable elements can be made using
a non-modal dialog.”
- specification: https://www.w3.org/TR/wai-aria-1.1/#tooltip > “A tooltip is a popup that displays information related to an element when the element receives
- widget description: https://www.w3.org/TR/wai-aria-practices/#tooltip > keyboard focus or the mouse hovers over it. It typically appears after a small delay and disappears
> when Escape is pressed or on mouse out.
> Tooltip widgets do not receive focus. A hover that contains focusable elements can be made using
> a non-modal dialog.”
- specification: <https://www.w3.org/TR/wai-aria-1.1/#tooltip>
- widget description: <https://www.w3.org/TR/wai-aria-practices/#tooltip>
What needs to be mentioned is that the W3C taskforce didnt reach consensus yet about the above What needs to be mentioned is that the W3C taskforce didnt reach consensus yet about the above
tooltip description. A good alternative resource: tooltip description. A good alternative resource:
https://inclusive-components.design/tooltips-toggletips/ <https://inclusive-components.design/tooltips-toggletips/>
### Dialog vs tooltip ### Dialog vs tooltip
Summarizing, the main differences between dialogs and tooltips are: Summarizing, the main differences between dialogs and tooltips are:
- Dialogs have a modal option, tooltips dont - Dialogs have a modal option, tooltips dont
- Dialogs have interactive content, tooltips dont - Dialogs have interactive content, tooltips dont
- Dialogs are opened via regular buttons (click/space/enter), tooltips act on focus/mouseover - Dialogs are opened via regular buttons (click/space/enter), tooltips act on focus/mouseover
### Other roles and concepts ### Other roles and concepts
Other roles worth mentioning are *alertdialog* (a specific instance of the dialog for system Other roles worth mentioning are _alertdialog_ (a specific instance of the dialog for system
alerts), select (an abstract role), *combobox* and *menu*. alerts), select (an abstract role), _combobox_ and _menu_.
Also, the W3C document often refers to *popup*. This term is mentioned in the context of *combobox*, Also, the W3C document often refers to _popup_. This term is mentioned in the context of _combobox_,
*listbox*, *grid*, *tree*, *dialog* and *tooltip*. Therefore, one could say it could be a term _listbox_, _grid_, _tree_, _dialog_ and _tooltip_. Therefore, one could say it could be a term
*aria-haspopup* attribute needs to be mentioned: it can have values menu, listbox, grid, _aria-haspopup_ attribute needs to be mentioned: it can have values menu, listbox, grid,
tree and dialog. tree and dialog.
## Common Overlay Components ## Common Overlay Components
In our component library, we want to have the following overlay child components: In our component library, we want to have the following overlay child components:
- Dialog - Dialog
- Tooltip - Tooltip
- Popover - Popover
@ -141,77 +146,69 @@ In our component library, we want to have the following overlay child comp
- Combobox/autocomplete - Combobox/autocomplete
- Application menu - Application menu
### Dialog ### Dialog Component
The dialog is pretty much the dialog as described in the W3C spec, having the modal option applied The dialog is pretty much the dialog as described in the W3C spec, having the modal option applied
by default. by default.
The flexibility in focus delegation (see https://www.w3.org/TR/wai-aria-practices/#dialog_modal The flexibility in focus delegation (see <https://www.w3.org/TR/wai-aria-practices/#dialog_modal> notes) is not implemented, however.
notes) is not implemented, however.
Addressing these: Addressing these:
- The first focusable element in the content: although delegate this focus management to the - The first focusable element in the content: although delegate this focus management to the
implementing developer is highly preferred, since this is highly dependent on the moment the dialog implementing developer is highly preferred, since this is highly dependent on the moment the dialog
content has finished rendering. This is something the overlay manager or dialog widget should not content has finished rendering. This is something the overlay manager or dialog widget should not
be aware of in order to provide.a clean and reliable component. be aware of in order to provide.a clean and reliable component.
- The focusable element after a close: by default, this is the invoker. For different behaviour, a - The focusable element after a close: by default, this is the invoker. For different behaviour, a
reference should be supplied to a more logical element in the particular workflow. reference should be supplied to a more logical element in the particular workflow.
### Tooltip Component
### Tooltip
The tooltip is always invoked on hover and has no interactive content. See The tooltip is always invoked on hover and has no interactive content. See
https://inclusive-components.design/tooltips-toggletips/ (the tooltip example, not the toggle tip). <https://inclusive-components.design/tooltips-toggletips/> (the tooltip example, not the toggle tip).
### Popover Component
### Popover
The popover looks like a crossover between the dialog and the tooltip. Popovers are invoked on The popover looks like a crossover between the dialog and the tooltip. Popovers are invoked on
click. For non interactive content, the https://inclusive-components.design/tooltips-toggletips/ click. For non interactive content, the <https://inclusive-components.design/tooltips-toggletips/> toggletip could be applied.
toggletip could be applied.
Whenever there would be a close button present, the non interactiveness wouldnt apply. Whenever there would be a close button present, the non interactiveness wouldnt apply.
An alternative implementation: https://whatsock.com/tsg/Coding%20Arena/Popups/Popup%20(Internal%20Content)/demo.htm An alternative implementation: <https://whatsock.com/tsg/Coding%20Arena/Popups/Popup%20(Internal%20Content)/demo.htm>
This looks more like a small dialog, except that the page flow is respected (no rotating tab) This looks more like a small dialog, except that the page flow is respected (no rotating tab)
### Dropdown Component
### Dropdown
The dropdown is not an official aria-widget and thus cant be tied to a specific role. It exists The dropdown is not an official aria-widget and thus cant be tied to a specific role. It exists
in a lot of UI libraries and most of them share these properties: in a lot of UI libraries and most of them share these properties:
- Preferred position is down
- When no space at bottom, they show up (in which. Case it behaves a as a dropup) - Preferred position is down
- Unlike popovers and tooltips, it will never be positioned horizontally - When no space at bottom, they show up (in which. Case it behaves a as a dropup)
- Unlike popovers and tooltips, it will never be positioned horizontally
Aliases are popdown, pulldown and many others. Aliases are popdown, pulldown and many others.
### Select Component
### Select
Implemented as a dropdown listbox with invoker button. Depending on the content of the options, Implemented as a dropdown listbox with invoker button. Depending on the content of the options,
the child list can either be of type listbox or grid. the child list can either be of type listbox or grid.
See: https://www.w3.org/TR/wai-aria-practices/examples/listbox/listbox-collapsible.html See: <https://www.w3.org/TR/wai-aria-practices/examples/listbox/listbox-collapsible.html>
### Combobox Component
### Combobox
Implemented as a dropdown combobox with invoker input. Input is used as search filter Implemented as a dropdown combobox with invoker input. Input is used as search filter
and can contain autocomplete or autosuggest functionality: and can contain autocomplete or autosuggest functionality:
See: https://www.w3.org/TR/wai-aria-practices/#combobox See: <https://www.w3.org/TR/wai-aria-practices/#combobox>
### (Application) menu Component
### (Application) menu
Or sometimes called context-menu. Uses a dropdown to position its content. Or sometimes called context-menu. Uses a dropdown to position its content.
See: https://www.w3.org/WAI/tutorials/menus/flyout/ See: <https://www.w3.org/WAI/tutorials/menus/flyout/>
Be aware not to use role=“menu”: https://www.w3.org/WAI/tutorials/menus/application-menus/ Be aware not to use role=“menu”: <https://www.w3.org/WAI/tutorials/menus/application-menus/>
### Toast Component
### Toast See: <https://www.webcomponents.org/element/@polymer/paper-toast>. Should probably be implemented as
See: https://www.webcomponents.org/element/@polymer/paper-toast. Should probably be implemented as
an alertdialog. an alertdialog.
### Sheet Component
### Sheet See: <https://material.io/design/components/sheets-bottom.html>. Should probably be a
See: https://material.io/design/components/sheets-bottom.html. Should probably be a
global(modal) dialog. global(modal) dialog.

View file

@ -7,7 +7,9 @@
You should use [lion-radio](../radio/)'s inside this element. You should use [lion-radio](../radio/)'s inside this element.
## Features ## Features
Since it extends from [lion-fieldset](../fieldset/), it has all the features a fieldset has. Since it extends from [lion-fieldset](../fieldset/), it has all the features a fieldset has.
- Get or set the checked value of the group: - Get or set the checked value of the group:
- modelValue (default) - `checkedValue()` - modelValue (default) - `checkedValue()`
- formattedValue - `formattedValue()` - formattedValue - `formattedValue()`
@ -16,7 +18,8 @@ Since it extends from [lion-fieldset](../fieldset/), it has all the features a f
## How to use ## How to use
### Installation ### Installation
```
```sh
npm i --save @lion/radio @lion/radio-group npm i --save @lion/radio @lion/radio-group
``` ```

View file

@ -5,6 +5,7 @@
`lion-radio` component is a sub-element to be used in [lion-radio-group](../radio-group/) elements. Its purpose is to provide a way for users to check a **single** option amongst a set of choices. `lion-radio` component is a sub-element to be used in [lion-radio-group](../radio-group/) elements. Its purpose is to provide a way for users to check a **single** option amongst a set of choices.
## Features ## Features
- Get or set the checked state (boolean) - `choiceChecked()` - Get or set the checked state (boolean) - `choiceChecked()`
- Get or set the value of the choice - `choiceValue()` - Get or set the value of the choice - `choiceValue()`
- Pre-select an option by setting the `checked` boolean attribute - Pre-select an option by setting the `checked` boolean attribute
@ -12,8 +13,9 @@
## How to use ## How to use
### Installation ### Installation
```
npm i --save @lion/radio; ```sh
npm i --save @lion/radio
``` ```
```js ```js

View file

@ -10,13 +10,15 @@ where each option name starts with the same word or phrase can also significantl
usability for keyboard and screen reader users. usability for keyboard and screen reader users.
## Features ## Features
- catches and forwards the select events - catches and forwards the select events
- can be set to required or disabled - can be set to required or disabled
## How to use ## How to use
### Installation ### Installation
```
```sh
npm i --save @lion/select npm i --save @lion/select
``` ```
@ -42,11 +44,9 @@ import '@lion/select/lion-select.js';
``` ```
You can preselect an option by setting the property modelValue. You can preselect an option by setting the property modelValue.
```html ```html
<lion-select <lion-select name="favoriteColor" .modelValue="${'<value of option 2>'}">
name="favoriteColor"
.modelValue="${'<value of option 2>'}"
>
... ...
</lion-select> </lion-select>
``` ```

View file

@ -5,6 +5,7 @@
`lion-steps` breaks a single goal down into dependable sub-tasks. `lion-steps` breaks a single goal down into dependable sub-tasks.
## Features ## Features
- navigate between different steps with 'previous' and 'next' functions. - navigate between different steps with 'previous' and 'next' functions.
- keeps status of each step - keeps status of each step
- untouched - untouched
@ -21,7 +22,8 @@ In many application you build multi-step workflows like multi-step forms where y
## How to use ## How to use
### Installation ### Installation
```
```sh
npm i --save @lion/select npm i --save @lion/select
``` ```
@ -45,7 +47,7 @@ We provide two components: `lion-steps` and `lion-step`. Steps need to be direct
The first step needs to be explicitely set via `initial-step` so that it get status `entered`, while others are `untouched` by default. You can navigate between steps using `next()` and `previous()` methods, so that next step gets `entered` status, while previous one becomes `left`: The first step needs to be explicitely set via `initial-step` so that it get status `entered`, while others are `untouched` by default. You can navigate between steps using `next()` and `previous()` methods, so that next step gets `entered` status, while previous one becomes `left`:
```javascript ```js
... ...
next() { next() {
return this.$id('steps').next(); return this.$id('steps').next();
@ -90,7 +92,8 @@ If you have an intermediate step loading data via AJAX request and then automati
<lion-steps> <lion-steps>
<lion-step>preliminary step</lion-step> <lion-step>preliminary step</lion-step>
<lion-step forward-only>data is loaded and next() is called automatically afterwards</lion-step> <lion-step forward-only>data is loaded and next() is called automatically afterwards</lion-step>
<lion-step>do smth with data</lion-step><!-- user decides to go to previous step here --> <!-- user decides to go to previous step here -->
<lion-step>do smth with data</lion-step>
</lion-steps> </lion-steps>
``` ```

View file

@ -24,8 +24,5 @@ import '@lion/textarea/lion-textarea.js';
### Example ### Example
```html ```html
<lion-textarea <lion-textarea label="Stops growing after 4 rows" max-rows="4"></lion-textarea>
label="Stops growing after 4 rows"
max-rows="4"
></lion-textarea>
``` ```

View file

@ -30,7 +30,7 @@ import { %validatorName% } from '@lion/validate';
``` ```
> Note that we import an lion-input here as an example of a form control implementing ValidateMixin. > Note that we import an lion-input here as an example of a form control implementing ValidateMixin.
We could equally well use lion-textarea, lion-select, lion-fieldset etc. to illustrate our example. > We could equally well use lion-textarea, lion-select, lion-fieldset etc. to illustrate our example.
### Example ### Example
@ -42,7 +42,8 @@ import '@lion/input/lion-input.js';
import { isString, maxLengthValidator, defaultOkValidator } from '@lion/validate'; import { isString, maxLengthValidator, defaultOkValidator } from '@lion/validate';
const isInitialsRegex = /^([A-Z]\.)+$/; const isInitialsRegex = /^([A-Z]\.)+$/;
export const isExampleInitials = value => isString(value) && isInitialsRegex.test(value.toUpperCase()); export const isExampleInitials = value =>
isString(value) && isInitialsRegex.test(value.toUpperCase());
export const isExampleInitialsValidator = () => [ export const isExampleInitialsValidator = () => [
(...params) => ({ isExampleInitials: isExampleInitials(...params) }), (...params) => ({ isExampleInitials: isExampleInitials(...params) }),
]; ];
@ -63,7 +64,7 @@ A validator applied to `.errorValidators` expects an array with a function, a pa
optionally an additional configuration object. optionally an additional configuration object.
```js ```js
minMaxLengthValidator({ min: 5, max: 10 }) minMaxLengthValidator({ min: 5, max: 10 });
``` ```
The custom `isExampleInitialsValidator` checks if the value is fitting our regex, but does not The custom `isExampleInitialsValidator` checks if the value is fitting our regex, but does not

View file

@ -25,8 +25,8 @@ All validators are provided via pure functions. They should be applied to the el
```html ```html
<validatable-el <validatable-el
.errorValidators="${[[ myValidatorFunction, { myParam: 'foo' }, { extra: 'options' } ]]}"> .errorValidators="${[[ myValidatorFunction, { myParam: 'foo' }, { extra: 'options' } ]]}"
</validatable-el> ></validatable-el>
``` ```
As you can see the 'errorValidators' property expects a map (an array of arrays). As you can see the 'errorValidators' property expects a map (an array of arrays).
@ -46,8 +46,8 @@ Below example has two validators (as factory functions) applied:
```html ```html
<validatable-el <validatable-el
.errorValidators="${[minLengthValidator({ min: 3 }), isZipCodeValidator()]}"> .errorValidators="${[minLengthValidator({ min: 3 }), isZipCodeValidator()]}"
</validatable-el> ></validatable-el>
``` ```
### Default Validators ### Default Validators
@ -112,8 +112,8 @@ The api for warning validators and info validators are as follows:
```html ```html
<validatable-field <validatable-field
.warningValidators="${[myWarningValidator()]}" .warningValidators="${[myWarningValidator()]}"
.infoValidators="${[myInfoValidator()]}"> .infoValidators="${[myInfoValidator()]}"
</validatable-field> ></validatable-field>
``` ```
### Success validators ### Success validators