Java
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 |
---|---|---|
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
onHorizontalLayout
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 withWebElement#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 newlinkki-search-vaadin-flow
module. The maven dependency tolinkki-f10-search
must be replaced by dependency tolinkki-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 tolinkki-core-vaadin-flow
,linkki-application-framework-vaadin-flow
orlinkki-vaadin-flow-component
depending on the functionality. -
Most functionalities of
CommonApplicationHeader
are incorporated intoApplicationHeader
. Functionalities that are related to the user are implemented in the newly introducedUserAwareApplicationHeader
with the same API. -
UIMenuList
andUIMenuButton
now have the default valueDERIVED_BY_LINKKI
for caption. Therefore, the attribute caption is not mandatory anymore. Existing declarations that useDERIVED_BY_LINKKI
can be removed. -
The
NavigationWorkaround
is not needed anymore as the underlying Vaadin bug is fixed.
Migration Guide
-
Include dependency to
org.linkki-framework:linkki-f10
if it is not used already and use the version2.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. -
Include new dependencies if necessary:
-
If
linkki-f10-search
was used as dependency, replace the dependency with linkki-search-vaadin-flow in version2.8.0
. -
Add dependency to
linkki-application-framework-vaadin-flow
if it was not used before and either any of the classesInfoTool
,InfoToolsComponent
,HasBrowserConfirmation
were used, orCommonsApplicationHeader
was used with the user menu.
-
-
Adjust to API changes:
-
Replace usages of
NavigationWorkaround
byUI.getCurrent().navigate()
. The class is not needed anymore as the underlying Vaadin bug is fixed. -
Replace
CommonsApplicationHeader
: if the user menu is not used, useApplicationHeader
directly instead. Otherwise, useUserAwareApplicationHeader
. Existing declarations that useDERIVED_BY_LINKKI
can be removed.
-
-
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.
-
-
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
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 |
de.faktorzehn.commons.linkki.CommonApplicationHeader |
|
de.faktorzehn.commons.linkki.infotool.InfoTool |
|
de.faktorzehn.commons.linkki.infotool.InfoToolsComponent |
|
de.faktorzehn.commons.linkki.ui.confirm.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 |
|
de.faktorzehn.commons.linkki.search.annotation.BindSlot (deprecated in a previous release, now removed) |
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 underlyingBindingContext
after the OK button was clicked.
The new UI annotation @UIOpenDialogButton resolves this inconvenience. The annotated method returns aDialogPmo
that is used to create the dialog that is opened upon button click.
For use with a Faktor-IPS model object, the abstract classIpsDialogPmo
provides a partial implementation ofDialogPmo
. When the user clicks on the OK button, the underlyingBindingContext
is updated automatically.
Alternatively, aFunction
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 aCompletableFuture
. 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 attributecontent-loading
is set. -
The
has-errors
attribute has been renamed tohas-loading-error
. -
The CSS property
error-messages
has been renamed toloading-error-message
. -
@UILabel
components in a loading state no longer have the CSS classloading
.
Custom CSS selectors that utilize the above attributes / CSS Properties must be adjusted accordingly.
-
Testing
-
Conditions for AssertJ
The utility classComponentConditions
providing UI specific conditions for AssertJ has been added tolinkki-vaadin-flow-test
to support writing more readable assertions such asassertThat(component).is(childOf(layout))
orassertThat(component).has(anyChildrenSatisfying(…))
. -
Component Tree Representation
When using AssertJ, the object that is tested is displayed withtoString()
in case of failure. In case of aComponent
, it is often not immediately clear how the component actually looks like. The new classComponentTreeRepresentation
can be used withAssertions#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
andgetComponentTree
also print out all table rows. -
KaribuUtils.Grids.getTextContentInColumn
also returns the value of an input element such as text field. The method also supportsTreeGrid
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 inKaribuUtils.ComboBoxes
are renamed to make them more consistent to methods inFields
:-
getComboBoxWithId
→getWithId
-
setComboBoxValue
→setValue
-
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 fromlinkki-core-nestedcomponent-vaadin-flow
tolinkki-core-vaadin-flow
. The modulelinkki-core-nestedcomponent-vaadin-flow
has been removed. -
New theme variant for form item label alignment
The new theme variantform-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 classflex-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 constantLinkkiTheme.Flex.BASIS_NONE
.
Bugfixes
-
SimpleItemSupplier
now throws aNullPointerException
if the model object to PMO mapping supplier returnsnull
. -
linkki-vaadin-flow-test
no longer includesvaadin-dev
, andlinkki-vaadin-flow-testbench-extension
now marksvaadin-core
as provided. This avoids unwanted dependencies in production.