facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
225.29k stars 45.94k forks source link

ReactJS: ascii art issue after JSX transformation #4698

Closed alansouzati closed 8 years ago

alansouzati commented 8 years ago

I'm trying to get an ascii art to be properly rendered by my React application.

After jsx-transformer is executed my art looses the format and renders pretty strange in the browser. It seems to be related to not identifying line breaks properly.

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hello World!</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/JSXTransformer.js"></script>
</head>
<body>
  <div id="content"></div>
  <script type="text/jsx">

    var App = React.createClass({
      render: function() {
        return (
          <pre>
            <code>
              +--------+   +-------+    +-------+
              |        |   + ditaa +    |       |
              |  Text  |   +-------+    |diagram|
              |Document|   |!magic!|    |       |
              |        |   |       |    |       |
              +---+----+   +-------+    +-------+
            </code>
          </pre>
        );
      }
    });

    var element = document.getElementById('content');
    React.render(React.createElement(App), element);
  </script>
</body>
</html>

This is just an example, and the real context is in a website generator that starts with markdown -> html -> jsx. That said, I cannot change the React.createClass element.

Someone proposed this:

var App = React.createClass({
  render: function() {
    var ascii = [
      "+--------+   +-------+    +-------+",
      "|        |   + ditaa +    |       |",
      "|  Text  |   +-------+    |diagram|",
      "|Document|   |!magic!|    |       |",
      "|        |   |       |    |       |",
      "+---+----+   +-------+    +-------+",
    ].join('\n');

    return (
      <pre>
        <code>
          {ascii}
        </code>
      </pre>
    );
  }
});

It fixes the problem, but I cannot edit the markdown elements to reflect this. I'm more interested to know why JSX transformation is not identifying to line breaks inside code blocks.

Alan

sophiebits commented 8 years ago

JSX collapses whitespace which is almost always what you want but is obviously inconvenient in this case (sorry). If you're using an ES6 compiler like Babel (just swap out JSXTransformer.js for https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.6.15/browser.min.js and use text/babel instead of text/jsx) then you can use an ES6 template string

<code>{`
  +--------+   +-------+    +-------+
  |        |   + ditaa +    |       |
  |  Text  |   +-------+    |diagram|
  |Document|   |!magic!|    |       |
  |        |   |       |    |       |
  +---+----+   +-------+    +-------+
`}</code>

which may be more palatable.

alansouzati commented 8 years ago

Thank you so much @spicyj!

alansouzati commented 8 years ago

Actually it turns out that adding only {...} fixes the problem, I did not have to switch to Babel. Any idea why it works?

This is the final version that works using jsx-transformer.

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hello World!</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/JSXTransformer.js"></script>
</head>
<body>
  <div id="content"></div>
  <script type="text/jsx">

    var App = React.createClass({
      render: function() {
        return (
          <pre>
            <code>{`
              +--------+   +-------+    +-------+
              |        |   + ditaa +    |       |
              |  Text  |   +-------+    |diagram|
              |Document|   |!magic!|    |       |
              |        |   |       |    |       |
              +---+----+   +-------+    +-------+
            `}</code>
          </pre>
        );
      }
    });

    var element = document.getElementById('content');
    React.render(React.createElement(App), element);
  </script>
</body>
</html>
zpao commented 8 years ago

JSXTransformer also supported template literals. @spicyj was trying to sneakily get you to upgrade since we deprecated JSXTransformer :)

alansouzati commented 8 years ago

smart move hahahah, thanks guys! In Grommet we are using babel and react, but for the sake of the example I used JSXTransformer.

Again, thanks for this awesome technology.

alansouzati commented 8 years ago

Sorry guys, I'm trying to understand this issue better. Sorry if I'm out of context here.

Could you please explain why React collapses whitespaces inside pre code tags? I've checked this PR https://github.com/facebook/react/pull/480 and I definitely agree that we should collapse for cases like this:

<div     className="testing">Adding unnecessary whitespace.</div>

But for the pre tag code seems weird to me.

I did a quick test on how github handles the pre code tags and they do preserve the spacing I provide.

See the ascii art here:

            +--------+   +-------+    +-------+
            |        | --+ ditaa +--> |       |
            |  Text  |   +-------+    |diagram|
            |Document|   |!magic!|    |       |
            |        |   |       |    |       |
            +---+----+   +-------+    +-------+
                :                         ^
                |       Lots of work      |
                +-------------------------+

The template literal works for a standalone scenario. But I'm trying to build something that converts markdown to JSX to be able to easily create websites based on React. I cannot control the user markdown to decide whether to put the template literal or not. And putting the template literal inside the markdown also sounds strange.

Also, I'm trying to replicate this markdown in JSX, all the pre code tags that explains markdown are in one line after the transpilation process.

For example, I'm expecting this:

  # H1
  ## H2
  ### H3
  #### H4
  ##### H5
  ###### H6

  Alternatively, for H1 and H2, an underline-ish style:

  Alt-H1
  ======

  Alt-H2
  ------

But I get this:

# H1 ## H2 ### H3 #### H4  ##### H5 ###### H6 Alternatively, for H1 and H2, an underline-ish style: Alt-H1 ======  Alt-H2  ------
browniefed commented 8 years ago

@alansouzati I had this issue, ended up writing a babel-plugin for it, by no means is it perfect but it works. Uses remarkable to do Markdown => HTML => Babel-AST => React Components. Feedback welcome if it helps you out. https://github.com/browniefed/babel-plugin-markdown-react

Edit: Previously used template literals and dangerouslySetInnerHtml, however that would mean you'd have to escape code block back ticks, or use an alternative string syntax, and replacement before processing.

The other issue is all the leading spaces. I didn't know an elegant way but I measure the least long left white space and remove it from all other lines with text on them. It seems to work but is not bulletproof by any means.

alansouzati commented 8 years ago

thanks @browniefed I will definitely check this plugin!

browniefed commented 8 years ago

No worries, if you're using webpack, config is pretty straight forward "babel-loader?plugins[]=babel-plugin-markdown-react" or setup a babelrc file with the plugin in it.

alansouzati commented 8 years ago

Hey @browniefed I've successfully integrated the plugin into my pipeline, but the same problem persists.

Can you try this in your environment?

import React from "react";
import Markdown from "babel-plugin-markdown-react/markdown";

export default class RenderMarkdown extends React.Component {

    render() {
        return (
            <Markdown>
              # H1

              ## H2
            ```
        </Markdown>
    )
}

}


It could be something wrong with my env, but the execution of this results in a single line element, as in:

H1 ## H2

browniefed commented 8 years ago

Did you intentionally wrap it in back tick code marks? I'll take a look though

syranide commented 8 years ago

@alansouzati <pre> and <code> are HTML-specific which JSX isn't, so even if we wanted to we couldn't. IMHO, generally I would not recommend writing large amounts of text inside JSX but instead putting it in a string (inside render or outside the component) and referencing that instead.

browniefed commented 8 years ago

@alansouzati take this to an issue on my plugin. We can sufficiently call this closed.