meliorence / react-native-render-html

iOS/Android pure javascript react-native component that renders your HTML into 100% native views
https://meliorence.github.io/react-native-render-html/
BSD 2-Clause "Simplified" License
3.48k stars 589 forks source link

Unordered List not nesting properly #522

Closed mineminemine closed 3 years ago

mineminemine commented 3 years ago

Decision Table

Good Faith Declaration

Description

I believe this is a similar issue to this one here #173 but the issue I'm posting now is the other way around.

Code:

<ul>
  <li>Tea</li>
  <li>Coffee</li>
  <ol>
    <li>Dog</li>
    <li>Cat</li>
  </ol>
</ul>

Expected result:

Actual result:

React Native Information

Expo CLI 4.11.0 environment info:
    System:
      OS: Windows 10 10.0.19043
    Binaries:
      Node: 14.17.6 - C:\Program Files\nodejs\node.EXE
      Yarn: 1.22.11 - C:\Users\Ryan\AppData\Roaming\npm\yarn.CMD
      npm: 6.14.15 - C:\Program Files\nodejs\npm.CMD
    IDEs:
      Android Studio: Version     2020.3.0.0 AI-203.7717.56.2031.7678000
    npmPackages:
      expo: ^41.0.1 => 41.0.1
      react: ^17.0.2 => 17.0.2
      react-dom: ^17.0.2 => 17.0.2
      react-native: ^0.64.2 => 0.64.2
      react-native-web: ~0.13.12 => 0.13.18
    Expo Workflow: bare

RNRH Version

6.1.0

Tested Platforms

Reproduction Platforms

Minimal, Reproducible Example

https://snack.expo.dev/Kgn-_Ny45

Additional Notes

No response

mineminemine commented 3 years ago
<ol>
  <li>Tea</li>
  <li>Coffee</li>
  <ul>
    <li>Dog</li>
    <li>Cat</li>
  </ul>
</ol>

Now that I look at it again, having the ol and ul the other way around would also give unwanted result. https://snack.expo.dev/qDwgiy5Ti

jsamr commented 3 years ago

@mineminemine Did you try to validate your HTML snippet against W3C validator such as recommended in our guidelines? The test fails because you can only have li as children of ul and ol:

image

jsamr commented 3 years ago

@mineminemine Since it is non-compliant, the parser library (htmlparser2) attempts a fix by wrapping ol with li element!

mineminemine commented 3 years ago

@jsamr Oh my god, truly sorry for this. This just goes to show that I've been doing nested list wrongly all these years.. I'll just close this issue since mistake was on my end. Sorry and thank you for your time.

jsamr commented 3 years ago

@mineminemine no worries, we all make mistakes :-) Browsers are kind of unhelpful to learn those things because of their laxness...

federicoeventifica commented 3 years ago

Hello, I'm facing the same problem, and after reading this thread I found out that the issue is the malformed html and not the react-native-render-html.

Do you know any library or function that transforms an html with this format:

<ol>
  <li>Tea</li>
  <li>Coffee</li>
  <ul>
    <li>Dog</li>
    <li>Cat</li>
  </ul>
</ol>

to this (the right way):

<ol>
  <li>Tea</li>
  <li>Coffee
  <ul>
    <li>Dog</li>
    <li>Cat</li>
  </ul>
  </li>
</ol>

Thank you, Federico

jsamr commented 3 years ago

@federicoeventifica If you want to do this transformation front-wise, you could use DOM tempering features available in this library, see this documentation page: https://meliorence.github.io/react-native-render-html/docs/guides/dom-tampering

KevinUreAtIndustryX0 commented 3 years ago

Hello, i have more questions after federicoeventifica's post the validator used above claims the following is invalid, as previously discussed.

<ul>
  <li>Outer</li>
  <ul>
    <li>Inner</li>
  </ul>
</ul>

but i would expect the right way to be the following:

<ul>
  <li>Outer</li>
  <li>
    <ul>
      <li>Inner</li>
    </ul>
  </li>
</ul>

that code passes validation using the tool but is still rendered incorrectly by the library

Screen Shot 2021-10-19 at 11 08 54 AM

federicoeventifica's method above or putting the nested list inside of the previous element seems like a hack to me, but also passes validation so i tried it and theres an unexpected line break

<ul>
  <li>Outer
    <ul>
      <li>Inner</li>
    </ul>
  </li>
</ul>
Screen Shot 2021-10-19 at 11 11 09 AM

Snack: https://snack.expo.dev/IldfgWh41

jsamr commented 3 years ago

@KevinUreAtIndustryX0

that code passes validation using the tool but is still rendered incorrectly by the library

Below is a snapshot of this snippet rendered in Mozilla Firefox:

image

What are you expecting exactly?

there's an unexpected line break

By “rendering incorrectly” you mean “not as many User Agents would”, which is often a matter of how their embedded stylesheet looks like. Although we try to match popular browsers' styles more than we don't, there is no standard requirement regarding those styles and we don't claim such informal compliance.

If you don't want this behavior, you could implement a custom renderer which removes any top / bottom margins in nested lists such as (typescript example):

import React from 'react';
import {SafeAreaView, useWindowDimensions} from 'react-native';
import RenderHTML, {CustomBlockRenderer} from 'react-native-render-html';

const htmlContent = `
<ul>
  <li>Outer
    <ul>
      <li>Inner</li>
    </ul>
  </li>
</ul>
`;

