posit-dev / py-htmltools

Tools for HTML generation and output
MIT License
19 stars 2 forks source link

Render neighboring inline tags with no whitespace between them #54

Closed wch closed 1 year ago

wch commented 1 year ago

This PR makes the following changes.

Each tag function gets a boolean parameter _add_ws, which defaults to False for (normally) inline components like <span>, and True for (normally) block components like <div>. The user can call those functions with a different value, because it is possible for CSS to change a component from inline to block and vice versa.

If two inline tags (like <span> or plain strings), are next to each other, they will be rendered with no whitespace between them:

span(span("a"), span("b"))
<span><span>a</span><span>b</span></span>

span("a", "b")
<span>ab</span>

The examples above show what happens if the inline tags are in another inline tag. Note that there's no whitespace between the opening <span> tag and the first child.

If they are within a block tag like div, there will be whitespace between the opening <div> tag and the first child.

div(span("a"), span("b"))
<div>
  <span>a</span><span>b</span>
</div>

div("a", "b")
<div>
  ab
</div>

If a block tag contains a block tag, each of the block children will be on its own line. If there are multiple inline children in sequence, they will render without whitespace between them.

div(div("a"), span("b"), span("c"), div("d"))
<div>
  <div>a</div>
  <span>b</span><span>c</span>
  <div>d</div>
</div>

I have not described the behavior when an inline tag contains a block tag, like span(div("a")). This is because in normal HTML, block tags can contain inline or block tags, but inline tags can only contain inline tags -- inline tags cannot contain block tags.

I added many tests for the correct whitespace behavior.

Note: One important change is that when a user writes code like this, no whitespace will be added automatically:

div("Hello", "world!")

Previously, that would render as:

<div>
  Hello world!
</div>

Now it will render as:

<div>
  Helloworld!
</div>

This is a significant change, but it's best to do it now rather than later.