diff --git a/docs/usage.md b/docs/usage.md
index 90b517a..e07cf9f 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -43,9 +43,13 @@ and safe to directly insert variable data via f-strings:
### Conditional Rendering
-`None` will not render anything. This can be useful to conditionally render some content.
+`True`, `False` and `None` will not render anything. Python's `and` and `or`
+operators will
+[short-circuit](https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not).
+You can use this to conditionally render content with inline `and` and
+`or`.
-```pycon title="Conditional rendering"
+```pycon title="Conditional rendering with a value that may be None"
>>> from htpy import div, b
>>> error = None
@@ -55,7 +59,7 @@ and safe to directly insert variable data via f-strings:
>>> error = 'Enter a valid email address.'
->>> print(div[error and b[error]])
+>>> print(div[has_error and b[error_message]])
Enter a valid email address.
# Inline if/else can also be used:
@@ -63,6 +67,25 @@ and safe to directly insert variable data via f-strings:
Enter a valid email address.
```
+```pycon title="Conditional rendering based on a bool variable"
+>>> from htpy import div
+>>> is_happy = True
+>>> print(div[is_happy and "😄"])
+😄
+
+>>> is_sad = False
+>>> print(div[is_sad and "😔"])
+
+
+>>> is_allowed = True
+>>> print(div[is_allowed or "Access denied!"])
+
+
+>>> is_allowed = False
+>>> print(div[is_allowed or "Access denied!"])
+Access denied
+```
+
### Loops / Iterating Over Children
You can pass a list, tuple or generator to generate multiple children:
diff --git a/htpy/__init__.py b/htpy/__init__.py
index b59fecf..bb7d6a3 100644
--- a/htpy/__init__.py
+++ b/htpy/__init__.py
@@ -110,6 +110,12 @@ def iter_node(x: Node) -> Iterator[str]:
if x is None:
return
+ if x is True:
+ return
+
+ if x is False:
+ return
+
if isinstance(x, BaseElement):
yield from x
elif isinstance(x, str) or hasattr(x, "__html__"):
@@ -227,7 +233,9 @@ def __html__(self) -> str: ...
_ClassNamesDict: TypeAlias = dict[str, bool]
_ClassNames: TypeAlias = Iterable[str | None | bool | _ClassNamesDict] | _ClassNamesDict
-Node: TypeAlias = None | str | BaseElement | _HasHtml | Iterable["Node"] | Callable[[], "Node"]
+Node: TypeAlias = (
+ None | bool | str | BaseElement | _HasHtml | Iterable["Node"] | Callable[[], "Node"]
+)
Attribute: TypeAlias = None | bool | str | _HasHtml | _ClassNames
diff --git a/tests/test_children.py b/tests/test_children.py
index f2e574a..1c96364 100644
--- a/tests/test_children.py
+++ b/tests/test_children.py
@@ -104,8 +104,9 @@ def test_custom_element() -> None:
assert str(el) == ""
-def test_ignore_none() -> None:
- assert str(div[None]) == ""
+@pytest.mark.parametrize("ignored_value", [None, True, False])
+def test_ignored(ignored_value: Any) -> None:
+ assert str(div[ignored_value]) == ""
def test_iter() -> None:
@@ -197,7 +198,7 @@ def test_callable_in_generator() -> None:
assert str(div[((lambda: "hi") for _ in range(1))]) == "hi
"
-@pytest.mark.parametrize("not_a_child", [1234, True, False, b"foo", object(), object])
+@pytest.mark.parametrize("not_a_child", [1234, b"foo", object(), object, 1, 0])
def test_invalid_child(not_a_child: Any) -> None:
with pytest.raises(ValueError, match="is not a valid child element"):
str(div[not_a_child])