Giter Club home page Giter Club logo

html-to-react's Introduction

html-to-react

Greenkeeper badge Build Status npm version Dependency Status Coverage Status npm

A lightweight library that converts raw HTML to a React DOM structure.

Why?

I had a scenario where an HTML template was generated by a different team, yet I wanted to leverage React for the parts I did have control over. The template basically contains something like:

<div class="row">
  <div class="col-sm-6">
    <div data-report-id="report-1">
      <!-- A React component for report-1 -->
    </div>
  </div>
  <div class="col-sm-6">
    <div data-report-id="report-2">
      <!-- A React component for report-2 -->
    </div>
  </div>
</div>

I had to replace each <div> that contains a data-report-id attribute with an actual report, which was nothing more than a React component.

Simply replacing the <div> elements with a React component would end up with multiple top-level React components that have no common parent.

The html-to-react module solves this problem by parsing each DOM element and converting it to a React tree with one single parent.

Installation

$ npm install --save html-to-react

Examples

Simple

The following example parses each node and its attributes and returns a tree of React elements.

const ReactDOMServer = require('react-dom/server');
const HtmlToReactParser = require('html-to-react').Parser;

const htmlInput = '<div><h1>Title</h1><p>A paragraph</p></div>';
const htmlToReactParser = new HtmlToReactParser();
const reactElement = htmlToReactParser.parse(htmlInput);
const reactHtml = ReactDOMServer.renderToStaticMarkup(reactElement);

assert.equal(reactHtml, htmlInput); // true

With Custom Processing Instructions

If certain DOM nodes require specific processing, for example if you want to capitalize each <h1> tag, the following example demonstrates this:

const ReactDOMServer = require('react-dom/server');
const HtmlToReact = require('html-to-react');
const HtmlToReactParser = require('html-to-react').Parser;

const htmlInput = '<div><h1>Title</h1><p>Paragraph</p><h1>Another title</h1></div>';
const htmlExpected = '<div><h1>TITLE</h1><p>Paragraph</p><h1>ANOTHER TITLE</h1></div>';

const isValidNode = function () {
  return true;
};

// Order matters. Instructions are processed in the order they're defined
const processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions();
const processingInstructions = [
  {
    // Custom <h1> processing
    shouldProcessNode: function (node) {
      return node.parent && node.parent.name && node.parent.name === 'h1';
    },
    processNode: function (node, children) {
      return node.data.toUpperCase();
    }
  },
  {
    // Anything else
    shouldProcessNode: function (node) {
      return true;
    },
    processNode: processNodeDefinitions.processDefaultNode
  }
];
const htmlToReactParser = new HtmlToReactParser();
const reactComponent = htmlToReactParser.parseWithInstructions(htmlInput, isValidNode,
  processingInstructions);
const reactHtml = ReactDOMServer.renderToStaticMarkup(reactComponent);
assert.equal(reactHtml, htmlExpected);

Replace the Children of an Element

There may be a situation where you want to replace the children of an element with a React component. This is beneficial if you want to:

  • a) Preserve the containing element
  • b) Not rely on any child node to insert your React component

Example

Below is a simple template that could get loaded via ajax into your application

Before
<div class="row">
  <div class="col-sm-6">
    <div data-container="wysiwyg">
      <h1>Sample Heading</h1>
      <p>Sample Text</p>
    </div>
  </div>
</div>
After

You may want to extract the inner html from the data-container attribute, store it and then pass it as a prop to your injected RichTextEditor.

<div class="row">
  <div class="col-sm-6">
    <div data-container="wysiwyg">
      <RichTextEditor html={"<h1>Sample heading</h1><p>Sample Text</p>"} />
    </div>
  </div>
</div>

Setup

In your instructions object, you must specify replaceChildren: true.

const React = require('react');
const HtmlToReact = require('html-to-react');
const HtmlToReactParser = require('html-to-react').Parser;

const htmlToReactParser = new HtmlToReactParser();
const htmlInput = '<div><div data-test="foo"><p>Text</p><p>Text</p></div></div>';
const htmlExpected = '<div><div data-test="foo"><h1>Heading</h1></div></div>';

const isValidNode = function () {
  return true;
};

const processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions();

// Order matters. Instructions are processed in
// the order they're defined
const processingInstructions = [
  {
    // This is REQUIRED, it tells the parser
    // that we want to insert our React
    // component as a child
    replaceChildren: true,
    shouldProcessNode: function (node) {
      return node.attribs && node.attribs['data-test'] === 'foo';
    },
    processNode: function (node, children, index) {
      return React.createElement('h1', {key: index,}, 'Heading');
    }
  },
  {
    // Anything else
    shouldProcessNode: function (node) {
      return true;
    },
    processNode: processNodeDefinitions.processDefaultNode,
  },
];

const reactComponent = htmlToReactParser.parseWithInstructions(
  htmlInput, isValidNode, processingInstructions);
const reactHtml = ReactDOMServer.renderToStaticMarkup(
  reactComponent);
assert.equal(reactHtml, htmlExpected);

With Preprocessing Instructions

There may be situations where you want to preprocess nodes before rendering them, analogously to the custom processing instructions functionality. The rationale for supporting preprocessing hooks is generally that you might want to apply more general processing to nodes, before applying custom processing hooks to filtered sets of nodes. You could accomplish the same by calling common functions from your custom processing hooks, but the preprocessing hooks API makes it more convenient.

