Release Notes

Version 2.8.0

The release notes contain changes compared to version 2.7.1.

Version Updates

The following versions have been updated:

Dependency New version Previous version

Java

21

17

Faktor-IPS

25.7.0.release (Release Notes)

25.1.1.release

Vaadin

24.7.5 (Release Notes)

24.5.12

Spring Boot compatibility tested for

3.5.2 (Release Notes)

3.4.2

The Vaadin version update brings some changes that may affect linkki applications:

  • The supported Node version is changed from 18 to 20.

  • New theme wrap on HorizontalLayout allowing elements to move to the next line, if there’s no space left.

  • Testbench: The method WebElement#getAttribute is deprecated and should be replaced with WebElement#getDomAttribute.

Integration of linkki-f10 functionalities into linkki

linkki-f10 is a library created by Faktor Zehn that provided additional linkki functionalities that are commonly used in Faktor Zehn products, but are not open source. With this version, classes in linkki-f10 are fully integrated into linkki, making the code and documentation available at one place.

As part of this transition, all classes in the linkki-f10 module have been deprecated and replaced by corresponding functionality in the other modules of linkki.

In addition to the changes in code, the documentation is now also moved to according sections at doc.linkki-framework.org. During this process, the documentation of the affected components has been reworked.

Migration

This change requires action from all users of linkki-f10 or linkki-f10-search. The migration mainly consists of package renamings in imports.

Most notable changes:

  • Functionalities previously contained in the linkki-f10-search module have been moved to the new linkki-search-vaadin-flow module. The maven dependency to linkki-f10-search must be replaced by dependency to linkki-search-vaadin-flow. The documentation is moved to section "Search" in chapter "UI Components".

  • Maven dependency for linkki-f10 is no longer available in newer version. Classes are moved to linkki-core-vaadin-flow, linkki-application-framework-vaadin-flow or linkki-vaadin-flow-component depending on the functionality.

  • Most functionalities of CommonApplicationHeader are incorporated into ApplicationHeader. Functionalities that are related to the user are implemented in the newly introduced UserAwareApplicationHeader with the same API.

  • UIMenuList and UIMenuButton now have the default value DERIVED_BY_LINKKI for caption. Therefore, the attribute caption is not mandatory anymore. Existing declarations that use DERIVED_BY_LINKKI can be removed.

  • The NavigationWorkaround is not needed anymore as the underlying Vaadin bug is fixed.

Migration Guide
  1. Include dependency to org.linkki-framework:linkki-f10 if it is not used already and use the version 2.8.0.
    The project should compile successfully at this point as all existing classes are available as-is with this dependency. The existing classes are deprecated and are documented with steps to fix the deprecation warning for each class.

  2. Include new dependencies if necessary:

    • If linkki-f10-search was used as dependency, replace the dependency with linkki-search-vaadin-flow in version 2.8.0.

    • Add dependency to linkki-application-framework-vaadin-flow if it was not used before and either any of the classes InfoTool, InfoToolsComponent, HasBrowserConfirmation were used, or CommonsApplicationHeader was used with the user menu.

  3. Adjust to API changes:

    • Replace usages of NavigationWorkaround by UI.getCurrent().navigate(). The class is not needed anymore as the underlying Vaadin bug is fixed.

    • Replace CommonsApplicationHeader: if the user menu is not used, use ApplicationHeader directly instead. Otherwise, use UserAwareApplicationHeader. Existing declarations that use DERIVED_BY_LINKKI can be removed.

  4. Adjust package imports:

    • If the IDE is capable of adding unambiguous missing imports for the whole project (e.g. Eclipse): remove dependency to linkki-f10 and reorganize imports.

    • Alternatively, the imports can be adjusted by using the python script provided below by executing the script in the working directory of the project.

  5. Make sure that dependency to linkki-f10 is removed.

Python migration script
import os

