# Widget Toolkit
## Application View Definition
Any application that can be loaded into the FIS3 UI is defined via an application definition in JSON format. The definition declares different aspects of an application, its name, its quick access parameters, validations, conditions and the view.
The view combines different widgets to form the user interface of the application. The declaration of an instance of a widget looks like the following:
```json
{
"component": "TextField",
"props": {
"id": "LocationTextField",
"label": "Location",
"valueBindingKey": "request.location",
"messageBindingKeys": ["request.location"]
},
"valueBindingKeyProps": { "valueBindingKey": "value" },
"conditionalProps": {},
"componentProps": [],
"children": []
},
```
This schema is similar to how a component is created in React. When an application is loaded React components are instantiated from those definitions. Usually only the children of a widget will be instantiated as React components, but some widgets do have properties that are actually React components, in this case they need some special treatment:
```json
{
"component": "Box",
"props": {
"id": "Box15",
"width": 45,
"contentOrientation": "horizontal",
"buttonAreaContent": [
{
"component": "Button",
"props": {
"id": "ApplyButton",
"label": "Filter",
"emitOnClick": true,
"hero": true
},
"conditionalProps": {},
"componentProps": [],
"children": []
}
]
},
"conditionalProps": {},
"componentProps": ["buttonAreaContent"],
"children": []
}
```
In addition there are two properties in the schema that are not strictly necessary, but help to improve performance by avoiding string manipulations: `conditionalProps` and `valueBindingKeyProps`.
The `valueBindingKeyProps` property describes how a `valueBindingKey` should be mapped to a specific property. Actually the key is not mapped, but the value retrieved with the key. If the widget has for instance a `startDateValueBindingKey` property, the `valueBindingKeyProps` property would contain the entry `{ "startDateValueBindingKey": "startDateValue" }`.
The `conditionalProps` property describes how a conditional property should be mapped to a non conditional property. The name of a conditional property always ends with an `If` and points to a condition. This condition is evaluated at runtime and the result is then passed to the component in the non conditional property. If the widget has for instance a `editableIf` property pointing to the condition `userIsAllowedToEdit`, the `conditionalProps` property would contain the entry `{ "editable": "userIsAllowedToEdit" }`.
## Widget Toolkit Language
It would be quite cumbersome to have to write such view definition by hand, without any helpfull hints or validations. Therefore the Application View Definition Language and the Widget Toolkit Language were invented. They are [domain-specific languages](https://en.wikipedia.org/wiki/Domain-specific_language) tailored for easy definition of application views and widgets.
The Application View Definition Language is used by an application developer to create the view of an application and its associated generator will generate such application view definitions as described above.
The Widget Toolkit Language provides the base information for the Application View Definition Language: The available widgets and how they can be combined to form the user interface of an application.
Besides structural elements like the type of a property, it also has validation elements, for instance if a property is mandatory or if a number property may contain negative numbers or how many children a widget might have.
The Widget Toolkit Language is design from the point of view of an application developer and not a React or frontend developer, because the application developer has to work with it when defining the view of an application.
### Widget Toolkit Definition
A file with the `.wtk` ending defines a widget toolkit. A toolkit mainly defines widgets and how they can be assembled to form a user interface. FIS3 widget toolkit is located in the [ideetools](https://gitlab.hlag.altemista.cloud/fis3/idetools) project in the [fis3.wtk](https://gitlab.hlag.altemista.cloud/fis3/idetools/-/blob/master/widgettoolkits/widgettoolkit-fis/src/main/java/com/hlag/fis/ide/widgettoolkit/fis/fis3.wtk) file. Whenever we implement a new widget or add some changes to an existing one, we must update this file accordingly. Also, our [widget toolkit reference](https://fis3doc.hlag.altemista.cloud/ui/widget-toolkit-reference/overview/widgettoolkit/) is generated based on it.
### Widgets
At least one widget in the toolkit needs to be marked as a root widget which is a widget that acts as a container for other widgets at the top level:
```java
rootWidget Application
```
Widgets can have children, but often a widget has special constraints as what widgets are allowed as children. Sometimes it may be only one or two very specific widgets, sometimes it may be a specific kind of widgets. For such cases the concept of groups exist:
```java
group ActionControl {
description: "This group contains widgets that trigger certain actions."
}
```
A widget always has a name, a description and at least one property. It can belong to one or more groups and groups can act as valid child types:
```java
widget Button {
description: "The Button widget lets the user take actions."
groups: ActionControl
boolean hero {
description: "If `true`, indicates the most important button within a tile."
defaultValue: false
}
...
}
```
Sometimes several widgets share a common set of properties, for instance the field widgets. It would be an error prone and tedious to have to repeat such properties in each widget definition. Therefore a widget can inherit properties from one or more widgets. It is possible to not inherit all properties, either because a property does not make sense in the context of the widget or an existing property should be overridden by another property with the same name.
A widget can be marked as an `abstract`:
```java
abstract widget Field {...}
```
Such a widget is only used in the Widget Toolkit Language to have a place where a set of shared properties can be defined. Such widgets are not usable in the Application View Definition and do not have any corresponding React component, but are used by other widgets in the toolkit to avoid code duplication:
```java
widget TextField extends Field {...}
```
### Properties
A property always has a description, it can have a default value (this is for documentation purpose only) and it can be mandatory. It is also possible to make the usage of a property dependent on the presensce or absence of other properties.
There are different kind of properties:
- const
- number (integer or decimal)
- boolean
- string
- combined
- child
- propchild
- reference
- Java feature reference
A property either represents a single value, multiple values or either a single value or multiple values. If a property is marked as `multiValue` it will always be generated as an array in the resulting view definition. If it is marked as `singleOrMultiValue`, it will only be generated as an array if it contains more than one value.
Widget properties can also use `prohibitedIfValueSetOfProperty` field to exclude each other. For example, we always want the widget to receive either non-conditional `editable` property or conditional `editableIf` property. The use of `prohibitedIfValueSetOfProperty` will make them mutually exclusive:
```java
boolean editable {
...,
prohibitedIfValueSetOfProperty: [editableIf]
}
javaFeatureReference editableIf {
...,
prohibitedIfValueSetOfProperty: [editable]
}
```
#### Const
The `const` property is used to define some constant value that is used internally in the corresponding React component:
```java
const name {
description: "The name of the action."
value: "fontSize"
}
```
#### Number
The `integer` or `decimal` property represents a number. The property has different kind of validation settings:
- if a negative value is allowed
- if a percent value is allowed
- if zero as a value is allowed
- a lower and upper bound
```java!
integer height {
description: "The height of the map. One unit of length is equal to the height of one field."
}
```
#### Boolean
The `boolean` property can either be `true` or `false`:
```java!
boolean alwaysUpperCase {
description: "If `true`, the value of the field is converted to uppercase."
defaultValue: true
}
```
#### String
The `string` property represents a string. The property can be marked as a`nativeLanguageSupport`, which means the application developer needs to provide values for it in different languages (currently only English):
```java!
string label nativeLanguageSupport {
description: "The visible text."
}
```
This is mainly used for labels and tooltips and does not apply to the frontend, because the frontend will always receive the translated value.
A `string` property can have permitted values:
```java!
string contentOrientation {
description: "Determines how to align the content."
defaultValue: "horizontal"
permittedValues: {
permittedValue: {
value: "horizontal"
description: "Aligns the content horizontally."
},
permittedValue: {
value: "vertical"
description: "Aligns the content vertically."
}
}
}
```
Sometimes permitted values are also used in the Java coding part of an application, such as for the color property of the `Icon` widget. In such a case Java enums are defined and it is possible to instruct a `string` property to load the permitted values from such an enum:
```java
string color {
description: "Sets the colour of the icon. This only works for material icons."
permittedValuesFromEnum: Color
}
```
It is also possible to allow non-permitted values:
```java
string name mandatory {
description: "The name of the icon to display. This can either be a constant of a FIS3 system icon or the name of a material icon (see https://fonts.google.com/icons?selected=Material+Icons)."
permittedValuesFromEnum: Icon
allowNonPermittedValues: true
}
```
#### Combined
A `combined` property is like an object in Javascript, it combines several properties:
```java!
combined onFocusLost {
description: "Defines what should happen when the field looses focus."
prohibitedIfValueSetOfProperty: onFocusLostHandler
javaFeatureReference handler {
description: "References the method of the controller that gets called."
javaFeatureType: method
anchor: controller
}
javaFeatureReference validateValues multiValue {
description: "The properties of the model that must be valid before the method of the controller is called."
javaFeatureType: field
anchor: model
}
}
```
#### Child and Propchild
The `child` and `propchild` properties can contain one or more widgets of a specific type:
```java!
child icon[Icon] {
description: "The icon used to describe the option."
}
```
```java!
propchild rowMenu [DropdownMenu] {
description: "Dropdown menu that contains actions for each row"
}
```
The difference between those two is, that a `child` property will result in React components in the frontend, whereas a `propchild` property will result in an object in the frontend that has the properties of the respective widget.
#### Reference
The `reference` property holds the reference to another widget. For instance, an application can have a default save button, therefore the `Application` widget has a `reference` property `defaultSaveButton`:
```java
reference defaultSaveButton [ActionControl] {
description: "References the button that should be clicked when the user presses the keyboard shortcut for save."
scope: Application
}
```
#### Java Feature Reference
The `javaFeatureReference` property is special, because it references a Java feature, such as a class or a method of a class. This is very far removed from the frontend way of thinking, but necessary for making the Java part of an application work.
Usually this kind of property is only used in three places:
- to link an application view to its model, controller and conditions class:
```java!
javaFeatureReference valueBinding {
description: "Binds to a property of the model that provides and receives the value of this field."
javaFeatureType: field
anchors: [model BoxList.valueBinding DataGrid.valueBinding]
assignableToJavaType: String
}
```
- for event handlers:
```java!
javaFeatureReference onClickHandler {
description: "References the method of the controller that gets called when the user clicks on the button."
javaFeatureType: method
anchor: controller
prohibitedIfValueSetOfProperty: onClick
}
```
- for conditional properties:
```java!
javaFeatureReference hiddenIf {
description: "Hides this element if the condition is evaluated to `true`."
javaFeatureType: method
anchor: fis3.conditions
}
```
The model, controller and conditions class references are used for making the live of an application developer easier, because the Application View Language can provide code completion and validation for other properties with this information.
The event handler and conditional properties could have been defined as `string` properties, but instead they reference a method in the controller class or the conditions class. The Application View Language can validate those values and also suggest valid values.
## Mapping Properties
Except for a few cases React component properties can be easily mapped to widget properties. Any special mappings will be described below. The generator of the Application View Definition Language will take care of converting those widget properties to the expected component properties.
### Identifier
Each widget when used in the application view will have a dedicated identifier. This identifier is mapped to the `id` property. Therefore a widget does not need to explicitly declare an `id` property.
### Values
A React component uses a `valueBindingKey` property to define a key under which to store and retrieve values from the model (Redux store). The naming of such properties is important. If a component needs for instance a `startDateValue`, it will need a `startDateValueBindingKey` property or to put it another way.
Whenever a component needs a value from the model, the corresponding `valueBindingKey` property will map to a `string` property named with the same prefix and `Value` at the end, so `valueBindingKey` maps to `value`, `tooltipValueBindingKey` maps to `tooltipValue`.
In the widget toolkit such properties do not end with a `Key` and they are not `string` properties, but `javaFeatureReference` properties and they have to specify valid Java types that must map to primitive values. Those properties are named for instance `valueBinding` or `tooltipValueBinding`.
If a component has a property that can either be set dynamically from the model or statically, the corresponding widget should define two properties, such as a `tooltip` `string` property and a `tooltipValueBinding` `javaFeatureReference` property. The widget should not have a property `tooltipValue`. This needs to be reflected in the component also as two properties, such as `tooltip` and `tooltipValue`.
More information about value binding can also be found [here](https://gitlab.hlag.altemista.cloud/fis3/commons-ui/commons-ui-frontend/-/blob/master/docs/development/value-binding.md).
### Conditional Properties
Conditional properties are a concept that most of the time is shielded from a React component. The name of a conditional property always ends with `If` and will be passed without the `If` to the component, for instance `editableIf` will be passed as `editable`. This concept is fully described in separate [Conditionals](https://hackmd.io/YLgVUpVXRwWTm8dY-4HJUg?both#-conditionalsmd-file) document.
A conditional property which is a `string` property in a component, needs to be a `javaFeatureReference` property in a widget, referencing a Java method of a conditions class:
```java!
javaFeatureReference editableIf {
description: "References a condition. If the condition evalutes to `false`, the field is read-only, the user cannot edit its content."
javaFeatureType: method
anchor: conditions
prohibitedIfValueSetOfProperty: [editable]
}
```
Sometimes a component does have a property that can be set to a fixed value or can be set conditionally during runtime, a good example would be the `editable` property. A component might only declare an `editable` `boolean` property because it does not care whether that property has been set at compile time or conditionally during runtime. The corresponding widget definition on the other hand, must care. So it requires one of two properties: an `editable` `boolean` property or an `editableIf` `javaFeatureReference` property.
If a property should only be a conditional property, the component would again only have for instance a `hidden` `boolean` property and now the widget would also have only one property, a `hiddenIf` `javaFeatureReference` property.
Another special case is setting the value of a property conditionally where the value is not a boolean, e.g. the name of an icon. In this case the widget needs to declare a `combined` property, with two properties, an `if` `javaFeatureReference` property and a `value` property. The component still declares one property with the desired type, such as `string` for the name of an icon.
### Event Properties
A component can have `boolean` properties which tell the component to send specific events, such as `emitOnClick`. Those properties are mapped to the so called handler properties in the corresponding widget, because those properties specify the controller method that should handle the event. The naming convention is easy, for instance an `emitOnClick` `boolean` property maps to a `onClickHandler` `javaFeatureReference` property.
Sometimes more needs to happen then just sending an event, such as asking the user if modified values should be saved or validate values before sending an event. Again following the naming convention a component will have additional properties, such as a `confirmSaveOnClick` `boolean` property or a `validateValuesOnClick` `array` `string` property.
In this case the widget needs a combined property called for instance `onClick`, which needs to have a `handler` `javaFeatureReference` property and optionally for instance a `confirmSave` `boolean` property or a `validateValues` `multivalue` `string` property:
```java
combined onClick {
description: "Defines what should happen when the user clicks on the button."
prohibitedIfValueSetOfProperty: onClickHandler
javaFeatureReference handler {
description: "References the method of the controller that gets called."
javaFeatureType: method
anchor: controller
}
javaFeatureReference validateValues multiValue {
description: "The properties of the model that must be valid before the method of the controller is called."
javaFeatureType: field
anchor: model
}
boolean confirmSave {
description: "If `true`, will ask the user if changes should be saved or discarded."
defaultValue: false
}
}
```
### Value Validations
There exists one special property `valueValidations`, which is used in field widgets. It defines conditions that when evaluated determine if a field contains a valid value or not.
```java
combined valueValidations multiValue {
description: "Defines how the value should be validated."
javaFeatureReference failIf {
description: "References a condition that fails the validation if it evaluates to `true`."
javaFeatureType: method
anchor: conditions
prohibitedIfValueSetOfProperty: failIfNot
}
javaFeatureReference failIfNot {
description: "References a condition that fails the validation if it evaluates to `false`."
javaFeatureType: method
anchor: conditions
prohibitedIfValueSetOfProperty: failIf
}
boolean contentValidation {
description: "If set to `true,` will show the error message when the field looses focus."
defaultValue: false
}
}
```
In the widget toolkit this property is a `multivalue` `combined` property. The generator of the Application View Definition Language will split this property into a property attached to the widget that follows the naming convention for value bindings and an entry in the global `validations` map at the root level of the application definition file.
So if a component has a `valueBindingKey` and the application developer should be able to write validations for that value, the widget needs a `valueValidations` `multivalue` `combined` property. For a `startDateValueBindingKey` that property would need to be named `startDateValueValidations`.
The combined property either contains a `failIf` or a `failIfNot` `javaFeatureReference` property, that references the condition that should be evaluated. Additionally, an `error` `string` property for the error message needs to be defined, and a `contentValidation` `boolean` property. The `contentValidation` `boolean` property determines when the validation should happen, when the user types a value (`true`) or when an event is fired (`false` default).