Example

Below is a simple template in which you may want to replace div IDs, via a preprocessing hook:

<div class="row">
  <div id="first" data-process="shared">
    <p>Sample For First</p>
  </div>
  <div id="second" data-process="shared">
    <p>Sample For Second</p>
  </div>
</div>

We want to process the above HTML into the following:

<div class="row">
  <h1 id="preprocessed-first">First</h1>
  <h2 id="preprocessed-second">Second</h2>
</div>

We can accomplish that with the following script, using a combination of preprocessing and custom processing instructions:

const React = require('react');
const HtmlToReact = require('html-to-react');
const HtmlToReactParser = require('html-to-react').Parser;

const htmlToReactParser = new HtmlToReactParser();
const htmlInput = '<div class="row">' +
  '<div id="first" data-process="shared">' +
    '<p>Sample For First</p>' +
  '</div>' +
  '<div id="second" data-process="shared">' +
    '<p>Sample For Second</p>' +
  '</div>' +
'</div>';

const htmlExpected = '<div class="row">' +
  '<h1 id="preprocessed-first">First</h1>' +
  '<h2 id="preprocessed-second">Second</h2>' +
'</div>';

const isValidNode = function () {
  return true;
};

const preprocessingInstructions = [
  {
    shouldPreprocessNode: function (node) {
      return node.attribs && node.attribs['data-process'] === 'shared';
    },
    preprocessNode: function (node) {
      node.attribs = {id: `preprocessed-${node.attribs.id}`,};
    },
  }
];
const processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions();
const processingInstructions = [
  {
    shouldProcessNode: function (node) {
      return node.attribs && node.attribs.id === 'preprocessed-first';
    },
    processNode: function(node, children, index) {
      return React.createElement('h1', {key: index, id: node.attribs.id,}, 'First');
    },
  },
  {
    shouldProcessNode: function (node) {
      return node.attribs && node.attribs.id === 'preprocessed-second';
    },
    processNode: function (node, children, index) {
      return React.createElement('h2', {key: index, id: node.attribs.id,}, 'Second');
    },
  },
  {
    shouldProcessNode: function (node) {
      return true;
    },
    processNode: processNodeDefinitions.processDefaultNode,
  },
];

const reactComponent = parser.parseWithInstructions(htmlInput, isValidNode, processingInstructions,
  preprocessingInstructions);
const reactHtml = ReactDOMServer.renderToStaticMarkup(reactComponent);
assert.equal(reactHtml, htmlExpected);

Tests & Coverage

Test locally: $ npm test

Test with coverage and report coverage to Coveralls: $ npm run test-coverage

Test with coverage and open HTML report: $ npm run test-html-coverage

html-to-react's People

Contributors

aknuds1 avatar aleemb avatar alexgilleran avatar arielger avatar artemnizelnyk avatar bilobom avatar brett-nuske-alliancesoftware avatar codepunkt avatar dependabot[bot] avatar gerhardsletten avatar gfx avatar github-actions[bot] avatar greenkeeper[bot] avatar greenkeeperio-bot avatar gycianka avatar jasonenglish avatar kevin940726 avatar maltoze avatar nickpalmer avatar no23reason avatar oroce avatar rajatdua avatar rexxars avatar simon04 avatar thangngoc89 avatar tryggvigy avatar tylersticka avatar vasildb avatar woofers avatar yomguithereal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

html-to-react's Issues

Error trying parse HTML

Hi

When I tried parse it:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
  <body>
    <div style="text-align: center;"><img id="img_preview" src="https://....png" width="238" height="137"></div>
    <div style="text-align: left;">text</div>
  </body>
</html>

Into a div:

import { Parser } from "html-to-react"

const X = ({ content }) = (
  <div>{new Parser().parse(content)}</div>
)

I received an error:

Uncaught DOMException: Failed to execute 'createElement' on 'Document': The tag name provided ('!doctype') is not a valid name.

Can you help me?

Thanks

data uri's fail to parse

When you try to parse this HTML:

html = `<span style="background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIW…Dxq31HTl6vABv4+6/5u1+/qj/8/l29/8vHos0/vjCjmwMA2Kl6t1I/6PwAAAAASUVORK5CYII=');"></span>`

You'll get an error:

utils.js:16 Uncaught TypeError: Cannot read property 'length' of undefined
    at createStyleJsonFromString (utils.js:16)
    at eval (eval at <anonymous> (utils.js:33), <anonymous>:1:1)
    at XWrap.f (utils.js:33)
    at XWrap../node_modules/ramda/src/internal/_xwrap.js.module.exports.XWrap.@@transducer/step (_xwrap.js:10)
    at _arrayReduce (_reduce.js:11)
    at _reduce (_reduce.js:44)
    at f3 (_curry3.js:35)
    at Object.createElement (utils.js:28)
    at Object.processDefaultNode [as processNode] (process-node-definitions.js:24)
    at traverseDom (parser.js:39)

Because the .split(':') in createStyleJsonFromString is tripped up by data:image/png.

Parser.parse() is not a function

When I tried to parse a html-string with the simple function "parse(html)" of the imported parser, an error occurred that "parse" is not a function...

Self-closing sibling elements not in `voidElementTags` get parsed wrong

Hello there! I don't know if I'm doing something wrong, but when I try to parse this string:

<div><test /><test /><test /></div>