# Mapping of old imports to new imports
import_mapping = {
    "de.faktorzehn.commons.linkki.F10ProductTheme": "org.linkki.core.ui.theme.F10ProductTheme",
    "de.faktorzehn.commons.linkki.board.BoardComponent": "org.linkki.core.vaadin.component.board.BoardComponent",
    "de.faktorzehn.commons.linkki.board.BoardLayout": "org.linkki.core.vaadin.component.board.BoardLayout",
    "de.faktorzehn.commons.linkki.ui.menu.UIMenuButton": "org.linkki.core.ui.element.annotation.UIMenuButton",
    "de.faktorzehn.commons.linkki.ui.menu.MenuButtonInvokeAspectDefinition": "org.linkki.core.ui.element.annotation.UIMenuButton.MenuButtonInvokeAspectDefinition",
    "de.faktorzehn.commons.linkki.ui.menu.UIMenuList": "org.linkki.core.ui.element.annotation.UIMenuList",
    "de.faktorzehn.commons.linkki.ui.menu.MenuItemsAspectDefinition": "org.linkki.core.ui.element.annotation.UIMenuList.MenuItemsAspectDefinition",
    "de.faktorzehn.commons.linkki.ui.table.HierarchicalTableUtil": "org.linkki.core.ui.table.util.HierarchicalTableUtil",
    "de.faktorzehn.commons.linkki.CommonApplicationHeader": "org.linkki.framework.ui.application.UserAwareApplicationHeader",
    "de.faktorzehn.commons.linkki.infotool.InfoTool": "org.linkki.framework.ui.component.infotool.InfoTool",
    "de.faktorzehn.commons.linkki.infotool.InfoToolsComponent": "org.linkki.framework.ui.component.infotool.InfoToolsComponent",
    "de.faktorzehn.commons.linkki.ui.confirm.HasBrowserConfirmation": "org.linkki.framework.ui.HasBrowserConfirmation",
    "de.faktorzehn.commons.linkki.ui.menu.SingleItemMenuBar": "org.linkki.core.vaadin.component.menu.SingleItemMenuBar",
    "de.faktorzehn.commons.linkki.ui.menu.MenuItemDefinition": "org.linkki.core.vaadin.component.menu.MenuItemDefinition",
    "de.faktorzehn.commons.linkki.search.annotation.UISearchLayoutHeadline": "org.linkki.framework.ui.component.UIHeadline",
    "de.faktorzehn.commons.linkki.search.annotation.BindSlot": "org.linkki.core.ui.aspects.annotation.BindSlot",
    "de.faktorzehn.commons.linkki.search.annotation.NestedPmoMethodLayoutDefinitionCreator": "org.linkki.search.annotation.NestedPmoMethodLayoutDefinitionCreator",
    "de.faktorzehn.commons.linkki.search.annotation.SearchInputLayoutDefinitionCreator": "org.linkki.search.annotation.SearchInputLayoutDefinitionCreator",
    "de.faktorzehn.commons.linkki.search.annotation.UISearchCriteriaGroup": "org.linkki.search.annotation.UISearchCriteriaGroup",
    "de.faktorzehn.commons.linkki.search.annotation.UISearchInputLayout": "org.linkki.search.annotation.UISearchInputLayout",
    "de.faktorzehn.commons.linkki.search.annotation.UISearchLayout": "org.linkki.search.annotation.UISearchLayout",
    "de.faktorzehn.commons.linkki.search.annotation.UISearchParameters": "org.linkki.search.annotation.UISearchParameters",
    "de.faktorzehn.commons.linkki.search.annotation.UISearchResultAction": "org.linkki.search.annotation.UISearchResultAction",
    "de.faktorzehn.commons.linkki.search.annotation.UISearchResultLayout": "org.linkki.search.annotation.UISearchResultLayout",
    "de.faktorzehn.commons.linkki.search.annotation.UISearchTable": "org.linkki.search.annotation.UISearchTable",
    "de.faktorzehn.commons.linkki.search.component.SearchCriteriaGroup": "org.linkki.search.component.SearchCriteriaGroup",
    "de.faktorzehn.commons.linkki.search.component.SearchInputLayout": "org.linkki.search.component.SearchInputLayout",
    "de.faktorzehn.commons.linkki.search.component.SearchLayout": "org.linkki.search.component.SearchLayout",
    "de.faktorzehn.commons.linkki.search.model.RoutingSearchController": "org.linkki.search.model.RoutingSearchController",
    "de.faktorzehn.commons.linkki.search.model.SearchController": "org.linkki.search.model.SearchController",
    "de.faktorzehn.commons.linkki.search.model.SearchParameterMapper": "org.linkki.search.model.SearchParameterMapper",
    "de.faktorzehn.commons.linkki.search.model.SimpleSearchController": "org.linkki.search.model.SimpleSearchController",
    "de.faktorzehn.commons.linkki.search.pmo.SearchButtonsPmo": "org.linkki.search.pmo.SearchButtonsPmo",
    "de.faktorzehn.commons.linkki.search.pmo.SearchInputPmo": "org.linkki.search.pmo.SearchInputPmo",
    "de.faktorzehn.commons.linkki.search.pmo.SearchLayoutPmo": "org.linkki.search.pmo.SearchLayoutPmo",
    "de.faktorzehn.commons.linkki.search.pmo.SearchResultPmo": "org.linkki.search.pmo.SearchResultPmo",
    "de.faktorzehn.commons.linkki.search.pmo.SearchResultTablePmo": "org.linkki.search.pmo.SearchResultTablePmo",
    "de.faktorzehn.commons.linkki.search.util.NlsSearch": "org.linkki.search.util.NlsSearch",
    "de.faktorzehn.commons.linkki.search.util.ParamsUtil": "org.linkki.search.util.ParamsUtil",
    "de.faktorzehn.commons.linkki.search.SearchLayoutBuilder": "org.linkki.search.SearchLayoutBuilder"
}

