Skip to content

Commit b68a38a

Browse files
authored
v2.0.0b10 (#1336)
- Allow `reactpy.utils.string_to_reactpy` to render empty strings as React fragments. - Allow `reactpy.utils.reactpy_to_string` to render components that return `None` as empty output. - Ensure `h` shorthand for `reactpy.html` is exposed at top-level module (`reactpy.h`)
1 parent 8c6732c commit b68a38a

File tree

15 files changed

+53
-8
lines changed

15 files changed

+53
-8
lines changed

src/js/bun.lockb

-784 Bytes
Binary file not shown.

src/reactpy/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from reactpy import config, logging, reactjs, types, web, widgets
2-
from reactpy._html import html
2+
from reactpy._html import h, html
33
from reactpy.core import hooks
44
from reactpy.core.component import component
55
from reactpy.core.events import event
@@ -23,7 +23,7 @@
2323
from reactpy.utils import Ref, reactpy_to_string, string_to_reactpy
2424

2525
__author__ = "The Reactive Python Team"
26-
__version__ = "2.0.0b9"
26+
__version__ = "2.0.0b10"
2727

2828
__all__ = [
2929
"Ref",
@@ -32,6 +32,7 @@
3232
"config",
3333
"create_context",
3434
"event",
35+
"h",
3536
"hooks",
3637
"html",
3738
"logging",

src/reactpy/utils.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from lxml import etree
1010
from lxml.html import fromstring
1111

12-
from reactpy import html
12+
from reactpy import h
1313
from reactpy.transforms import RequiredTransforms, attributes_to_reactjs
1414
from reactpy.types import Component, VdomDict
1515

@@ -105,9 +105,14 @@ def string_to_reactpy(
105105
event handler that prevents the browser from navigating to the link. This is
106106
useful if you would rather have `reactpy-router` handle your URL navigation.
107107
"""
108-
if not isinstance(html, str): # nocov
108+
if not isinstance(html, str):
109109
msg = f"Expected html to be a string, not {type(html).__name__}"
110110
raise TypeError(msg)
111+
if not html.strip():
112+
return h.fragment()
113+
if "<" not in html or ">" not in html:
114+
msg = "Expected html string to contain HTML tags, but no tags were found."
115+
raise ValueError(msg)
111116

112117
# If the user provided a string, convert it to a list of lxml.etree nodes
113118
try:
@@ -153,7 +158,7 @@ def _etree_to_vdom(
153158
attributes = attributes_to_reactjs(dict(node.items()))
154159

155160
# Convert the lxml node to a VDOM dict
156-
constructor = getattr(html, str(node.tag))
161+
constructor = getattr(h, str(node.tag))
157162
el = constructor(attributes, children)
158163

159164
# Perform necessary transformations on the VDOM attributes to meet VDOM spec
@@ -236,13 +241,13 @@ def component_to_vdom(component: Component) -> VdomDict:
236241
"""Convert the first render of a component into a VDOM dictionary"""
237242
result = component.render()
238243

244+
if result is None:
245+
return h.fragment()
239246
if isinstance(result, dict):
240247
return result
241248
if hasattr(result, "render"):
242249
return component_to_vdom(cast(Component, result))
243-
elif isinstance(result, str):
244-
return html.div(result)
245-
return html()
250+
return h.div(result) if isinstance(result, str) else h.div()
246251

247252

248253
def _react_attribute_to_html(key: str, value: Any) -> tuple[str, str]:

tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
)
1616
from reactpy.testing.display import _playwright_visible
1717

18+
from . import pytestmark # noqa: F401
19+
1820
REACTPY_ASYNC_RENDERING.set_current(True)
1921
REACTPY_DEBUG.set_current(True)
2022

tests/test_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from tests.tooling.common import DEFAULT_TYPE_DELAY
77
from tests.tooling.hooks import use_counter
88

9+
from . import pytestmark # noqa: F401
10+
911
JS_DIR = Path(__file__).parent / "js"
1012

1113

tests/test_core/test_component.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import reactpy
22
from reactpy.testing import DisplayFixture
33

4+
from .. import pytestmark # noqa: F401
5+
46

57
def test_component_repr():
68
@reactpy.component

tests/test_core/test_events.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
from reactpy.types import Event
1414
from tests.tooling.common import DEFAULT_TYPE_DELAY
1515

16+
from .. import pytestmark # noqa: F401
17+
1618

1719
def test_event_handler_repr():
1820
handler = EventHandler(lambda: None)

tests/test_core/test_hooks.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
from reactpy.utils import Ref
1414
from tests.tooling.common import DEFAULT_TYPE_DELAY, update_message
1515

16+
from .. import pytestmark # noqa: F401
17+
1618

1719
async def test_must_be_rendering_in_layout_to_use_hooks():
1820
@reactpy.component

tests/test_html.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from tests.tooling.common import DEFAULT_TYPE_DELAY
88
from tests.tooling.hooks import use_counter
99

10+
from . import pytestmark # noqa: F401
11+
1012

1113
async def test_script_re_run_on_content_change(display: DisplayFixture):
1214
@component

tests/test_pyscript/test_components.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from reactpy.testing import BackendFixture, DisplayFixture
99
from reactpy.testing.backend import root_hotswap_component
1010

11+
from .. import pytestmark # noqa: F401
12+
1113

1214
@pytest.fixture(scope="module")
1315
async def display(browser):

0 commit comments

Comments
 (0)