with custom processingInstructions that do a createElement, I don't receive three siblings, I receive three nested children - the parser seems to think this is what's happening:
<div><test><test><test></test></test></test></div>

Same with any self-closing tag not in voidElementTags

In case it's any use, the package html2json seems to parse it into the correct json tree structure and converts it back into html adding closing tags to non-self-closing tags (so sanitising the html input for html-to-react with json2html(html2json(html)) is a quick workaround)

Also related, if you try to parse <textarea>some value</textarea> it will swallow the value because children of voidElementTags get ignored

An in-range update of react is breaking the build 🚨

Version 15.6.1 of react just got published.

Branch Build failing 🚨
Dependency react
Current Version 15.6.0
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As react is “only” a devDependency of this project it might not break production or downstream projects, but “only” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this 💪

Status Details
  • continuous-integration/travis-ci/push The Travis CI build failed Details
  • coverage/coveralls First build on greenkeeper/react-15.6.1 at 100.0% Details

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

Unknown prop for img

Have issues with img elements with our adaptive-src attribute

Warning: Unknown prop adaptive-src on

tag. Remove this prop from the element. For details, see https://fb.me/react-unknown-prop
in div
in div
in div
in div
in div
in div
in div (created by App)
in App

Webpack bundle

Is it possibile to add a webpack task to pack as application bundle?
Thanks

[BUG] '&' character breaks parsing

Hello!

Whenever my HTML text has an '&' in it, the behaviour of this library acts very strange. Here is a pretty basic example:

render() {

    const isValidNode = () => true;
    const html = "<h3>Test&</h3>";
  
    const instructions = [
      {
        shouldProcessNode: node => true,
        processNode: (node, children ,index) => {
          console.log(node);
          return this.processNodeDefinitions.processDefaultNode(node, children, index)
        }
      }
    ];

    return (
      <div >
         {this.parser.parseWithInstructions(html, isValidNode, instructions)}
      </div>
    );
}

The output of this component then looks like this:

image

The console log in processNode gets called twice, and the output is:

image

Notice that the last 't' in "Test" gets duplicated as well.

If I change the html string to

html = "<h3> Test& </h3>";

Then the output doesn't have the stray h3, but the last "t" is still duplicated.

image.

My versions are:

{
   "react-dom": "^16.6.3",
   "react": "^16.6.3",
   "html-to-react": "^1.3.4",
}

Any help is appreciated. Thank you!

isValidNode function seems redundant

processingInstructions already ignores any node that fails all shouldProcessNode; it seems both confusing and unnecessary to have that extra guard.

Below is the code that I take issue with:

if (isValidNode(node)) { // how is this helpful?
     var processingInstruction = find(function (processingInstruction) {
       return processingInstruction.shouldProcessNode(node);
     }, processingInstructions);
     if (processingInstruction != null) {
       var children = reject(function (x) {return x == null || x === false;},
         addIndex(map)(function (child, i) {
           return traverseDom(child, isValidNode, processingInstructions, i);
         }, node.children || []));

       if (processingInstruction.replaceChildren) {
         return utils.createElement(node, index, node.data, [
           processingInstruction.processNode(node, children, index),
         ]);
       }

       return processingInstruction.processNode(node, children, index);
     } else {
       return false;
     }
   } else {
     return false;
   }

Breaks on inline base64 images in styles

Issue #28 fixes : bug due to the background-image property containing a : as per

