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

View file

@ -1,3 +1,5 @@
# Lion Web Components
> ## 🛠 Status: Pilot Phase
>
> 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 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)
# 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>)
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.

View file

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

View file

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

View file

@ -7,12 +7,14 @@
You should use [lion-checkbox](../checkbox/)'s inside this element.
## Features
Since it extends from [lion-fieldset](../fieldset/), it has all the features a fieldset has.
## How to use
### Installation
```
```sh
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.
## Features
- Get or set the checked state (boolean) - `choiceChecked()`
- Get or set the value of the choice - `choiceValue()`
- Pre-select an option by setting the `checked` boolean attribute
@ -12,8 +13,9 @@
## How to use
### Installation
```
npm i --save @lion/checkbox;
```sh
npm i --save @lion/checkbox
```
```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.
```javascript
```js
const BaseMixin = dedupeMixin((superClass) => {
return class extends superClass { ... };
});

View file

@ -1,4 +1,3 @@
# Form Fundaments
[//]: # '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
[custom form field](./docs/CustomFieldsTutorial.md) should be created. One could think of components
like:
like:
- slider
- combobox
@ -56,9 +55,11 @@ Fieldsets are the basis for:
<!-- TODO: - [`FormControlMixin`] () -->
<!-- TODO: - [`LionField`] () -->
- [Model Value](./docs/ModelValue.md)
- [Formatting and parsing](./docs/FormattingAndParsing.md)
- [Interaction states](./docs/InteractionStates.md)
- [Validation System](../validate/docs/ValidationSystem.md)
- [Custom Fields](./docs/CustomFieldsTutorial.md)
<!-- TODO: - [`FocusMixin`] (/FocusMixin.md) -->

View file

@ -1,9 +1,11 @@
# Creating a custom field
Custom fields can be created in just a few steps. All you need is an interaction element
(like for instance a slider, a listbox or a combobox) and connect it to the [Field](../README.md)
functionality.
## Prerequisite: an interaction element
An interaction element provides the means for the end user to enter a certain value, just like
native elements provide in this (think of `<input>`, `<textarea>` and `<select>`).
An example of a non native element is the
@ -15,24 +17,27 @@ For this tutorial, we assume we have a component `<my-slider>` that exposes its
it has a tabindex=“0” applied.
## Connecting the interaction element to the field
Now we want to integrate the slider in our form framework to enrich the user interface, get
validation support and get all the other [benefits of LionField](../README.md).
We start of by creating a component `<lion-slider>` that extends from `LionField`.
Then we follow the steps below:
- ### 1. Add your interaction element as input slot'
Here you return the element the user interacts with. By configuring it as a slot, it will end up
in light DOM, ensuring the best accessibility for the end user.
Here you return the element the user interacts with. By configuring it as a slot, it will end up
in light DOM, ensuring the best accessibility for the end user.
- ### 2. Proxy event `my-slider-changed` to `user-input-changed` event
The `user-input-changed` event is listened to by the FormatMixin: it should be regarded as the
equivalent of the `input` event of the platform, but for custom built interaction elements.
The `user-input-changed` event is listened to by the FormatMixin: it should be regarded as the
equivalent of the `input` event of the platform, but for custom built interaction elements.
- ### 3. Proxy property `<my-slider>.mySliderValue` to `<lion-slider>.value`
Every time the `user-input-changed` fires, the value of `<my-slider>` is synchronized with the
[`modelValue`](./modelValue.md) of `<my-slider>`. Now the cycle is complete: the modelValue connects
your interaction element to all logic inside the LionField.
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:

View file

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

View file

@ -1,10 +1,12 @@
# FormatMixin
The FormatMixin keeps track of the `modelValue`, `formattedValue` and `serializedValue`.
It is designed to work in conjunction with `LionField`.
## Concepts of different values
### model value
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.
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`.
Examples:
- 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`
### view value
The view value is the result of the formatter function.
It will be stored as `.formattedValue` and synchronized to `.value` (a viewValue setter that
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.
Examples:
- 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
1234.56)
1234.56)
### serialized value
This is the serialized version of the model value.
It exists for maximal compatibility with the platform API.
The serialized value can be an interface in context where data binding is not supported
and a serialized string needs to be set.
Examples:
- 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
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
In order to create advanced user experiences (automatically formatting a user input or an input
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.
### Formatters
A formatter should return a `formattedValue`:
```js
function formatDate(modelValue, options) {
if (!(modelValue instanceof Date)) {
@ -58,27 +67,35 @@ function formatDate(modelValue, options) {
return formatDateLocalized(modelValue, options);
}
```
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
### Parsers
A parser should return a `modelValue`:
```js
function parseDate(formattedValue, options) {
return formattedValue === '' ? undefined : parseDateLocalized(formattedValue);
}
```
Notice that when it's not possible to create a valid modelValue based on the formattedValue,
one should return `undefined`.
### Serializers and deserializers
A serializer should return a `serializedValue`:
```js
function serializeDate(modelValue, options) {
return modelValue.toISOString();
}
```
A deserializer should return a `modelValue`:
```js
function deserializeDate(serializeValue, options) {
return new Date(serializeValue);
@ -86,13 +103,15 @@ function deserializeDate(serializeValue, options) {
```
### FieldCustomMixin
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
[`<lion-input-amount>`](../../input-amount).
## Flow diagram
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.
[Flow diagram](./formatterParserFlow.svg)

View file

@ -27,7 +27,7 @@ Examples:
- 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
1234.56)
1234.56)
### 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 number input this would be the String representation of a float ('1234.56' instead
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
@ -108,7 +108,7 @@ Concrete examples can be found at [`<lion-input-date>`](../../input-date/) and
## Flow diagram
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.
[Flow diagram](./formatterParserFlow.svg)

