ui 5 release
We've recently released @dhis2/ui
version 5. It unifies ui-core
, ui-widgets
and ui-forms
to simplify the user experience and allow for some architectural changes. In this post we'll go through the most important changes to try and help you with the upgrading process. To view a complete list of all the changes see the changelog.
For upgrading to the latest version of @dhis2/ui
we recommend to not mix the upgrade changes with other changes in your codebase. To keep the process manageable, branch off of your production branch (commonly master
) then upgrade and merge back your changes. A common best practice for all changes to a codebase of course.
If there's anything you've missed in this post, or encountered whilst upgrading, please let us know and we'll add it.
#
Import styleSince @dhis2/ui
now bundles all our ui libraries, you can now import everything that you imported previously from ui-core
, ui-widgets
and ui-forms
directly from @dhis2/ui
. An example:
The original libraries are still published alongside @dhis2/ui
, so if you want you can still import from the underlying libraries. We do recommend that you use @dhis2/ui
though, as that way you don't have to know which underlying library the component is exported from.
#
NoticeboxWe've added a notice box component. A notice box highlights useful information that is directly relevant to the page the user is viewing. It is meant to be used wherever there is important, temporary information about a page or situation that the user needs to be aware of.
See the design specs for more information about the component. As an example, this is how you could use the NoticeBox to display a warning to a user:
#
Specifying selected optionsTo simplify specifying selected options with our components, we've moved to a slightly simpler API. Instead of having to pass objects, or an array of objects with value
and label
, now only a value
string or an array of value
strings is sufficient. This change affects the Transfer
, the SingleSelect
and the MultiSelect
.
#
TransferAs noted above, instead of accepting an array of objects for the selected
prop, the Transfer
now expects an array of strings. The strings supplied to the selected
prop should match the value
prop of a supplied option. So as an illustration:
#
SingleSelect and MultiSelectJust like the Transfer
, the SingleSelect
and MultiSelect
now also expect strings instead of objects for the selected
prop. To illustrate:
Note that the SingleSelect
expects a single string, and the MultiSelect
an array of strings.
This change also means that the Selects can no longer distinguish between options with identical values, even if the labels are different, as they now only have the values to compare by. Make sure that this is not a problem in your application before using the new Selects.
#
Transfer optionsThe Transfer now expects options to be passed to an options
prop instead of as children. It is our intention to eventually move all our components to this API, as it allows for far simpler internals. An example:
If you want to use a custom option component, you can still do so. The Transfer
accepts a renderOption
prop, where you can supply a callback that returns the markup for a custom option. This pattern is called the render prop pattern, see the React docs for further information. An illustration of how this works:
#
Layering componentsWe've changed the set of components used to produce various types of overlays. The underlying logic has been improved and we've clarified the scope of certain components that did a bit too much.
More specifically, Layer
and CenteredContent
have been introduced to replace the Backdrop
and ScreenCover
and the API for the ComponentCover
component has been aligned with the Layer
.
#
LayerLayer
is an overlay component that fills the entire viewport and allows you to stack various components on top of one another. The Layer
accepts an onClick
callback, so you can catch clicks on the background of whatever you're rendering. It also has a translucent
prop, if you want the Layer
to darken the background slightly (the default is fully transparent). An example:
The Layer
uses React context internally to control the stacking logic. This context has been exposed via the useLayerContext
hook, which can be used to append portals to the current layer-node, for advanced users.
#
ComponentCoverThe ComponentCover
is similar to the Layer
, except ComponentCover
only fills its parent, provided that the parent is positioned (so has a relative
,absolute
, fixed
or sticky
position). The ComponentCover
also accepts an onClick
and translucent
prop, just like the Layer
. An example of the ComponentCover
:
{% raw %}
{% endraw %}
#
CenteredContentCenteredContent
is a component that centers its children. It has a position
prop which can be used to vertically align the children at the top
, middle
(default), or bottom
. It can be useful when you want to render a loading spinner on top of your app for example:
#
Click based MenuThe Menu
has been refactored to make it easier to use. It is now click-based instead of hover-based, so sub-menus will stay open even if the user is no longer hovering over them.
#
Renamed componentsThe MenuList
has been renamed to Menu
, this component will expand to fill the full width of the parent container and is not meant to contain submenus. It is a good fit for a sidebar menu for example.
The original Menu
component has been renamed to FlyoutMenu
, which will render a menu in a Card
and allows for submenus. An example of both:
#
MenuDivider and MenuSectionHeaderWe've also introduced two new components, the MenuDivider
and MenuSectionHeader
. As you would expect, the MenuDivider
renders a divider between MenuItems
. The MenuSectionHeader
can be used to render a header in between menu-items. For example:
#
SubmenusFinally, to create sub-menus, you can now add MenuItems directly under a parent MenuItem, there's no need to wrap them in another component anymore:
#
Form components#
Final-form enabled components suffixIf you were using our final-form
enabled components from @dhis2/ui-forms
you'll probably know that they did not have a suffix. They were just exported as Checkbox
, Radio
, etc. However, those names collided with our regular form fields in @dhis2/ui
.
To remedy that, we've now suffixed all our final-form
enabled components with FieldFF
. The Field suffix indicates the relation with our regular Field components and the FF stands for final-form
. Note that this is only important for you if you were using the final-form
enabled components, our regular form components are unaffected! So, for example:
#
Scoped react-final-form and final-form exportsAs you can see in the example above, the re-export style for react-final-form
and final-form
has changed. With @dhis2/ui-forms
we re-exported the react-final-form
and final-form
exports directly. We still re-export these libraries with @dhis2/ui
, but we've scoped the exports to the ReactFinalForm
and FinalForm
named exports. To clarify:
#
Explicit api for final-form enabled toggle componentsWe've modified our toggle components (SwitchFieldFF
, CheckboxFieldFF
, and RadioFieldFF
) to use an API that aligns more closely with the react-final-form
API. This means that when using one of these toggle components, you have to provide a type
attribute, to signal the type of field to react-final-form
:
Because of this change, our components will now behave like explained in the react-final-form docs (see the type
section):
If set to "checkbox" or "radio", React Final Form will know to manage your values as a checkbox or radio button respectively. Results in a checked boolean inside the input value given to your render prop.
#
Generic Field componentTo simplify the creation of custom form fields, our Field
component has been modified. The Field
can now be used to wrap a custom form field and will allow displaying a label, helptext and validation messages in the same style as our other form components. So for example, say that you have a custom input
field, and you want to use it with our Field
:
Note that the Field
we're covering in this section, the one exported directly from @dhis2/ui
, is different from the Field
from react-final-form
. The latter exists for integration with final-form
, whereas the Field
exported directly from @dhis2/ui
is meant to help align styles for custom form fields with our other form components.
#
Removal of grouping componentsWe've removed the grouping components for our toggle components: RadioGroup
, RadioGroupField
, CheckboxGroup
, and CheckboxGroupField
. These components could be used to wrap our toggle components and would add the necessary props and event handlers by cloning their children. We've removed them because these components were blocking a generic Field
and because we're trying to move to more explicit patterns, to improve readability and maintainability. A simplified example:
#
New grouping componentsInstead of the grouping components mentioned above, we've introduced simplified grouping components that don't modify their children via cloning: FieldSet
, FieldGroup
and FieldGroupFF
.
#
FieldSetThe FieldSet
is the most basic of the three, it can be used if you want to group several related controls as well as labels in a form (see the MDN docs on fieldset). An example:
#
FieldGroupThe FieldGroup
wraps your controls in a FieldSet
as well as a Field
. It can be used in the same manner as our regular Field
component, but for a group of controls. For example:
#
FieldGroupFFThe FieldGroupFF
does the same as the FieldGroup
, but also connects to final-form
, and displays error state and error messages from the underlying fields automatically. Just like our other final-form
connected components. An example:
#
TranslationsWe've added translations to the following widgets: FileInputField
, SingleSelectField
, MultiSelectField
. We'll be adding translations to all components in widgets, so that you're not required to add these yourself every time you use them.
#
Other notable changesPopover
:onBackdropClick
has been renamed toonClickOutside
Constrictor
: has been renamed toBox
since it now does more than just restrict sizes