Skip to content

Commit aae4a42

Browse files
committed
docs: update draft 6.2 release notes blog post
1 parent 9b3a007 commit aae4a42

File tree

1 file changed

+231
-15
lines changed

1 file changed

+231
-15
lines changed

apps/website/blog/2021-09-27-6-2-updates.mdx

Lines changed: 231 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,25 @@ hide_table_of_contents: false
1111
draft: true
1212
---
1313

14+
import APIReference from '@site/src/components/APIReference';
15+
1416
It has been over two months since the first final version of the Foundry
1517
released has been made public.
1618

17-
## You Might not Need a Custom Component Renderer Anymore
19+
## You Might not Need a Custom Component Renderer
1820

19-
Model-based custom rendering via `customHTMLElementModels` prop has been a
20-
lightweight alternative to custom (component) renderers since the early stages
21-
of the Foundry release. However, it was limited to setting user agent styles
22-
(`mixedUAStyles`), although those styles could be derived from the DOM node
23-
element attributes (`getUADerivedStyleFromAttributes`).
21+
Model-based custom rendering via <APIReference name="RenderHTMLProps"
22+
member="customHTMLElementModels"/> prop has been a lightweight alternative to
23+
custom (component) renderers since the early stages of the Foundry release.
24+
However, it was limited to setting user agent styles (<APIReference
25+
name="HTMLElementModel" member="mixedUAStyles" />),
26+
although those styles could be derived from the DOM node element attributes
27+
(<APIReference name="HTMLElementModel" member="getUADerivedStyleFromAttributes" />).
2428

2529
```js title="An Example of Model-Based Rendering"
30+
import RenderHTML, { HTMLElementModel } from 'react-native-render-html';
31+
32+
// The eponym prop to pass to RenderHTML
2633
const customHTMLElementModels = {
2734
'blue-circle': HTMLElementModel.fromCustomModel({
2835
tagName: 'blue-circle',
@@ -38,33 +45,238 @@ const customHTMLElementModels = {
3845
};
3946
```
4047

41-
Version 6.2 ships with a bunch of new properties for HTML element models which
42-
should make model-based custom rendering much more prevalent. Let's take a tour!
48+
:::important
49+
Keep in mind that <APIReference name="HTMLElementModel" member="mixedUAStyles"
50+
/> has a lower specificity than styles passed to `RenderHTML` such as
51+
&ZeroWidthSpace;<APIReference name="RenderHTMLProps" member="tagsStyles" />.
52+
:::
53+
54+
Version 6.2 ships with a bunch of new fields for HTML element models which
55+
should make model-based custom rendering more popular. Let's take a tour!
56+
57+
### `getMixedUAStyles` almost features CSS selectors!
4358