View file

@ -40,14 +40,14 @@ static _isPrefilled(modelValue) {
We show the validity feedback when one of the following conditions is met:
- prefilled:
The user already filled in something, or the value is prefilled
when the form is initially rendered.
The user already filled in something, or the value is prefilled
when the form is initially rendered.
- touched && dirty && !prefilled:
When a user starts typing for the first time in a field with for instance `required` validation,
error message should not be shown until a field becomes `touched` (a user leaves(blurs) a field).
When a user enters a field without altering the value (making it `dirty` but not `touched`),
an error message shouldn't be shown either.
When a user starts typing for the first time in a field with for instance `required` validation,
error message should not be shown until a field becomes `touched` (a user leaves(blurs) a field).
When a user enters a field without altering the value (making it `dirty` but not `touched`),
an error message shouldn't be shown either.
- 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.
## Single source of truth
ModelValues are designed to provide the Application Developer a single way of programmatical
interaction with the form for an Application Developer.
### One single concept for Application Developers
Application Developers need to only care about interacting with the modelValue on a form control
level, via:
- `.modelValue`
- `@model-value-changed`
> 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
Internally, all derived states are computed from model-value-changed events.
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
(see Interaction States).
## Unparseable modelValues
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`.
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
purposes:
- restoring user sessions
- realtime updated with all value changes
### Restoring user sessions
As a modelValue is always a single source of truth
### Realtime updated with all value changes
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.

View file

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

View file

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

View file

@ -28,13 +28,19 @@ import '@lion/form/lion-form.js';
### Example
```html
<lion-form><form>
<lion-fieldset name="fullName">
<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-fieldset>
<lion-textarea label="Description" name="description" .modelValue=${model.description}></lion-textarea>
</form></lion-form>
<lion-form>
<form>
<lion-fieldset name="fullName">
<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-fieldset>
<lion-textarea
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)

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:
```html
<lion-icon .svg=${bugSvg}></lion-icon>
<lion-icon .svg="${bugSvg}"></lion-icon>
```
### 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:
```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.
@ -83,7 +84,7 @@ A `lion-icon` may be styled like a regular HTML element:
stroke: lightsteelblue;
}
</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.

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.
## Features
- based on [lion-input](../input)
- makes use of [formatNumber](../localize/docs/number.md) for formatting and parsing.
- option to show currency as a suffix
@ -20,7 +21,8 @@
## How to use
### Installation
```
```sh
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.
## Features
- based on [lion-input](../input)
- makes use of [formatDate](../localize/docs/date.md) for formatting and parsing.
- option to overwrite locale to change the formatting and parsing
@ -19,7 +19,8 @@
## How to use
### Installation
```
```sh
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).
## Features
- input field with a datepicker to help to choose a date
- based on [lion-input-date](../input-date)
- 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
### Installation
```
```sh
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.
## Features
- based on [lion-input](../input)
- default label 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
### Installation
```
```sh
npm i --save @lion/input-email
```
@ -25,8 +26,5 @@ import '@lion/input-email/lion-input-email.js';
### Example
```html
<lion-input-email
name="email"
.errorValidators="${[['required']]}"
></lion-input-email>
<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.
## Features
- based on [lion-input](../input)
- default label in different languages
- makes use of IBAN specific [validate](../validate) with corresponding error messages in different languages
@ -14,7 +15,8 @@
## How to use
### Installation
```
```sh
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.
## Features
- based on [field](../field/)
- 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.
@ -21,8 +22,9 @@
## How to use
### Installation
```
npm i --save @lion/input;
```sh
npm i --save @lion/input
```
```js
@ -44,6 +46,7 @@ import { maxLengthValidator } from '@lion/validate';
```
Making use of slots:
```html
<lion-input name="amount">
<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.
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';
// 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
As a component developer you get:
- unified data structure for different locales;
- promisified helper to load data;
- notification about page locale changes;
@ -36,52 +37,52 @@ Localization data modules for `my-hello-component` might look like these:
- `/path/to/my-hello-component/translations/en-GB.js`
```js
export default {
greeting: 'Hello {name}!',
};
```
```js
export default {
greeting: 'Hello {name}!',
};
```
- `/path/to/my-hello-component/translations/nl-NL.js`
```js
export default {
greeting: 'Hallo {name}!',
};
```
```js
export default {
greeting: 'Hallo {name}!',
};
```
The approach with ES modules is great because it allows to simply reuse basic locale data and override only the needed parts for more specific locales.
- `/path/to/my-family-component/translations/en.js`
```js
export default {
havePartnerQuestion: 'Do you have a partner?',
haveChildrenQuestion: 'Do you have children?',
};
```
```js
export default {
havePartnerQuestion: 'Do you have a partner?',
haveChildrenQuestion: 'Do you have children?',
};
```
- `/path/to/my-family-component/translations/en-GB.js`
```js
import en from './en.js'
export default en;
```
```js
import en from './en.js';
export default en;
```
- `/path/to/my-family-component/translations/en-US.js`
```js
import en from './en.js'
```js
import en from './en.js';
export default {
...en,
haveChildrenQuestion: 'Do you have kids?',
};
```
export default {
...en,
haveChildrenQuestion: 'Do you have kids?',
};
```
To load this data the method `loadNamespace()` which returns a promise can be used.
```javascript
```js
localize.loadNamespace(namespace).then(() => {
// do smth when data is loaded
});
@ -92,58 +93,60 @@ Let's look at both cases in depth.
1. Using explicit loader functions:
```javascript
// use the dynamic import to load static assets
localize.loadNamespace({
'my-hello-component': (locale) => {
// resolves to a module with the module.default `{ greeting: 'Hallo {name}!' }`
return import(`./translations/${locale}.js`);
},
});
```
```js
// use the dynamic import to load static assets
localize.loadNamespace({
'my-hello-component': locale => {
// resolves to a module with the module.default `{ greeting: 'Hallo {name}!' }`
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.
The module must have a `default` export as shown above to be handled properly.
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.
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.
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.
```javascript
// fetch from an API
localize.loadNamespace({
'my-hello-component': async (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}!' }`
},
});
```
```js
// fetch from an API
localize.loadNamespace({
'my-hello-component': async 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}!' }`
},
});
```
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.
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.
2. Using the loaders preconfigured via `setupNamespaceLoader()`:
1. Using the loaders preconfigured via `setupNamespaceLoader()`:
```javascript
// using the regexp to match all component names staring with 'my-'
localize.setupNamespaceLoader(/my-.+/, async (locale, namespace) => {
const response = await fetch(`http://api.example.com/?namespace=${namespace}&locale=${locale}`);
return response.json();
});
```js
// using the regexp to match all component names staring with 'my-'
localize.setupNamespaceLoader(/my-.+/, async (locale, namespace) => {
const response = await fetch(`http://api.example.com/?namespace=${namespace}&locale=${locale}`);
return response.json();
});
Promise.all([
localize.loadNamespace('my-hello-component');
localize.loadNamespace('my-goodbuy-component');
])
```
Promise.all([
localize.loadNamespace('my-hello-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 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.
```javascript
```js
localize.addEventListener('localeChanged', () => {
// 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.
It expects a key in the format of `namespace:name` and can also receive variables as a second argument.
```javascript
```js
_onNameChanged() {
// inserts 'Hello John!' into the element with id="name"
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.
It provides several capabilities:
- automatic loading of specified namespaces;
- life-cycle callbacks for localization events;
- alias `_m` for `localize.msg`;
- promisified alias `_msgAsync` for `localize.msg` resolved when data is loaded.
```javascript
```js
class MyHelloComponent extends LocalizeMixin(LionLitElement) {
static get localizeNamespaces() {
// 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.
It provides extra capabilities on top of LocalizeMixin:
- smart wrapper `msg` for `localize.msg`;
- 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()`:
```javascript
```js
render() {
return html`
<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
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;
- smart defaults for data loading;
- 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:
```javascript
```js
// my-inlined-data.js
import { localize } from 'lion-localize/localize.js';
localize.addData('en-GB', 'my-namespace', {/* data */});
localize.addData('nl-NL', 'my-namespace', {/* data */});
localize.addData('en-GB', 'my-namespace', {
/* data */
});
localize.addData('nl-NL', 'my-namespace', {
/* data */
});
// my-app.js
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()`.
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
localize.setupNamespaceLoader('my-hello-component', async (locale) => {
const response = await fetch(`http://api.example.com/?namespace=my-hello-component&locale=${locale}`);
localize.setupNamespaceLoader('my-hello-component', async locale => {
const response = await fetch(
`http://api.example.com/?namespace=my-hello-component&locale=${locale}`,
);
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.
## Features
- **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
## How to use
### Installation
```
npm i --save @lion/localize;
```sh
npm i --save @lion/localize
```
### 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.
## Features
- **formatDate**: returns a formatted date based on locale
- **parseDate**: returns a date Object
- **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
### Installation
```
npm i --save @lion/localize;
```sh
npm i --save @lion/localize
```
### Example
@ -19,12 +21,15 @@ npm i --save @lion/localize;
```js
import { parseDate, formatDate } from '@lion/localize';
function dateExampleFunction () {
function dateExampleFunction() {
const parsedDate = parseDate('21-05-2012');
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.
`LocalizeMixin` has the following features:
- Use translated text in your template (`msgLit()`)
- Get the localize namespaces of the component (`localizeNamespaces`)
- Await loading of localize namespaces (`localizeNamespacesLoaded`)
Advanced:
- 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
- Lifecycle methods `onLocaleReady()`, `onLocaleChanged()`, `onLocaleUpdated()`
## How to use
### 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.
An example of the a preconfigured loader:
```js
static get 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:
```js
render() {
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.
### 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`.
```js
@ -112,6 +116,7 @@ export function myTemplate(someData) {
`;
}
```
This template is meant for importing in your webcomponent which uses this localize namespace.
### Translation files
@ -121,33 +126,32 @@ 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:
- `/path/to/my-family-component/translations/en.js`
```js
export default {
havePartnerQuestion: 'Do you have a partner?',
haveChildrenQuestion: 'Do you have children?',
};
```
```js
export default {
havePartnerQuestion: 'Do you have a partner?',
haveChildrenQuestion: 'Do you have children?',
};
```
- `/path/to/my-family-component/translations/en-GB.js`
```js
import en from './en.js'
export default en;
```
```js
import en from './en.js';
export default en;
```
- `/path/to/my-family-component/translations/en-US.js`
```js
import en from './en.js'
```js
import en from './en.js';
export default {
...en,
haveChildrenQuestion: 'Do you have kids?',
};
```
export default {
...en,
haveChildrenQuestion: 'Do you have kids?',
};
```
The module must have a `default` export as shown above to be handled properly.
@ -158,8 +162,10 @@ If you want to fetch translation data from some API this is also possible.
```js
// fetch from an API
localize.loadNamespace({
'my-hello-component': async (locale) => {
const response = await fetch(`http://api.example.com/?namespace=my-hello-component&locale=${locale}`);
'my-hello-component': async 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}!' }`
},
});

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.
## Features
- **formatNumber**: returns a formatted number based on locale
- **formatNumberToParts**: returns a formatted number in parts based on locale
- **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
### Installation
```
npm i --save @lion/localize;
```sh
npm i --save @lion/localize
```
### Example
@ -21,9 +23,9 @@ npm i --save @lion/localize;
```js
import { formatNumber } from '@lion/localize';
function numberExampleFunction () {
function numberExampleFunction() {
const number = 2000;
const options = { style: 'currency', currency: 'EUR', currencyDisplay: 'code' };
return formatNumber(number, options) // 'EUR 2,000.00' for British locale
const options = { style: 'currency', currency: 'EUR', currencyDisplay: 'code' };
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.
## Features
- [**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:
- [**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
### Installation
```sh
npm i --save @lion/overlays
```
### Example
```js
import { overlays } from '@lion/overlays';
const myCtrl = overlays.add(
new OverlayTypeController({
/* options */
})
}),
);
// name OverlayTypeController is for illustration purpose only
// 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
### Installation
```sh
npm i --save @lion/overlays
```
### Example
```js
import { overlays } from '@lion/overlays';
const myCtrl = overlays.add(
new GlobalOverlayController({
/* options */
})
}),
);
```
### ModalDialogController
A specific extension of GlobalOverlayController configured to create accessible modal dialogs.
```js

View file

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

View file

@ -1,19 +1,22 @@
# LocalOverlayPositioning
## 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.
## Features
- 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.
> 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
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):
```js
const localOverlay = overlays.add(
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:
```js
const localOverlay = overlays.add(
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.
## 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.
- 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
This document provides an outline of all possible occurrences of overlays found in applications in
general and thus provided by Lion.
For all concepts referred to in this document, please read [Overlay System Scope](./OverlaySystemScope.md).
## Local and global overlay controllers
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.
All of their configuration options will be described below as part of the _Responsive overlay_ section.
### Connection points and placement contexts
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)).
However, there is no required relationship here: we can create a modal dialog from
local context('page cursor') as well.
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
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
element with role="dialog". (we basically need to test our supported browsers and screen readers
for compatibility with aria-modal).
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
element with role="dialog". (we basically need to test our supported browsers and screen readers
for compatibility with aria-modal).
- 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
'counter'.
'counter'.
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.
@ -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,
per instance of an overlay.
## Responsive overlay
### Responsive 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,
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.
Note: a more generic and precise term for all mentionings of `invoker` below would actually be
`relative positioning element`.
```
```text
- {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} 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:
```
```text
- {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} 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)
```
```text
// 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
- {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 provide preconfigured configuration objects for the global/local
overlay controllers.
They provide an imperative and very flexible api for creating overlays and should be used by
Subclassers, inside webcomponents.
#### Dialog
### Dialog Controller
```js
{
isGlobal: true,
@ -130,7 +140,8 @@ Subclassers, inside webcomponents.
}
```
#### Tooltip
### Tooltip Controller
```js
{
isTooltip: true,
@ -139,16 +150,20 @@ Subclassers, inside webcomponents.
}
```
#### Popover
### Popover Controller
```js
{
handlesUserInteraction: true,
handlesAccessibility: true,
}
```
#### Dropdown
### Dropdown Controller
It will be quite common to override placement to 'bottom-fullwidth'.
Also, it would be quite common to add a pointerNode.
```js
{
placement: 'bottom',
@ -156,63 +171,79 @@ Also, it would be quite common to add a pointerNode.
handlesAccessibility: true,
}
```
#### Toast
### Toast Controller
TODO:
- add an option for role="alertdialog" ?
- add an option for a 'hide timer' and belonging a11y features for this
```js
{
...Dialog,
viewportPlacement: 'top-right', (?)
}
```
#### Sheet (bottom, top, left, right)
### Sheet Controller (bottom, top, left, right)
```js
{
...Dialog,
viewportPlacement: '{top|bottom|left|right}-fullwidth', (?)
}
```
#### Select
### Select Controller
No need for a config, will probably invoke ResponsiveOverlayCtrl and switches
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
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
config based on media query from Dropdown to BottomSheet/CenteredDialog
## Web components
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
user interfaces.
Web components should use
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.
#### Tooltip
### Tooltip Component
```html
<lion-tooltip>
<button slot="invoker">hover/focus</button>
<div slot="content">This will be shown</div>
</lion-tooltip>
```
#### Popover
### Popover Component
```html
<lion-popover>
<button slot="invoker">click/space/enter</button>
<div slot="content">This will be shown</div>
</lion-popover>
```
#### Dropdown
### Dropdown Component
Like the name suggests, the default placement will be button
```html
<lion-dropdown>
<button slot="invoker">click/space/enter</button>
@ -223,17 +254,18 @@ Like the name suggests, the default placement will be button
</ul>
</lion-dropdown>
```
#### Toast
Imperative might be better here?
#### Sheet (bottom, top, left, right)
### Toast Component
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
in detail in different sections.

View file

@ -10,18 +10,20 @@ document flow.
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.
As opposed to a single overlay, the overlay manager stores knowledge about:
- whether the scroll behaviour of the body element can be manipulated
- 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
to control:
- 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
values or transforms) cant be apparent. Additionally, making a modal dialog requiring
all surroundings to have aria-hidden="true", will be easier when the overlay is attached on
body level.
- locally: next to the invoking element. This gives advantages for accessibility and
- locally: next to the invoking element. This gives advantages for accessibility and
(performant) rendering of an overlay next to its invoker on scroll and window resizes
- Toggling of the shown state of the overlay
- Positioning preferences(for instance bottom-left) and strategies (ordered fallback preferences)
@ -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
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
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:
- dialog
- modal
- 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
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
below:
![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
official roles.
@ -80,57 +83,59 @@ Lets take a closer look at their definitions...
### Dialog
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
- widget description: https://www.w3.org/TR/wai-aria-practices/#dialog_modal
> “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>
- widget description: <https://www.w3.org/TR/wai-aria-practices/#dialog_modal>
### Tooltip
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
- widget description: https://www.w3.org/TR/wai-aria-practices/#tooltip
> “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>
- 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
tooltip description. A good alternative resource:
https://inclusive-components.design/tooltips-toggletips/
<https://inclusive-components.design/tooltips-toggletips/>
### Dialog vs tooltip
Summarizing, the main differences between dialogs and tooltips are:
- Dialogs have a modal option, tooltips dont
- Dialogs have interactive content, tooltips dont
- Dialogs are opened via regular buttons (click/space/enter), tooltips act on focus/mouseover
### Other roles and concepts
Other roles worth mentioning are *alertdialog* (a specific instance of the dialog for system
alerts), select (an abstract role), *combobox* and *menu*.
Other roles worth mentioning are _alertdialog_ (a specific instance of the dialog for system
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*,
*listbox*, *grid*, *tree*, *dialog* and *tooltip*. Therefore, one could say it could be a term
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
*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.
## Common Overlay Components
In our component library, we want to have the following overlay child components:
- Dialog
- Tooltip
- Popover
@ -141,77 +146,69 @@ In our component library, we want to have the following overlay child comp
- Combobox/autocomplete
- Application menu
### Dialog
### Dialog Component
The dialog is pretty much the dialog as described in the W3C spec, having the modal option applied
by default.
The flexibility in focus delegation (see https://www.w3.org/TR/wai-aria-practices/#dialog_modal
notes) is not implemented, however.
The flexibility in focus delegation (see <https://www.w3.org/TR/wai-aria-practices/#dialog_modal> notes) is not implemented, however.
Addressing these:
- 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
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.
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
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
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
### Tooltip Component
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
### Popover Component
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/
toggletip could be applied.
click. For non interactive content, the <https://inclusive-components.design/tooltips-toggletips/> toggletip could be applied.
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)
### Dropdown
### Dropdown Component
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:
- Preferred position is down
- 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
- Preferred position is down
- 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.
### Select
### Select Component
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.
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
### Combobox Component
Implemented as a dropdown combobox with invoker input. Input is used as search filter
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
### (Application) menu Component
Or sometimes called context-menu. Uses a dropdown to position its content.
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/
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/>
### 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.
### 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.

View file

@ -7,7 +7,9 @@
You should use [lion-radio](../radio/)'s inside this element.
## Features
Since it extends from [lion-fieldset](../fieldset/), it has all the features a fieldset has.
- Get or set the checked value of the group:
- modelValue (default) - `checkedValue()`
- formattedValue - `formattedValue()`
@ -16,7 +18,8 @@ Since it extends from [lion-fieldset](../fieldset/), it has all the features a f
## How to use
### Installation
```
```sh
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.
## Features
- Get or set the checked state (boolean) - `choiceChecked()`
- Get or set the value of the choice - `choiceValue()`
- Pre-select an option by setting the `checked` boolean attribute
@ -12,8 +13,9 @@
## How to use
### Installation
```
npm i --save @lion/radio;
```sh
npm i --save @lion/radio
```
```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.
## Features
- catches and forwards the select events
- can be set to required or disabled
## How to use
### Installation
```
```sh
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.
```html
<lion-select
name="favoriteColor"
.modelValue="${'<value of option 2>'}"
>
<lion-select name="favoriteColor" .modelValue="${'<value of option 2>'}">
...
</lion-select>
```

View file

@ -5,6 +5,7 @@
`lion-steps` breaks a single goal down into dependable sub-tasks.
## Features
- navigate between different steps with 'previous' and 'next' functions.
- keeps status of each step
- untouched
@ -21,7 +22,8 @@ In many application you build multi-step workflows like multi-step forms where y
## How to use
### Installation
```
```sh
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`:
```javascript
```js
...
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-step>preliminary step</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>
```

View file

@ -24,8 +24,5 @@ import '@lion/textarea/lion-textarea.js';
### Example
```html
<lion-textarea
label="Stops growing after 4 rows"
max-rows="4"
></lion-textarea>
<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.
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
@ -42,7 +42,8 @@ import '@lion/input/lion-input.js';
import { isString, maxLengthValidator, defaultOkValidator } from '@lion/validate';
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 = () => [
(...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.
```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

View file

@ -25,8 +25,8 @@ All validators are provided via pure functions. They should be applied to the el
```html
<validatable-el
.errorValidators="${[[ myValidatorFunction, { myParam: 'foo' }, { extra: 'options' } ]]}">
</validatable-el>
.errorValidators="${[[ myValidatorFunction, { myParam: 'foo' }, { extra: 'options' } ]]}"
></validatable-el>
```
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
<validatable-el
.errorValidators="${[minLengthValidator({ min: 3 }), isZipCodeValidator()]}">
</validatable-el>
.errorValidators="${[minLengthValidator({ min: 3 }), isZipCodeValidator()]}"
></validatable-el>
```
### Default Validators
@ -112,8 +112,8 @@ The api for warning validators and info validators are as follows:
```html
<validatable-field
.warningValidators="${[myWarningValidator()]}"
.infoValidators="${[myInfoValidator()]}">
</validatable-field>
.infoValidators="${[myInfoValidator()]}"
></validatable-field>
```
### Success validators