Skip to content

Commit 259eca1

Browse files
committed
feat: add transform option
1 parent 3c9c777 commit 259eca1

File tree

5 files changed

+70
-4
lines changed

5 files changed

+70
-4
lines changed

README.md

+16
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ parse('<p>Hello, World!</p>'); // React.createElement('p', {}, 'Hello, World!')
3838
- [replace element and children](#replace-element-and-children)
3939
- [replace element attributes](#replace-element-attributes)
4040
- [replace and remove element](#replace-and-remove-element)
41+
- [transform](#transform)
4142
- [library](#library)
4243
- [htmlparser2](#htmlparser2)
4344
- [trim](#trim)
@@ -303,6 +304,21 @@ HTML output:
303304
<p></p>
304305
```
305306

307+
### transform
308+
309+
The `transform` option allows you to transform each element individually after it's parsed.
310+
311+
The `transform` callback's first argument is the React element:
312+
313+
```jsx
314+
parse('<br>', {
315+
transform: (reactNode, domNode, index) => {
316+
// this will wrap every element in a div
317+
return <div>{reactNode}</div>;
318+
}
319+
});
320+
```
321+
306322
### library
307323

308324
The `library` option specifies the UI library. The default library is **React**.

index.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ export interface HTMLReactParserOptions {
3838
domNode: DOMNode
3939
) => JSX.Element | object | void | undefined | null | false;
4040

41+
transform?: (
42+
reactNode: JSX.Element | string,
43+
domNode: DOMNode,
44+
index: number
45+
) => JSX.Element | string | null;
46+
4147
trim?: boolean;
4248
}
4349

lib/dom-to-react.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ var canTextBeChildOfNode = utilities.canTextBeChildOfNode;
1111
* @param {DomElement[]} nodes - DOM nodes.
1212
* @param {object} [options={}] - Options.
1313
* @param {Function} [options.replace] - Replacer.
14+
* @param {Function} [options.transform] - Transform.
1415
* @param {object} [options.library] - Library (React, Preact, etc.).
1516
* @returns - String or JSX element(s).
1617
*/
@@ -26,6 +27,7 @@ function domToReact(nodes, options) {
2627
var node;
2728
var isWhitespace;
2829
var hasReplace = typeof options.replace === 'function';
30+
var transform = options.transform || utilities.returnFirstArg;
2931
var replaceElement;
3032
var props;
3133
var children;
@@ -46,7 +48,7 @@ function domToReact(nodes, options) {
4648
key: replaceElement.key || i
4749
});
4850
}
49-
result.push(replaceElement);
51+
result.push(transform(replaceElement, node, i));
5052
continue;
5153
}
5254
}
@@ -68,7 +70,7 @@ function domToReact(nodes, options) {
6870

6971
// We have a text node that's not whitespace and it can be nested
7072
// in its parent so add it to the results
71-
result.push(node.data);
73+
result.push(transform(node.data, node, i));
7274
continue;
7375
}
7476

@@ -115,7 +117,7 @@ function domToReact(nodes, options) {
115117
props.key = i;
116118
}
117119

118-
result.push(createElement(node.name, props, children));
120+
result.push(transform(createElement(node.name, props, children), node, i));
119121
}
120122

121123
return result.length === 1 ? result[0] : result;

lib/utilities.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,16 @@ function canTextBeChildOfNode(node) {
120120
return !elementsWithNoTextChildren.has(node.name);
121121
}
122122

123+
function returnFirstArg(arg) {
124+
return arg;
125+
}
126+
123127
module.exports = {
124128
PRESERVE_CUSTOM_ATTRIBUTES: PRESERVE_CUSTOM_ATTRIBUTES,
125129
invertObject: invertObject,
126130
isCustomComponent: isCustomComponent,
127131
setStyleProp: setStyleProp,
128132
canTextBeChildOfNode: canTextBeChildOfNode,
129-
elementsWithNoTextChildren: elementsWithNoTextChildren
133+
elementsWithNoTextChildren: elementsWithNoTextChildren,
134+
returnFirstArg: returnFirstArg
130135
};

test/dom-to-react.test.js

+37
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,43 @@ describe('domToReact replace option', () => {
206206
});
207207
});
208208

209+
describe('domToReact transform option', () => {
210+
it('can wrap all elements', () => {
211+
const options = {
212+
transform: (reactNode, domNode, i) => {
213+
return React.createElement('div', { key: i }, reactNode);
214+
}
215+
};
216+
217+
const reactElement = domToReact(htmlToDOM(html.list), options);
218+
expect(reactElement.key).toBe('0');
219+
expect(reactElement.props.children.props.children[0].key).toBe('0');
220+
expect(reactElement.props.children.props.children[1].key).toBe('1');
221+
expect(reactElement).toMatchInlineSnapshot(`
222+
<div>
223+
<ol>
224+
<div>
225+
<li>
226+
<div>
227+
One
228+
</div>
229+
</li>
230+
</div>
231+
<div>
232+
<li
233+
value="2"
234+
>
235+
<div>
236+
Two
237+
</div>
238+
</li>
239+
</div>
240+
</ol>
241+
</div>
242+
`);
243+
});
244+
});
245+
209246
describe('domToReact', () => {
210247
describe('when React >=16', () => {
211248
it('preserves unknown attributes', () => {

0 commit comments

Comments
 (0)