44-
### `getUADynamicMixedStyles`
59+
This field deprecates <APIReference name="HTMLElementModel" member="getUADerivedStyleFromAttributes"/>; it's basically the same
60+
but its signature has changed: it receives the target `tnode` and DOM `element`, which lets us implement CSS-selector-like behaviors:
61+
62+
```js title="Conditionnaly remove margins of 'ol' direct descendents of 'p' elements."
63+
import RenderHTML, {
64+
defaultHTMLElementModels,
65+
isDomElement
66+
} from 'react-native-render-html';
67+
68+
// The eponym prop to pass to RenderHTML
69+
const customHTMLElementModels = {
70+
ol: defaultHTMLElementModels.ol.extend({
71+
getMixedUAStyles(tnode, element) {
72+
if (isDomElement(element.parent) && element.parent.tagName === 'p') {
73+
// This is equivalent to targetting a "p > ol" CSS selector.
74+
return {
75+
marginTop: 0,
76+
marginBottom: 0
77+
};
78+
}
79+
}
80+
})
81+
};
82+
```
83+
84+
:::warning
85+
Beware that this `tnode` is an instance of <APIReference name="TNodeDescriptor"
86+
/>, which is a minimal `tnode` shape available during the Transient Render
87+
Engine creation. You will not have access to `parent` or `children` fields,
88+
since the hierarchy is yet in the making. This is why we are using the second
89+
argument, `element`, to query the DOM hierarchy instead. For this very reason,
90+
you are advised to use [`domutils`](https://github.com/fb55/domutils) library
91+
to query the DOM and create your conditional styling rules.
92+
:::
4593

4694
### `reactNativeProps`
4795

48-
### `getDynamicReactNativeProps`
96+
This field will set props to the native component during the rendering phase. It is an object with three optional properties:
97+
98+
1. `text`, to pass native props to the `Text` component when the renderer is textual;
99+
2. `view`, to pass native props to the `View` component when the renderer is block;
100+
3. `native`, to pass props to either `View` or `Text` components, irrespective of the renderer type.
101+
102+
The type definition for this object is <APIReference name="ReactNativePropsDefinitions" />.
103+
104+
In the below example, we are defining a custom tag, `nav-widget`, and setting
105+
accessibility props to any underlying native component for the render phase. We
106+
can also define `onPress`, which will cause the renderer to use the
107+
&ZeroWidthSpace;<APIReference name="RenderHTMLProps" member="GenericPressable" /> instead of
108+
default `View` for block renderers.
109+
110+
```js title="Defining React Native props in an HTML element model"
111+
import RenderHTML, {
112+
HTMLContentModel,
113+
HTMLElementModel
114+
} from 'react-native-render-html';
115+
116+
// The eponym prop to pass to RenderHTML
117+
const customHTMLElementModels = {
118+
'nav-widget': HTMLElementModel.fromCustomModel({
119+
tagName: 'nav-widget',
120+
contentModel: HTMLContentModel.block,
121+
reactNativeProps: {
122+
native: {
123+
accessibilityRole: 'link',
124+
onPress() {
125+
console.info('Pressed the nav widget!');
126+
}
127+
}
128+
}
129+
})
130+
};
131+
```
132+
133+
However, this field is somehow limited in that it cannot depend on `tnode` attributes.
134+
This is where <APIReference name="HTMLElementModel" member="getReactNativeProps" /> comes to the rescue!
135+
136+
### `getReactNativeProps`
137+
138+
The purpose of this field is identical to <APIReference name="HTMLElementModel"
139+
member="reactNativeProps" /> field. It only differs in that instead of a plain
140+
object, it is a method which takes two arguments, `tnode` (the Transient Node)
141+
and `element` (the DOM node) and returns a plain object (see <APIReference
142+
name="ReactNativePropsDefinitions" />).
143+
144+
In the example below, a custom `nav-widget` tag is registered. This time, we
145+
are handling `onPress` events conditionally, based on attributes of the
146+
`tnode`. The snippets uses a phony API, `appNavigatorControler`, to navigate
147+
between screens. Such API is easy to implement with a globally-defined ref to a
148+
`react-navigation` "navigation" object.
149+
150+
:::important
151+
It is worth noting that you cannot use React hooks in any of element models functions. But you can use
152+
any ad-hoc API to emit events. If you really need React hooks, add a custom component renderer.
153+
:::
49154

50-
## Don't Forget you can Mix Model-Based and Component-Based Custom Rendering
155+
```js title="Defining React Native props based on the TNode in an HTML element model"
156+
import RenderHTML, {
157+
HTMLContentModel,
158+
HTMLElementModel
159+
} from 'react-native-render-html';
160+
import appNavigatorControler from './appNavigatorControler';
161+
162+
// The eponym prop to pass to RenderHTML
163+
const customHTMLElementModels = {
164+
'nav-widget': HTMLElementModel.fromCustomModel({
165+
tagName: 'nav-widget',
166+
contentModel: HTMLContentModel.block,
167+
getReactNativeProps(tnode) {
168+
return {
169+
native: {
170+
accessibilityRole: 'link',
171+
onPress() {
172+
const targetScreen = tnode.attributes['data-target'];
173+
const targetParams = tnode.attributes['data-params'];
174+
appNavigatorControler.navigate(
175+
targetScreen,
176+
targetParams ? JSON.parse(targetParams) : null
177+
);
178+
}
179+
}
180+
};
181+
}
182+
})
183+
};
184+
```
185+
186+
:::tip
187+
Don't forget that you can mix model-based and component-based rendering!
188+
:::
51189

52190
## A Focus on Accessibility
53191

54192
### Support for `aria-label` and `aria-role` Attributes
55193

194+
`aria-label` and `aria-role` HTML attributes roughly map to
195+
`accessibilityLabel` and `accessibilityRole` React Native props. The new
196+
Transient Render Engine which features React Native props generation will from now on
197+
translate both attributes to their React Native counterparts. The mapping rules
198+
for `aria-role` have been extracted from `react-native-web`.
199+
56200
### Accessible `<a>` Tags
57201

58-
### Accessible `<img>` Tags
202+
`<a>` tags now receive an `accessibilityRole="link"` prop when their `href`
203+
attribute is non-empty. Let's see now how we could set a custom generic hint by
204+
extending the HTML model:
59205

60-
### Accessible `<h1-60>` Tags
206+
```ts
207+
import RenderHTML, { defaultHTMLElementModels } from 'react-native-render-html';
61208

62-
### Other tags
209+
// The eponym prop to pass to RenderHTML
210+
const customHTMLElementModels = {
211+
a: defaultHTMLElementModels.a.extend((aModel) => ({
212+
nativeProps: {
213+
...aModel.nativeProps,
214+
accessibilityHint: 'Open in your system web browser.'
215+
}
216+
}))
217+
};
218+
```
219+
220+
:::warning
221+
Because of a [React Native
222+
limitation](https://github.com/facebook/react-native/issues/32004), nested
223+
`Text` elements are not accessible, which means that the screen reader will not
224+
be able to identify `<a>` tags as links when grouped with other textual
225+
elements. Below is an example:
226+
227+
```html
228+
<p>
229+
Unfortunately,
230+
<a href="https://domain.com">this hyperlink is not accessible</a>
231+
</p>
232+
```
233+
234+
Luke Walczak from Callstack [explains how to circumvent this issue in a great
235+
post](https://callstack.com/blog/react-native-android-accessibility-tips/).
236+
Unfortunately, this workaround is not generalizable and we will have to wait
237+
for an upstream fix.
238+
239+
:::
240+
241+
### Enhanced Accessibility for `<img>` Tags
242+
243+
`<img>` tags have been accessible since the Foundry beta. But the accessibility
244+
props were set after the loading was complete. We have found that changing
245+
accessibility annotations depending on loading state can degrade aural
246+
experience. We have thus replaced this behavior by moving those props to the
247+
image container, to make it independent of the internal state.
248+
249+
:::info
250+
`accessibilityRole="image"` will be set for `<img>` only when either `alt` or
251+
`aria-label` attribute is present.
252+
:::
253+
254+
### Accessible `<h1-6>` Tags
255+
256+
React Native has a “header” accessibility role which screen reader users depend
257+
on a lot to identify efficiently the content hierarchy of a screen. Until this
258+
release, `react-native-render-html` did not pass this role to heading tags.
63259

64260
## Other Enhancements
65261

66262
### Support for `user-select` CSS property
67263

264+
With the new Transient Render Engine featuring React Native prop generation, it
265+
has become very easy to pass the `selectable` prop to React Native `Text`
266+
components based on the presence of `user-select` CSS property. Usage example:
267+
268+
```html
269+
<p style="user-select: none">
270+
This line is not selectable.<br />
271+
<span>Neither is that one.</span>
272+
</p>
273+
```
274+
275+
:::important
276+
Please not that this is not full support. The TRE will map `user-select: none;` to `selectable={false}` and
277+
any other value to `selectable={true}`.
278+
:::
279+
68280
### `getNativePropsForTnode` Function
69281

70282
## Bonus: Notes on Version 6.1
@@ -77,4 +289,8 @@ should make model-based custom rendering much more prevalent. Let's take a tour!
77289

78290
### `provideEmbeddedHeaders` Prop
79291

80-
### `bypassAnonymousTPhrasingNodes` Prop
292+
This is a function prop which allows consumer to generate HTTP headers for
293+
remote resources. It currently works with `<img>` and `<iframe>` tags (from the
294+
`@native-html/iframe-plugin` lib).
295+
296+
### `bypassAnonymousTPhrasingNodes` Prop

0 commit comments

Comments
 (0)