What's New in v6.2?
It has been over three months since the first final version of the Foundry release has been made public. Today, I am glad to announce the release of the 6.2 version! This new version focuses on two areas of improvements:
- Accessibility, and more specifically integration with VoiceOver and TalkBack screen readers;
- Richer model-based rendering, and the feature to define React Native props from those models.
As you will discover through this reading, both are somehow related. Let's find out how!
bonus
This post also covers a due overview of features introduced in the previous 6.1 minor release.
#
You Might not Need a Custom Component RendererModel-based rendering via customHTMLElementModels
prop has been a lightweight alternative to
custom (component) renderers since the early stages of the Foundry release.
However, it was limited to setting user agent styles (mixedUAStyles
),
although those styles could be derived from the DOM node element attributes
(the now-deprecated getUADerivedStyleFromAttributes
).
The below example is a reminder on how those element models can be defined, for instance to
register a new <blue-circle>
tag which renders to a 50 by 50 blue circle!
important
Keep in mind that mixedUAStyles
has a lower specificity than styles passed to RenderHTML
such as
tagsStyles
.
See the CSS Processing guide section
related to specificity for a refresher.
Version 6.2 ships with a bunch of new fields for HTML element models which should increase model-based rendering adoption. Let's take a tour!
getMixedUAStyles
#
This field deprecates getUADerivedStyleFromAttributes
; it serves the same purpose but its
signature has changed. It now receives the target tnode
and DOM element
,
which lets us implement more fine-grained logic such as CSS-selector-like
behaviors:
However and as stated before, those styles will have a lower specificity than tags, classes and ID styles and as such, these are not "real" CSS selectors. Hopefully, CSS selectors will be implemented at some point, you can upvote the feature request here! The hard challenge is that these should not impede performances. I am planning to explore this issue next year.
warning
Beware that this tnode
is an instance of TNodeDescriptor
, which is a minimal tnode
shape available during the Transient Render
Engine creation. You will not have access to parent
or children
fields,
since the hierarchy is yet in the making. This is why we are using the second
argument, element
, to query the DOM hierarchy instead. For this very reason,
you are advised to use domutils
library
to query the DOM and create your conditional styling rules.
reactNativeProps
#
This field holds props that will be passed to the native component during the
rendering phase. It is an object with three optional properties (for reference,
the shape of this object is a ReactNativePropsDefinitions
):
text
, to pass native props toText
-backed renderers;view
, to pass native props toView
-backed renderers;native
, to pass props to eitherView
orText
-backed renderers.
In the next snippet, we are defining a custom tag, <nav-widget>
, and setting
accessibility props to any underlying native component for the render phase. We
can also define onPress
, which will cause the renderer to use the
GenericPressable
instead of
default View
for block renderers.
However, this field is somehow limited in that it cannot depend on tnode
attributes.
This is where getReactNativeProps
comes to the rescue!
getReactNativeProps
#
The purpose of this field is identical to reactNativeProps
field. It only differs in that instead of a plain
object, it is a method which takes three arguments. A tnode
(the Transient Node),
the preGeneratedProps
(props generated by the TRE such as accessibility props, see next section)
and and element
(the DOM node). Finally, it returns a plain object (see ReactNativePropsDefinitions
).
In the example below, a custom nav-widget
tag is registered. This time, we
are handling onPress
events conditionally, based on attributes of the
tnode
. The snippets uses a phony API, appNavigatorController
, to navigate
between screens. Such API is easy to implement with a globally-defined ref to a
react-navigation
"navigation" object.
important
It is worth noting that you cannot use React hooks in those element models functions. But you can use any ad-hoc API to emit events, and glue that logic to hooks. If you really need React hooks, it might be simple to create a custom component renderer.
tip
Don't forget that you can mix model-based and component-based rendering!
#
A Focus on AccessibilityScreen readers integration has been worked on sparsely since the Foundry release, by addressing issues raised by the community, but until v6.2 there has not been structural improvements to cover the full range of required features. Thanks to the new Transient Render Engine, it has become very easy to define translations from HTML attributes to React Native props within the engine. Let's find out what's new!
aria-label
and role
Attributes#
Support for On one hand, aria-label
is used to hint screen readers on how a specific node and all its descendants should be read out loud. It is especially
useful to handle icons which don't have inner semantic meanings. This attribute has a React Native prop equivalent, namely
accessibilityLabel
, which serves the same purpose.
On the other hand, role
informs the screen reader of the target element nature and how a user might interact with it.
This attributes roughly maps to React Native accessibilityRole
prop, although the set of allowed values slightly differs.
See all supported roles and their mapping
here.
warning
Remember that a majority of interactive elements will not be rendered by this library. You must change their
content model to block
in order for them to be rendered.
Nevertheless, those interactive element models are already shipped with the appropriate accessibilityRole
prop.
The new Transient Render Engine will from now on translate both attributes to their React Native counterparts.
<a>
Tags#
Accessible <a>
tags now receive an accessibilityRole="link"
prop when their href
attribute is non-empty. To link that part with model enhancements seen in
the previous section, let's see now how we could set an accessibility
hint by
extending the HTML model:
Notice that HTMLElementModel.extend
method can now take a generator function. Pretty convenient to merge
nested fields!
warning
Because of a React Native
bug, nested
Text
elements are not accessible, which means that the screen reader will not
be able to identify <a>
tags as links when grouped with other textual
elements. Below is an example:
Luke Walczak from Callstack explains how to circumvent this issue in a great post. Unfortunately, this workaround cannot be genericized and we will have to wait for an upstream fix.
<img>
Tags#
Enhanced Accessibility for <img>
tags have been accessible since the Foundry beta. But the accessibility
props were set after the loading was complete. We have found that changing
accessibility annotations on the fly can degrade aural
experiences and have provided a fix.
info
accessibilityRole="image"
will be set for <img>
only when either alt
or
aria-label
attribute is present.
<h1-6>
Tags#
Accessible React Native has a “header” accessibility role which screen reader users depend
on widely to identify quickly the content hierarchy of a screen. Until this
release, react-native-render-html
did not pass the appropriate role to
heading tags.
#
Other Enhancementsuser-select
CSS property#
Support for With the new Transient Render Engine featuring React Native prop generation, it
has become very easy to pass the selectable
prop to React Native Text
components based on the presence of user-select
CSS property. Usage example:
important
Please not that this is not full support. The TRE will map user-select: none;
to selectable={false}
and
any other value to selectable={true}
.
#
Bonus: Version 6.1 FeaturesI didn't publish a release notes post for this version; I'm catching up here! From now on, I will try to write up a post for each minor and major release.
enableExperimentalBRCollapsing
Prop#
This recommended prop allows consumers to circumvent a bug in the Foundry
release where line breaks (<br>
) would be printed erroneously, such
as at the end of a paragraph. Its default it yet false
to avoid introducing
breaking changes but it will be enabled by default in the next major
release.
learn more
Read the complete explanation for this prop in the textual content guide.
enableExperimentalGhostLinesPrevention
Prop#
This recommended prop allows to circumvent a React Native
bug where empty Text
elements would be printed as ghost lines of fixed height (around 20 dpi). Its
default it yet false
to avoid introducing breaking changes but it will be
enabled by default in the next major release.
learn more
Read the complete explanation for this prop in the textual content guide.
provideEmbeddedHeaders
Prop#
A function prop which allows to generate HTTP headers for
remote resources. It currently works with <img>
and <iframe>
tags (since version 2.6 of the
@native-html/iframe-plugin
library).
learn more
See for example how it can be used with images in the image content guide.
bypassAnonymousTPhrasingNodes
Prop#
This prop, true
by default, makes the React rendering layer bypass grouping
(anonymous) TPhrasing
nodes which have only one child. It is best understood by
example. For instance, the following HTML snippet:
will produce the below Transient Render Tree:
which by default rendering rules, would be translated to the below React render tree:
However, when bypassAnonymousTPhrasingNodes
prop is true
(the default),
the render tree will be simplified to:
This behavior is preferred for many reasons. The most simple one is that it
simplifies the render tree. The less React elements there are, the best it is
performance-wise. Moreover, there are a lot of React Native bugs related to
nested Text
nodes, so this simplification limits the number of occurrences
of those bugs.
#
Learn MoreCheck out the release notes in the official repository. Moreover, if you encounter any issue while upgrading from a lower minor (6.0.x, 6.1.x), you are welcome to comment this Github issue!