def replace_imports_in_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        content = file.read()

    for old_import, new_import in import_mapping.items():
        content = content.replace(old_import, new_import)

    with open(file_path, 'w', encoding='utf-8') as file:
        file.write(content)

def process_directory(directory):
    for root, _, files in os.walk(directory):
        for file in files:
            if file.endswith('.java'):  # Assuming Java files
                file_path = os.path.join(root, file)
                replace_imports_in_file(file_path)

# Get the current working directory
current_directory = os.getcwd()
process_directory(current_directory)
Full list of all changed imports
Table 1. linkki-f10 classes that are moved to linkki-core-vaadin-flow

de.faktorzehn.commons.linkki.F10ProductTheme

de.faktorzehn.commons.linkki.board.BoardComponent

de.faktorzehn.commons.linkki.board.BoardLayout

de.faktorzehn.commons.linkki.ui.menu.UIMenuButton

de.faktorzehn.commons.linkki.ui.menu.MenuButtonInvokeAspectDefinition

org.linkki.core.ui.element.annotation.UIMenuButton.MenuButtonInvokeAspectDefinition

de.faktorzehn.commons.linkki.ui.menu.UIMenuList

de.faktorzehn.commons.linkki.ui.menu.MenuItemsAspectDefinition

org.linkki.core.ui.element.annotation.UIMenuList.MenuItemsAspectDefinition

de.faktorzehn.commons.linkki.ui.table.HierarchicalTableUtil

Table 2. linkki-f10 classes that are moved to linkki-application-framework-vaadin-flow

de.faktorzehn.commons.linkki.CommonApplicationHeader

de.faktorzehn.commons.linkki.infotool.InfoTool

de.faktorzehn.commons.linkki.infotool.InfoToolsComponent

de.faktorzehn.commons.linkki.ui.confirm.HasBrowserConfirmation

Table 3. linkki-f10 classes that are moved to linkki-vaadin-flow-component

de.faktorzehn.commons.linkki.ui.menu.SingleItemMenuBar

org.linkki.core.vaadin.component.menu.SingleItemMenuBar

de.faktorzehn.commons.linkki.ui.menu.MenuItemDefinition

