Skip to content

Commit

Permalink
Merge pull request #987 from hansottowirtz/add-transform-option
Browse files Browse the repository at this point in the history
feat: add transform option
  • Loading branch information
remarkablemark authored Jul 20, 2023
2 parents 3c9c777 + 259eca1 commit 725acc2
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 4 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ parse('<p>Hello, World!</p>'); // React.createElement('p', {}, 'Hello, World!')
- [replace element and children](#replace-element-and-children)
- [replace element attributes](#replace-element-attributes)
- [replace and remove element](#replace-and-remove-element)
- [transform](#transform)
- [library](#library)
- [htmlparser2](#htmlparser2)
- [trim](#trim)
Expand Down Expand Up @@ -303,6 +304,21 @@ HTML output:
<p></p>
```

### transform

The `transform` option allows you to transform each element individually after it's parsed.

The `transform` callback's first argument is the React element:

```jsx
parse('<br>', {
transform: (reactNode, domNode, index) => {
// this will wrap every element in a div
return <div>{reactNode}</div>;
}
});
```

### library

The `library` option specifies the UI library. The default library is **React**.
Expand Down
6 changes: 6 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ export interface HTMLReactParserOptions {
domNode: DOMNode
) => JSX.Element | object | void | undefined | null | false;

transform?: (
reactNode: JSX.Element | string,
domNode: DOMNode,
index: number
) => JSX.Element | string | null;

trim?: boolean;
}

Expand Down
8 changes: 5 additions & 3 deletions lib/dom-to-react.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var canTextBeChildOfNode = utilities.canTextBeChildOfNode;
* @param {DomElement[]} nodes - DOM nodes.
* @param {object} [options={}] - Options.
* @param {Function} [options.replace] - Replacer.
* @param {Function} [options.transform] - Transform.
* @param {object} [options.library] - Library (React, Preact, etc.).
* @returns - String or JSX element(s).
*/
Expand All @@ -26,6 +27,7 @@ function domToReact(nodes, options) {
var node;
var isWhitespace;
var hasReplace = typeof options.replace === 'function';
var transform = options.transform || utilities.returnFirstArg;
var replaceElement;
var props;
var children;
Expand All @@ -46,7 +48,7 @@ function domToReact(nodes, options) {
key: replaceElement.key || i
});
}
result.push(replaceElement);
result.push(transform(replaceElement, node, i));
continue;
}
}
Expand All @@ -68,7 +70,7 @@ function domToReact(nodes, options) {

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

Expand Down Expand Up @@ -115,7 +117,7 @@ function domToReact(nodes, options) {
props.key = i;
}

result.push(createElement(node.name, props, children));
result.push(transform(createElement(node.name, props, children), node, i));
}

return result.length === 1 ? result[0] : result;
Expand Down
7 changes: 6 additions & 1 deletion lib/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,16 @@ function canTextBeChildOfNode(node) {
return !elementsWithNoTextChildren.has(node.name);
}

function returnFirstArg(arg) {
return arg;
}

module.exports = {
PRESERVE_CUSTOM_ATTRIBUTES: PRESERVE_CUSTOM_ATTRIBUTES,
invertObject: invertObject,
isCustomComponent: isCustomComponent,
setStyleProp: setStyleProp,
canTextBeChildOfNode: canTextBeChildOfNode,
elementsWithNoTextChildren: elementsWithNoTextChildren
elementsWithNoTextChildren: elementsWithNoTextChildren,
returnFirstArg: returnFirstArg
};
37 changes: 37 additions & 0 deletions test/dom-to-react.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,43 @@ describe('domToReact replace option', () => {
});
});

describe('domToReact transform option', () => {
it('can wrap all elements', () => {
const options = {
transform: (reactNode, domNode, i) => {
return React.createElement('div', { key: i }, reactNode);
}
};

const reactElement = domToReact(htmlToDOM(html.list), options);
expect(reactElement.key).toBe('0');
expect(reactElement.props.children.props.children[0].key).toBe('0');
expect(reactElement.props.children.props.children[1].key).toBe('1');
expect(reactElement).toMatchInlineSnapshot(`
<div>
<ol>
<div>
<li>
<div>
One
</div>
</li>
</div>
<div>
<li
value="2"
>
<div>
Two
</div>
</li>
</div>
</ol>
</div>
`);
});
});

describe('domToReact', () => {
describe('when React >=16', () => {
it('preserves unknown attributes', () => {
Expand Down

0 comments on commit 725acc2

Please sign in to comment.