const CustomListRenderer: CustomBlockRenderer = ({
  InternalRenderer,
  ...props
}) => {
  const fixMarginStyle =
    props.tnode.markers.olNestLevel >= 1 || props.tnode.markers.ulNestLevel >= 1
      ? {marginTop: 0, marginBottom: 0}
      : null;
  return (
    <InternalRenderer {...props} style={{...props.style, ...fixMarginStyle}} />
  );
};

const renderers = {
  ol: CustomListRenderer,
  ul: CustomListRenderer,
};

export default function App() {
  const {width} = useWindowDimensions();
  return (
    <SafeAreaView style={{flex: 1, backgroundColor: 'white'}}>
      <RenderHTML
        renderers={renderers}
        source={{html: htmlContent}}
        contentWidth={width}
      />
    </SafeAreaView>
  );
}

PS: Note that with the upcoming v6.2 release, you'll be able to define user agent styles with more ease.

EDIT: added notes about compliance considerations.

KevinUreAtIndustryX0 commented 3 years ago

@jsamr

If you don't want this behavior, you could implement a custom renderer which removes any top / bottom margins in nested lists such as (typescript example):

import React from 'react';
import {SafeAreaView, useWindowDimensions} from 'react-native';
import RenderHTML, {CustomBlockRenderer} from 'react-native-render-html';

const htmlContent = `
<ul>
  <li>Outer
    <ul>
      <li>Inner</li>
    </ul>
  </li>
</ul>
`;

const CustomListRenderer: CustomBlockRenderer = ({
  InternalRenderer,
  ...props
}) => {
  const fixMarginStyle =
    props.tnode.markers.olNestLevel >= 1 || props.tnode.markers.ulNestLevel >= 1
      ? {marginTop: 0, marginBottom: 0}
      : null;
  return (
    <InternalRenderer {...props} style={{...props.style, ...fixMarginStyle}} />
  );
};

const renderers = {
  ol: CustomListRenderer,
  ul: CustomListRenderer,
};

export default function App() {
  const {width} = useWindowDimensions();
  return (
    <SafeAreaView style={{flex: 1, backgroundColor: 'white'}}>
      <RenderHTML
        renderers={renderers}
        source={{html: htmlContent}}
        contentWidth={width}
      />
    </SafeAreaView>
  );
}

Awesome example here, as i dug into the library this was an outstanding question of mine, thank you

image

What are you expecting exactly?

I'm expecting the circled dot not to be there

Screen Shot 2021-10-20 at 10 59 23 AM
jsamr commented 3 years ago

@KevinUreAtIndustryX0 you're welcome :-)

I'm expecting the circled dot not to be there

Not sure there is much we can do here! The dot is mandated by the WHATWG standard; any <li> element will have a marker painted before the block formatting context it creates... You can try this snippet in multiple browsers if you wish, I bet you will find out the dark dot will always be there!

KevinUreAtIndustryX0 commented 3 years ago

@jsamr i checked in chrome and firefox and it does indeed render the extra dot with the strictly compliant html5 but i also checked it with the example that my CotS backend returns (the strictly invalid html)

<ul>
  <li>Outer</li>
  <ul>
    <li>Inner</li>
  </ul>
</ul>

and both of them render what my backend expects them to

Screen Shot 2021-10-20 at 11 17 14 AM

so i guess my question is this: is there a way for me to change the behavior of rendered to remove that black dot thats on a line of its own? perhaps using the above custom block renderer?

Screen Shot 2021-10-20 at 11 20 29 AM
jsamr commented 3 years ago

@KevinUreAtIndustryX0 Unfortunately I don't see an easy workaround. You would have to implement your own custom ul renderer and implement the feature yourself, or offer a PR to allow this "fault tolerant" behavior...

ashwanihundwani1986 commented 2 years ago

How to change the bullet icon but keep the content same as with new CustomRenderer(V6) we do not get the React DOM in CustomRendererProps the way we used to get it (V5). Simulator Screen Shot - iPhone 13 - 2022-07-13 at 21 44 48

Nantris commented 2 years ago

I don't understand if this is still an issue? Is the extra bullet caused by the custom renderer? Because we don't see that with the default renderers with identically nested HTML lists.

mgutbezahl commented 4 months ago

I wrote this solution to "clean up" the nested lists, maybe need more styling on the final result, but it works

Funtion:

sanitizeListHTML: (element) => {
    if (['ol', 'ul'].includes(element.tagName)) {
      const { children } = element;
      for (let i = 0; i < children.length; i++) {
        const child = children[i];
        let nextSibling = children[i + 1];
        while (nextSibling && ['ol', 'ul'].includes(nextSibling.tagName)) {
          child.children.push(nextSibling);
          children.splice(i + 1, 1); // Remove the nested list from its original position
          nextSibling = children[i + 1];
        }
      }
    }
  },

And usit like this on the component:

<RenderHTML
          contentWidth={width}
          source={{ html: props.text?.trim() }}
          tagsStyles={tagsStyles}
          domVisitors={{ onElement: helpers.sanitizeListHTML }}
        />

Captura de pantalla 2024-06-19 a las 15 40 14 p  m

Nantris commented 4 months ago

That's very nifty! Thanks for sharing!

Maybe I'm reading it wrong, but my interpretation is that you're un-nesting the lists, but then I'm confused how the indentation is being handled? The end result looks great though!

mgutbezahl commented 4 months ago

That's very nifty! Thanks for sharing!

Maybe I'm reading it wrong, but my interpretation is that you're un-nesting the lists, but then I'm confused how the indentation is being handled? The end result looks great though!

I'm using this https://github.com/meliorence/react-native-render-html/issues/522#issuecomment-946825772 as a solution, making the "correct" format for the HTML to be render.

Nantris commented 4 months ago

Very cool! Thanks for taking the time to explain!