background-image:url(http://foo.com)

In a similar class of bugs is an inline style containing a ;

background-image:url(data:image/png;base64,iVBORw0KGgoAAA)

Since styles are split on ; this breaks.

Unknown DOM property - xmlns:xlink

The parser does not seem to not support svgs with the xmlns:xlink attribute. React is throwing the following error:

Warning: Unknown DOM property xmlns:xlink. Did you mean xmlnsXlink?

Example svg:

<svg version="1.1" class="tp_svg_x" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 31.5 31.5" style="enable-background:new 0 0 31.5 31.5;" xml:space="preserve">
    <path class="tp_svg_x_path" d="M11.2,15.8L0,4.5L4.5,0l11.2,11.2L27,0l4.5,4.5L20.2,15.8L31.5,27L27,31.5L15.8,20.2L4.5,31.5L0,27L11.2,15.8z"/>
</svg>

Issues with quotes in inline styles and image backgrounds

This little snippet does a great job reconciling ReactJS with any static HTML from the server without having to use NodeJS server rendering -although that would be the best thing to do- however, not in my case since I am using Drupal as the back-end and I simply need to get the server produced HTML, combine it with ReactJS components and create a new components tree. The webpage works even without JS (as an edge case). But I'm having some issues with inline styles and quotes escaping, more exactly when I pass in a static image URL to the background property of a div:

The HTML

<div class="page-section--teaser__image" style="background-image: url(http://lorempixel.com/400/200)"></div>

The error

GET http://localhost:3000/http 404 (Not Found)

The component

class App extends Component{
  render(){
    var html = $('#container').html();
    var reactElement = htmlToReactParser.parse(html);

    return (
      <div>
        <Nav/>
        This is a React App
        {reactElement}
      </div>
    )
  }
}

As you can see this is trimming the URL and removing everything after the first :, therefore the URL address which should be treated as an external URL is being treated as a relative path. Any ideas of what's going on? I guess it has to do with escaping issues.

Extend resulting component

Hi,
How I would extend the resulting component from processNode (high order components)?
Probably there should be a postProcessNode callback at the processing instructions?

Feature request: Enhancing the node object itself before rendering a react component

I would like to have your input if this feature would be a sensible addition to the library.
You can definitely do this with the current implementation it is just not as straight forward.

Problem:
I have multiple ProcessInstructions which rely on shared code.
e.g. rewriting classes, custom handling for on-handlers, custom handling of root/leaf nodes,...

Suggested change:

const enhancers = [
  {
    shouldEnhanceNode: (node) => {
      return node.attribs && node.attribs['data-test'] === 'foo';
    },
    enhanceNode: (node, children, index) => {
      return { ...node, attribs: { ...node.attribs, ...extraAttribs } };
    }
  },
  {
    shouldEnhanceNode: (node) => {
      return true;
    },
    enhanceNode: (node, children, index) => {
      if (node.attribs && node.attribs.class) {
        const prefixedNode = { ...node };
        prefixedNode.attribs.class = prefixedNode.attribs.class.split(' ').map((className) => `${prefix}-${className}`).join(' ');
      }
      return node;
    },
  },
];

let reactComponent = parser.parseWithInstructions(
  htmlInput, isValidNode, processingInstructions, enhancers);
// Or maby change the api a bit?
reactComponent = parser.parse({
  html: htmlInput, // mandatory
  isValidNode, // optional
  processingInstructions, // optional
  enhancers, // optional
});

Filter "empty" strings

Hi,

I'd like to filter out "empty" strings, that add noise to my component composition.

Imagine the following HTML source:

<react-component react-name="Accordion">
    <react-component react-name="AccordionItem" title="Title 1">
        <p>Some text</p>
    </react-component>
    <react-component react-name="AccordionItem" title="Title 2">
        <p>Some other text</p>
    </react-component>
</react-component>

I correctly get instances of Accordion and AccordionItem.
But within my Accordion component this.props.children returns the following:

["↵    ", Object, "↵    ", Object, "↵"]

I want to strip out these white-space strings. I tried to do that, by adjusting my isValidNode method:

function(node) {
    if(typeof node === 'object') {
        return !(node.type === 'text' && node.data.trim().replace(/^\n+$/g, '') === '')
    }
    return true;
};

But this leads to:

[false, Object, false, Object, false]

What I want is:

[Object, Object]

Any hints for me?

I've wrapped html in a div, and it keeps saying there are more than 1 root element.

parser.js:49 Uncaught (in promise) Error: html-to-react currently only supports HTML with one single root element. The HTML provided contains 3 root elements. You can fix that by simply wrapping your HTML in a div element

Below is output in the chrome console when I'm on Html2ReactParser function.

I left spaces unchanged because maybe it's affecting the parsing?

html

"

<div>
  

<div class="form-group purchaser-fullname required">
    
    <label class="control-label" for="id_purchaser-fullname"></label>
    
    
        <input class="form-control" id="id_purchaser-fullname" name="purchaser-fullname" placeholder="이름" type="text" />
    
    
    
    

    

    
</div>



<div class="form-group purchaser-phone">
    
    <label class="control-label" for="id_purchaser-phone"></label>
    
    
        <input class="form-control" id="id_purchaser-phone" name="purchaser-phone" placeholder="전화번호" type="tel" />
    
    
    
    

    

    
</div>



<div class="form-group purchaser-email required">
    
    <label class="control-label" for="id_purchaser-email"></label>
    
    
        <input class="form-control" id="id_purchaser-email" maxlength="128" name="purchaser-email" placeholder="Email 주소" type="email" />
    
    
    
    

    

    
</div>



<input id="id_purchaser-email_real" name="purchaser-email_real" placeholder="None" type="hidden" />


</div>
"


handler.dom


[Object, Object, Object]

array[3]
0
:
Object
data
:
"↵↵"
next
:
Object
parent
:
null
prev
:
null
type
:
"text"
__proto__
:
Object
1
:
Object
attribs
:
Object
children
:
Array[9]
name
:
"div"
next
:
Object
parent
:
null
prev
:
Object
type
:
"tag"
__proto__
:
Object
2
:
Object
data
:
"↵"
next
:
null
parent
:
null
prev
:
Object
type
:
"text"
__proto__
:
Object
length
:
3
__proto__
:
Array[0]

reduce lib size

It seems that the library is 1.38 MB, is there any chance to shrink it?
I think that the lib is very interesting but I wouldn't be able to use it if it is that big in size.

Dashed attributes not converted to camelCase

It seems like HTML / SVG elements containing dashed attributes (e.g. stroke-width) are not being converted to camelCase causing React to complain and throw out the props.

it('should handle dashed attributes', function () {
    var input = '<svg><circle stroke-width="2"></circle></svg>';
    var reactComponent = parser.parse(input);
    var reactHtml = ReactDOMServer.renderToStaticMarkup(reactComponent);

    assert.equal(reactHtml, input);
});
AssertionError: '<svg><circle></circle></svg>' == '<svg><circle stroke-width="2"></circle></svg>'
+ expected - actual
-<svg><circle></circle></svg>
+<svg><circle stroke-width="2"></circle></svg>

Not parse attributes of custom elements

Official react docs "One common confusion is that Web Components use "class" instead of "className"."
Html-to-react:
Input: <custom-node class="test"></custom-node>
Output: <custom-node classname="test"></custom-node>
React-tool(official parser JSX):
Input: <custom-node class="test"></custom-node>
Output: <custom-node class="test"></custom-node>

An in-range update of eslint is breaking the build 🚨

The devDependency eslint was updated from 5.14.0 to 5.14.1.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

eslint is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build failed (Details).
  • coverage/coveralls: First build on greenkeeper/eslint-5.14.1 at 100.0% (Details).

Release Notes for v5.14.1
  • 1d6e639 Fix: sort-keys throws Error at SpreadElement (fixes #11402) (#11403) (Krist Wongsuphasawat)
Commits

The new version differs by 3 commits.

  • b2e94d8 5.14.1
  • ce129ed Build: changelog update for 5.14.1
  • 1d6e639 Fix: sort-keys throws Error at SpreadElement (fixes #11402) (#11403)

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Script tag data should not be escaped

Double quotes in my script, in JSON for example, are escaped and appear as &quot, which breaks the script.

Example script that I'm trying to parse:

<script type='text/javascript' > window.NREUM||(NREUM={});NREUM.info = {"agent":"","beacon":"bam.nr-data.net","errorBeacon":"bam.nr-data.net","licenseKey":"abcdefgh","applicationID":"12345678","applicationTime":2393.08704,"transactionName":"Z1VQN0IFC0ACAhEIC14fdxtAFgBAEAsWTiN1ZB1MGg==","queueTime":0,"ttGuid":"a32406d67bc990","agentToken":null}; window.NREUM||(NREUM={}),__nr_require=function(e,n,t){function r(t){if(!n[t]){var o=n[t]={exports:{}};e[t][0].call(o.exports,function(n){var o=e[t][1][n];return r(o||n)},o,o.exports)}return n[t].exports}if("function"==typeof __nr_require)return __nr_require;for(var o=0;o<t.length;o++)r(t[o]);return r}({1:[function(e,n,t){function r(){}function o(e,n,t){return function(){return i(e,[c.now()].concat(u(arguments)),n?null:this,t),n?void 0:this}}var i=e("handle"),a=e(3),u=e(4),f=e("ee").get("tracer"),c=e("loader"),s=NREUM;"undefined"==typeof window.newrelic&&(newrelic=s);var p=["setPageViewName","setCustomAttribute","setErrorHandler","finished","addToTrace","inlineHit","addRelease"],d="api-",l=d+"ixn-";a(p,function(e,n){s[n]=o(d+n,!0,"api")}),s.addPageAction=o(d+"addPageAction",!0),s.setCurrentRouteName=o(d+"routeName",!0),n.exports=newrelic,s.interaction=function(){return(new r).get()};var m=r.prototype={createTracer:function(e,n){var t={},r=this,o="function"==typeof n;return i(l+"tracer",[c.now(),e,t],r),function(){if(f.emit((o?"":"no-")+"fn-start",[c.now(),r,o],t),o)try{return n.apply(this,arguments)}catch(e){throw f.emit("fn-err",[arguments,this,e],t),e}finally{f.emit("fn-end",[c.now()],t)}}}};a("actionText,setName,setAttribute,save,ignore,onEnd,getContext,end,get".split(","),function(e,n){m[n]=o(l+n)}),newrelic.noticeError=function(e,n){"string"==typeof e&&(e=new Error(e)),i("err",[e,c.now(),!1,n])}},{}],2:[function(e,n,t){function r(e,n){if(!o)return!1;if(e!==o)return!1;if(!n)return!0;if(!i)return!1;for(var t=i.split("."),r=n.split("."),a=0;a<r.length;a++)if(r[a]!==t[a])return!1;return!0}var o=null,i=null,a=/Version\/(\S+)\s+Safari/;if(navigator.userAgent){var u=navigator.userAgent,f=u.match(a);f&&u.indexOf("Chrome")===-1&&u.indexOf("Chromium")===-1&&(o="Safari",i=f[1])}n.exports={agent:o,version:i,match:r}},{}],3:[function(e,n,t){function r(e,n){var t=[],r="",i=0;for(r in e)o.call(e,r)&&(t[i]=n(r,e[r]),i+=1);return t}var o=Object.prototype.hasOwnProperty;n.exports=r},{}],4:[function(e,n,t){function r(e,n,t){n||(n=0),"undefined"==typeof t&&(t=e?e.length:0);for(var r=-1,o=t-n||0,i=Array(o<0?0:o);++r<o;)i[r]=e[n+r];return i}n.exports=r},{}],5:[function(e,n,t){n.exports={exists:"undefined"!=typeof window.performance&&window.performance.timing&&"undefined"!=typeof window.performance.timing.navigationStart}},{}],ee:[function(e,n,t){function r(){}function o(e){function n(e){return e&&e instanceof r?e:e?f(e,u,i):i()}function t(t,r,o,i){if(!d.aborted||i){e&&e(t,r,o);for(var a=n(o),u=v(t),f=u.length,c=0;c<f;c++)u[c].apply(a,r);var p=s[y[t]];return p&&p.push([b,t,r,a]),a}}function l(e,n){h[e]=v(e).concat(n)}function m(e,n){var t=h[e];if(t)for(var r=0;r<t.length;r++)t[r]===n&&t.splice(r,1)}function v(e){return h[e]||[]}function g(e){return p[e]=p[e]||o(t)}function w(e,n){c(e,function(e,t){n=n||"feature",y[t]=n,n in s||(s[n]=[])})}var h={},y={},b={on:l,addEventListener:l,removeEventListener:m,emit:t,get:g,listeners:v,context:n,buffer:w,abort:a,aborted:!1};return b}function i(){return new r}function a(){(s.api||s.feature)&&(d.aborted=!0,s=d.backlog={})}var u="nr@context",f=e("gos"),c=e(3),s={},p={},d=n.exports=o();d.backlog=s},{}],gos:[function(e,n,t){function r(e,n,t){if(o.call(e,n))return e[n];var r=t();if(Object.defineProperty&&Object.keys)try{return Object.defineProperty(e,n,{value:r,writable:!0,enumerable:!1}),r}catch(i){}return e[n]=r,r}var o=Object.prototype.hasOwnProperty;n.exports=r},{}],handle:[function(e,n,t){function r(e,n,t,r){o.buffer([e],r),o.emit(e,n,t)}var o=e("ee").get("handle");n.exports=r,r.ee=o},{}],id:[function(e,n,t){function r(e){var n=typeof e;return!e||"object"!==n&&"function"!==n?-1:e===window?0:a(e,i,function(){return o++})}var o=1,i="nr@id",a=e("gos");n.exports=r},{}],loader:[function(e,n,t){function r(){if(!E++){var e=x.info=NREUM.info,n=l.getElementsByTagName("script")[0];if(setTimeout(s.abort,3e4),!(e&&e.licenseKey&&e.applicationID&&n))return s.abort();c(y,function(n,t){e[n]||(e[n]=t)}),f("mark",["onload",a()+x.offset],null,"api");var t=l.createElement("script");t.src="https://"+e.agent,n.parentNode.insertBefore(t,n)}}function o(){"complete"===l.readyState&&i()}function i(){f("mark",["domContent",a()+x.offset],null,"api")}function a(){return O.exists&&performance.now?Math.round(performance.now()):(u=Math.max((new Date).getTime(),u))-x.offset}var u=(new Date).getTime(),f=e("handle"),c=e(3),s=e("ee"),p=e(2),d=window,l=d.document,m="addEventListener",v="attachEvent",g=d.XMLHttpRequest,w=g&&g.prototype;NREUM.o={ST:setTimeout,SI:d.setImmediate,CT:clearTimeout,XHR:g,REQ:d.Request,EV:d.Event,PR:d.Promise,MO:d.MutationObserver};var h=""+location,y={beacon:"bam.nr-data.net",errorBeacon:"bam.nr-data.net",agent:"js-agent.newrelic.com/nr-1118.min.js"},b=g&&w&&w[m]&&!/CriOS/.test(navigator.userAgent),x=n.exports={offset:u,now:a,origin:h,features:{},xhrWrappable:b,userAgent:p};e(1),l[m]?(l[m]("DOMContentLoaded",i,!1),d[m]("load",r,!1)):(l[v]("onreadystatechange",o),d[v]("onload",r)),f("mark",["firstbyte",u],null,"api");var E=0,O=e(5)},{}]},{},["loader"]);</script>

How do I change css of processed node?

I'm trying to do something like below in a processing instruction. The code below is definitely not-working as the node argument is by no means a DOM node.

Anyone has any ideas how this can be implemented?

// code not working
{
    shouldProcessNode: node => node.attribs && node.attribs['data-bg-image'],
    processNode: ( node, children, i ) => {
      const $el = $( node ); // node cannot be jquer-ied this way
      $el.css( { 'background-image': 'url(' + node.attribs['data-bg-image'] + ')' } );
      return children;
    }
  }

Should href attribute be encoded

We are using this library to parse and display content from wordpress-api and there & in urls are encoded to &

When we parse and insert content with html-to-react these attributes are encoded another time, resulting in links that are not working:

<a href="http://domain.com/search?query=xy&amp;amp;publications=5">Some link</a>

Is this a bug in html-to-react? It it is, I can submit an PR

[Request] ES6 Example

Hi everyone!

First of all thanks for a great project. I wonder if anybody has an example of usage with ES6?

Replacing children of an element

Hi,

I am testing your package and I like it so far, but I have found a weird behavior when replacing children.

This is your example:

var React = require('react');
var HtmlToReact = require('html-to-react');
var HtmlToReactParser = require('html-to-react').Parser;
 
var htmlToReactParser = new HtmlToReactParser();
var htmlInput = '<div><div data-test="foo"><p>Text</p><p>Text</p></div></div>';
var htmlExpected = '<div><div data-test="foo"><h1>Heading</h1></div></div>';
 
var isValidNode = function () {
  return true;
};
 
var processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions(React);
 
// Order matters. Instructions are processed in
// the order they're defined
var processingInstructions = [
  {
    // This is REQUIRED, it tells the parser
    // that we want to insert our React
    // component as a child
    replaceChildren: true,
    shouldProcessNode: function (node) {
      return node.attribs && node.attribs['data-test'] === 'foo';
    },
    processNode: function (node, children, index) {
      return React.createElement('h1', {key: index,}, 'Heading');
    }
  },
  {
    // Anything else
    shouldProcessNode: function (node) {
      return true;
    },
    processNode: processNodeDefinitions.processDefaultNode,
  },
];
 
var reactComponent = htmlToReactParser.parseWithInstructions(
  htmlInput, isValidNode, processingInstructions);
var reactHtml = ReactDOMServer.renderToStaticMarkup(
  reactComponent);
assert.equal(reactHtml, htmlExpected);

It behaves exactly like you have explained which is awesome.

But when I try the following:

var React = require('react');
var HtmlToReact = require('html-to-react');
var HtmlToReactParser = require('html-to-react').Parser;
 
var htmlToReactParser = new HtmlToReactParser();
var htmlInput = '<div><div data-test="foo" /><div data-test="foo" /><div data-test="foo" /></div>';
var htmlExpected = '<div><div data-test="foo"><h1>Heading</h1></div><div data-test="foo"><h1>Heading</h1></div><div data-test="foo"><h1>Heading</h1></div></div>';
 
var isValidNode = function () {
  return true;
};
 
var processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions(React);
 
// Order matters. Instructions are processed in
// the order they're defined
var processingInstructions = [
  {
    // This is REQUIRED, it tells the parser
    // that we want to insert our React
    // component as a child
    replaceChildren: true,
    shouldProcessNode: function (node) {
      return node.attribs && node.attribs['data-test'] === 'foo';
    },
    processNode: function (node, children, index) {
      return React.createElement('h1', {key: index,}, 'Heading');
    }
  },
  {
    // Anything else
    shouldProcessNode: function (node) {
      return true;
    },
    processNode: processNodeDefinitions.processDefaultNode,
  },
];
 
var reactComponent = htmlToReactParser.parseWithInstructions(
  htmlInput, isValidNode, processingInstructions);
var reactHtml = ReactDOMServer.renderToStaticMarkup(
  reactComponent);
assert.equal(reactHtml, htmlExpected);

It doesn't work, it only outputs the result once instead of replacing it 3 times.
But if I have closing div tags, it works.

Is this normal behavior? I am just asking.

Thanks!

actual DOM node to React element

Hi,

I have a scenario where i am creating a DIV node by using document.createElement('div') which i am then passing to an external library which inserts some mark up into that DIV. and after it inserts that markup, i want to use it in my render method to render the entire VIEW.

Is there an API in html-to-react to do that?

[Bug] bgcolor and align for td is not working

Hi,
If i pass

<td align="center" bgcolor="#e9703e" id="idw4sc" style="box-sizing: border-box; border-radius: 3px;">

it doesn't render align and bgcolor..
I'm working on email templates and the reason why use bgcolor is that in order for it to look nice in all the email clients even including outlook, that one needs to have bgcolor instead of background-color using css.

Thank you.

how to modify different tags

hi, this is not an issue, is just that i want to set different rules for each tag, and i was wondering what is the syntax of that, since in you're example you show only one rule.

Also, i have an 'a' tag with some children and some atributes.
How can i change the attributes for the a tag? What should i return in the processNode function?

thank you for this great library

[question]: Does this work with React routing?

For example, if I passed this HTML string;

Dashboard

Would it be recognized as a valid router link?

In Angular this is very difficult to do at runtime, so I am starting to look into React to see if the issue can be solved.

Thanks

Whitespace warnings

Code like <div style="margin: 0; padding: 0;"> yields this warning:

image

Omitting the spaces (margin:0;padding:0;) works as a workaround.

An in-range update of react is breaking the build 🚨

There have been updates to the react monorepo:

    • The devDependency react was updated from 16.6.1 to 16.6.2.
  • The devDependency react-dom was updated from 16.6.1 to 16.6.2.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

This monorepo update includes releases of one or more dependencies which all belong to the react group definition.

react is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build could not complete due to an error (Details).

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

How to transform ↵ symbol?

Hi. Thanks for cool module. But I encountered issue that I need to transform ↵ symbol into
tagname. Is it a feature that I might accomplish with html-to-react?

Version 10 of node.js has been released

Version 10 of Node.js (code name Dubnium) has been released! 🎊

To see what happens to your code in Node.js 10, Greenkeeper has created a branch with the following changes:

  • Added the new Node.js version to your .travis.yml
  • Replaced the old Node.js version in your .nvmrc with the new one

If you’re interested in upgrading this repo to Node.js 10, you can open a PR with these changes. Please note that this issue is just intended as a friendly reminder and the PR as a possible starting point for getting your code running on Node.js 10.

More information on this issue

Greenkeeper has checked the engines key in any package.json file, the .nvmrc file, and the .travis.yml file, if present.

  • engines was only updated if it defined a single version, not a range.
  • .nvmrc was updated to Node.js 10
  • .travis.yml was only changed if there was a root-level node_js that didn’t already include Node.js 10, such as node or lts/*. In this case, the new version was appended to the list. We didn’t touch job or matrix configurations because these tend to be quite specific and complex, and it’s difficult to infer what the intentions were.

For many simpler .travis.yml configurations, this PR should suffice as-is, but depending on what you’re doing it may require additional work or may not be applicable at all. We’re also aware that you may have good reasons to not update to Node.js 10, which is why this was sent as an issue and not a pull request. Feel free to delete it without comment, I’m a humble robot and won’t feel rejected 🤖


FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

[Question] How to override HTML component to JSX

Hello!

Could you give me some advice. I want to override raw html to JSX.

so, I have plain HTML like this:

<h1>Header<h1>
<div>Text
   <div>Some another text</div>
</div>
<code> hello word</code>

and react components:

export const Code = ({ children }) => (
  <div className="code">{children}</div>
);

export const H1 = ({ children }) => (
  <div className="h1">{children}</div>
);

so, I want to render like this:

<h1 class="h1">Header<h1>
<div>Text
   <div>Some another text</div>
</div>
<code class="code"> hello word</code>

and save all of children after overriding.

The code will be like:

import { Parser } from 'html-to-react';
  const processingInstructions = [
      {
        shouldProcessNode(node) {
          return node.type === 'tag';
        },
        processNode(node, children, index) {
          if (node.name === "h1") {
               return <H1>{children}</H1>
          }
    ];

    const isValidNode = function() {
      return true;
    };

    const reactHtml = new Parser().parseWithInstructions(
      text,
      isValidNode,
      processingInstructions
    );

Thanks!

Question: Why do empty attributes get the key as value

Is there a reason why empty attributes get the key as value?

This is the part of the code responsible for this behaviour. It looks intentionally written but it gives unwanted behaviour in my project.

result[key] = value || key;

Using with webpack: ERROR in ./~/ent/reversed.json

Hi. I encountered this error. I installed https://www.npmjs.com/package/json-loader but this didn't help me.
Full list of errors in bundle

  1. ERROR in ./~/ent/entities.json Module parse failed: D:\reflex\front-end\node_modules\ent\entities.json Unexpected token (2:13)
    You may need an appropriate loader to handle this file type.
  2. ERROR in ./~/ent/reversed.json
    Module parse failed: D:\reflex\front-end\node_modules\ent\reversed.json Unexpected token (2:7)
    You may need an appropriate loader to handle this file type.
  3. ERROR in ./~/entities/maps/legacy.json
    Module parse failed: D:\reflex\front-end\node_modules\entities\maps\legacy.json Unexpected token (1:9)
    You may need an appropriate loader to handle this file type.
  4. ERROR in ./~/entities/maps/xml.json
    Module parse failed: D:\reflex\front-end\node_modules\entities\maps\xml.json Unexpected token (1:6) You may need an appropriate loader to handle this file type.
  5. ERROR in ./~/entities/maps/entities.json
    Module parse failed: D:\reflex\front-end\node_modules\entities\maps\entities.json Unexpected token (1:9)
    You may need an appropriate loader to handle this file type.
  6. ERROR in ./~/entities/maps/decode.json
    Module parse failed: D:\reflex\front-end\node_modules\entities\maps\decode.json Unexpected token (1:4)

import fails

In a react component, instead of var HtmlToReactParser = require('html-to-react').Parser; I wanted to use import HtmlToReactParser from 'html-to-react'; but it fails with "xx is not a constructor" when I call var parser = new HtmlToReactParser();

Do you recommend a way to do this ?

html-to-react crashes on invalid style tag

function createStyleJsonFromString(styleString) {
  styleString = styleString || '';
  var styles = styleString.split(/;(?!base64)/);
  var singleStyle, key, value, jsonStyles = {};
  for (var i = 0; i < styles.length; ++i) {
    singleStyle = styles[i].split(':');
    if (singleStyle.length > 2) {
      singleStyle[1] = singleStyle.slice(1).join(':');
    }
    
    key = camelize(singleStyle[0]);
    value = singleStyle[1];
    //
    // If key or value are undefined this blows up
    //
    if (key.length > 0 && value.length > 0) {
      jsonStyles[key] = value;
    }
  }
  return jsonStyles;
}

Updating children nodes of parent and passing them to be processed.

Hi,

My question is quite peculiar and I believe is related to this issue.

To be as clear as possible, here is my situation.
In my HTML string I have a form with input fields.
I have a rule that checks for the form tag and then renders a react component for the form and passes its children.

I have another rule that processes the input nodes and renders react components.

I want to be able to determine if the input being processed is actually a child of the parent form tag.
In case the HTML string has input fields that are not under a form tag, etc. (for whatever reason)

So I was thinking that maybe I could process the node.children and set an attribute for each children matching the input field under the form node. I got the idea from here.

The problem is that when I do it like that, it doesn't process the other rules, for the same node.

So I was wondering, if there would be an easy way I could achieve this?

At first I was thinking of adding an attribute on the form tag, say form-identifier="something".
And then in the input node, go up the parent tree and look for that attribute.
Or do the reverse, and tag its children with an attribute, so I can recognize it when I get to the child in another rule.

Any help would be appreciated.

Great library!

Whitespace text nodes cannot appear as a child of <table>

When I want to create a React table from a multi line string I will get a warning from React:

index.js:1452 Warning: validateDOMNesting(...): Whitespace text nodes cannot appear as a child of <table>. Make sure you don't have any extra whitespace between tags on each line of your source code.

Example code that will trigger the error

render() {
  const table = parser.parse(`
    <table>
      <tbody>
        <tr>
          <td>Lorem Ipsum</td>
        </tr>
      </tbody>
    </table>
  `);

  return (
    <React.Fragment>
      {table}
    </React.Fragment>
  );

Off course I can fix this warning by writing the table on one line, but that is not the solution where I'm looking for.
const table = parser.parse('<table><tbody><tr><td>Lorem Ipsum</td></tr></tbody></table>');

With dangerouslySetInnerHTML I can use a multi line string without any problems. I also tried the package html-react-parser but it will have the same problem.

Thanks for your time and help,
Ben

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.