~loges/haitch

49c88ede7dc5c187bd9df26e6132f33b6a040784 — Logan Connolly a month ago 635d3f6
feat: pass unescaped HTML with `unsafe` component

Implements: https://todo.sr.ht/~loges/haitch/12
M CHANGELOG.md => CHANGELOG.md +6 -0
@@ 5,6 5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added

- Add `unsafe` component for passed unescaped HTML as an element.

## 0.8.2 - 2024-03-22

### Changed

M src/haitch/__init__.py => src/haitch/__init__.py +2 -0
@@ 45,6 45,7 @@ to HTML, you need to invoke the underlying `__str__` method:

from haitch._components._fragment import fragment
from haitch._components._html5 import html5
from haitch._components._unsafe import unsafe
from haitch._elements._a import AnchorElement, a
from haitch._elements._body import BodyElement, body
from haitch._elements._div import DivElement, div


@@ 176,6 177,7 @@ __all__ = [
    "tr",
    "track",
    "ul",
    "unsafe",
    "wbr",
]


A src/haitch/_components/_unsafe.py => src/haitch/_components/_unsafe.py +6 -0
@@ 0,0 1,6 @@
from haitch._elements._element import Element


def unsafe(html: str) -> Element:
    """Creates a fragment element with unescaped HTML."""
    return Element("fragment", unsafe=True)(html)

M src/haitch/_elements/_element.py => src/haitch/_elements/_element.py +3 -2
@@ 15,9 15,10 @@ class Element:
    `__str__` method.
    """

    def __init__(self, tag: str) -> None:
    def __init__(self, tag: str, unsafe: bool = False) -> None:
        """Initialize element by providing a tag name, ie. "a", "div", etc."""
        self._tag = tag
        self._unsafe = unsafe
        self._attrs: Mapping[str, AttributeValue] = {}
        self._children: Iterable[Child] = []



@@ 66,7 67,7 @@ class Element:
            return ""

        if isinstance(child, str):
            return html.escape(child)
            return child if self._unsafe else html.escape(child)

        if isinstance(child, SupportsHtml):
            return child._render()

M tests/test_components.py => tests/test_components.py +6 -0
@@ 13,6 13,12 @@ def test_fragment_element_does_not_render_its_parent_tag():
    assert got == want


def test_unsafe_does_not_escape_html_string():
    got = str(H.unsafe("<h1>Hello</h1>"))
    want = "<h1>Hello</h1>"
    assert got == want


def test_html5_component():
    html = H.html5(
        content=H.fragment(