org.linkki.core.vaadin.component.menu.MenuItemDefinition

Table 4. linkki-f10-search classes that are moved to linkki-application-framework-vaadin-flow and linkki-core-vaadin-flow

de.faktorzehn.commons.linkki.search.annotation.UISearchLayoutHeadline

de.faktorzehn.commons.linkki.search.annotation.BindSlot (deprecated in a previous release, now removed)

Table 5. linkki-f10-search classes that are moved to linkki-search-vaadin-flow

de.faktorzehn.commons.linkki.search.annotation.NestedPmoMethodLayoutDefinitionCreator

org.linkki.search.annotation.NestedPmoMethodLayoutDefinitionCreator

de.faktorzehn.commons.linkki.search.annotation.SearchInputLayoutDefinitionCreator

org.linkki.search.annotation.SearchInputLayoutDefinitionCreator

de.faktorzehn.commons.linkki.search.annotation.UISearchCriteriaGroup

org.linkki.search.annotation.UISearchCriteriaGroup

de.faktorzehn.commons.linkki.search.annotation.UISearchInputLayout

org.linkki.search.annotation.UISearchInputLayout

de.faktorzehn.commons.linkki.search.annotation.UISearchLayout

org.linkki.search.annotation.UISearchLayout

de.faktorzehn.commons.linkki.search.annotation.UISearchParameters

org.linkki.search.annotation.UISearchParameters

de.faktorzehn.commons.linkki.search.annotation.UISearchResultAction

de.faktorzehn.commons.linkki.search.annotation.UISearchResultLayout

org.linkki.search.annotation.UISearchResultLayout

de.faktorzehn.commons.linkki.search.annotation.UISearchTable

org.linkki.search.annotation.UISearchTable

de.faktorzehn.commons.linkki.search.component.SearchCriteriaGroup

org.linkki.search.component.SearchCriteriaGroup

de.faktorzehn.commons.linkki.search.component.SearchInputLayout

org.linkki.search.component.SearchInputLayout

de.faktorzehn.commons.linkki.search.component.SearchLayout

org.linkki.search.component.SearchLayout

de.faktorzehn.commons.linkki.search.model.RoutingSearchController

de.faktorzehn.commons.linkki.search.model.SearchController

de.faktorzehn.commons.linkki.search.model.SearchParameterMapper

de.faktorzehn.commons.linkki.search.model.SimpleSearchController

de.faktorzehn.commons.linkki.search.pmo.SearchButtonsPmo

org.linkki.search.pmo.SearchButtonsPmo

de.faktorzehn.commons.linkki.search.pmo.SearchInputPmo

org.linkki.search.pmo.SearchInputPmo

de.faktorzehn.commons.linkki.search.pmo.SearchLayoutPmo

de.faktorzehn.commons.linkki.search.pmo.SearchResultPmo

org.linkki.search.pmo.SearchResultPmo

de.faktorzehn.commons.linkki.search.pmo.SearchResultTablePmo

org.linkki.search.pmo.SearchResultTablePmo

de.faktorzehn.commons.linkki.search.util.NlsSearch

org.linkki.search.util.NlsSearch

de.faktorzehn.commons.linkki.search.util.ParamsUtil

de.faktorzehn.commons.linkki.search.SearchLayoutBuilder

