asciidoctor / asciidoctor.js

:scroll: A JavaScript port of Asciidoctor, a modern implementation of AsciiDoc
https://asciidoctor.org
MIT License
729 stars 135 forks source link

Bug: Inline macro is splitting raw input in two paragraphs #1717

Closed konsalex closed 9 months ago

konsalex commented 9 months ago

Hey there,

I am using Asciidoctor.js alongside Antora, and the extension looks like this:

function labelInline() {
  const self = this;
  self.named('label')

  self.process(function (parent, target, attrs) {
    console.log(target);
    // Target will be the type of label
    // We have the following types:
    // - info
    // - warning
    // - danger
    // - success
    let color;
    if(target==="info"){
      color = "primary";
    } else {
      color = target;
    }

    const html = `<span role="status" aria-label="label" class="ndl-label n-bg-palette-${color}-bg-strong n-text-palette-neutral-text-inverse ndl-filled"><div class="ndl-label-content"><span title="Label" class="ndl-label-text">Label</span></div></span>`
    return self.createInline(parent, "quoted", html, {type: 'unquoted'})
  })
}

CleanShot 2023-12-05 at 10 52 26@2x

The generated output as in the image above is splitting the top level span, and then the body to another div of each one. Is there something I am doing wrong in the extension that could fix it? Also this is the only inline macro extension I am using.

ggrossetie commented 9 months ago

This is the expected behavior of HTML, <div> is not a permitted content of <span>: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span

As a result, the <div> will be moved outside of the <span> by the browser/HTML engine.

konsalex commented 9 months ago

Thanks for the prompt response @ggrossetie.

While not permitted still can be rendered from browsers. Example from a component library we want to use and render the labels.

CleanShot 2023-12-11 at 09 31 12@2x

A workflow I am trying to establish is Antora -> Asciidoctor -> React.renderToString which may result to an issue like this.

Would there be a way to avoid this behaviour and embed the raw html string?

ggrossetie commented 9 months ago

Sorry, the reason is that the <div> is inside a <p> element:

The start tag is required. The end tag may be omitted if the <p> element is immediately followed by an (...) <div>, (...) or another <p> element, or if there is no more content in the parent element (...)

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p

The <div> will effectively close the paragraph element. That's why you have an empty <p></p> in the first screenshot. I would strongly recommend to use a more semantic structure for an inline component/element.

konsalex commented 9 months ago

@ggrossetie thanks for the explanation. Indeed the div only approach works as expected, but the issue in our case is that we cannot always control the HTML that is produced from React.renderToString().

Is there an alternative way to avoid the extra <p> wrapping, a mechanism essentially to create inline with raw HTML, and not having asciidoctor intercepting the created element?

ggrossetie commented 9 months ago

I guess you will need a custom converter as the built-in HTML5 converter puts the content (inline nodes) inside a <p> element:

https://github.com/asciidoctor/asciidoctor/blob/1e6357ec8006782c8f1c65abd76f797912065256/lib/asciidoctor/converter/html5.rb#L794

mojavelinux commented 9 months ago

In the future, I think this sort of question should be moved to the project chat at https://chat.asciidoctor.org.