UI Annotations and Aspects

  • UIHeadline
    A new UI component, @UIHeadline, is now available, providing a way to easily create headlines that only display text.

  • UIOpenDialogButton
    Sometimes, a value or a grid item should not be edited directly. Instead, a dialog should be opened upon a button click in which the user can edit the value in a more complex form. To implement this use case, BindingContext#modelChanged often needs to be passed to the PMO to update the underlying BindingContext after the OK button was clicked.
    The new UI annotation @UIOpenDialogButton resolves this inconvenience. The annotated method returns a DialogPmo that is used to create the dialog that is opened upon button click.
    For use with a Faktor-IPS model object, the abstract class IpsDialogPmo provides a partial implementation of DialogPmo. When the user clicks on the OK button, the underlying BindingContext is updated automatically.
    Alternatively, a Function can be returned that defines how to create a dialog that applies the given handler additionally on OK click. This can be used for use cases where the dialog content cannot be defined as a PMO.

    Note that this annotation is an experimental feature that may be subject of API change in the near future.

  • UIMenuList and UIButton
    New UI components, @UIMenuList and @UIMenuButton, are now available, providing a way to create buttons in menus that can expand a menu list or execute an action, while maintaining a consistent look and feel.

  • BindComboBoxItemStyle
    @BindComboBoxItemStyle can now be used together with @UIMultiSelect.

  • Custom aspects with asynchronously loaded values
    It is now possible to create custom aspects that load values asynchronously with a CompletableFuture. The documentation for creating custom aspects is extended with a how-to guide for creating custom future aware aspects.

    Additionally, the frontend behaviors of @UILabel and @UITableComponent considering loading have been unified. This leads to following breaking changes in the frontend:

    • During loading, the items-loading (UITableComponent) / value-loading (UILabel) attributes are no longer set. Instead, the attribute content-loading is set.

    • The has-errors attribute has been renamed to has-loading-error.

    • The CSS property error-messages has been renamed to loading-error-message.

    • @UILabel components in a loading state no longer have the CSS class loading.

    Custom CSS selectors that utilize the above attributes / CSS Properties must be adjusted accordingly.

Testing

  • Conditions for AssertJ
    The utility class ComponentConditions providing UI specific conditions for AssertJ has been added to linkki-vaadin-flow-test to support writing more readable assertions such as assertThat(component).is(childOf(layout)) or assertThat(component).has(anyChildrenSatisfying(…​)).

  • Component Tree Representation
    When using AssertJ, the object that is tested is displayed with toString() in case of failure. In case of a Component, it is often not immediately clear how the component actually looks like. The new class ComponentTreeRepresentation can be used with Assertions#withRepresentation to display the component with the complete component tree in case of failure.

  • Added Functionalities in KaribuUtils

    • KaribuUtils#getTextContent now also returns the text content of layout elements.

    • The component tree can now be returned without printing it using KaribuUtils#getComponentTree.

    • printComponentTree and getComponentTree also print out all table rows.

    • KaribuUtils.Grids.getTextContentInColumn also returns the value of an input element such as text field. The method also supports TreeGrid now, but only displays root items.

    • Added KaribuUtils.ComboBoxes#setValueByLabel to set the value within a combobox based on a label.

    • Added KaribuUtils.Dialogs#getFirstMessage to get the component of the first validation message in a OkCancelDialog.

  • Method renaming within KaribuUtils.ComboBoxes
    Methods in KaribuUtils.ComboBoxes are renamed to make them more consistent to methods in Fields:

    • getComboBoxWithIdgetWithId

    • setComboBoxValuesetValue

Other Improvements

  • New documentation chapter - How-To
    Introduced a How-To chapter containing coding examples and best practises.

  • Relocation of UINestedComponent
    UINestedComponent has been moved from linkki-core-nestedcomponent-vaadin-flow to linkki-core-vaadin-flow. The module linkki-core-nestedcomponent-vaadin-flow has been removed.

  • New theme variant for form item label alignment
    The new theme variant form-item-label-start can be used to create sections with left aligned labels on form items. The variant can be applied to a single layout using @BindVariantNames.

  • New Utility CSS class for nested flex items
    The class flex-basis-0 sets the initial size of flex items to zero, allowing flex items to grow based only on the available space. It is also available as the constant LinkkiTheme.Flex.BASIS_NONE.

Bugfixes

  • SimpleItemSupplier now throws a NullPointerException if the model object to PMO mapping supplier returns null.

  • linkki-vaadin-flow-test no longer includes vaadin-dev, and linkki-vaadin-flow-testbench-extension now marks vaadin-core as provided. This avoids unwanted dependencies in production.