Skip to content

Commit 08034fa

Browse files
committed
Simplify transforms
1 parent 4077a06 commit 08034fa

File tree

1 file changed

+10
-62
lines changed

1 file changed

+10
-62
lines changed

src/reactpy_django/forms/transforms.py

Lines changed: 10 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from typing import TYPE_CHECKING
3+
from typing import TYPE_CHECKING, Any
44

55
from reactpy.core.events import EventHandler, to_event_handler_function
66

@@ -12,42 +12,27 @@
1212

1313
def convert_html_props_to_reactjs(vdom_tree: VdomDict) -> VdomDict:
1414
"""Transformation that standardizes the prop names to be used in the component."""
15-
if not isinstance(vdom_tree, dict):
16-
return vdom_tree
17-
1815
# On each node, replace the 'attributes' key names with the standardized names.
1916
if "attributes" in vdom_tree:
2017
vdom_tree["attributes"] = {_normalize_prop_name(k): v for k, v in vdom_tree["attributes"].items()}
2118

22-
for child in vdom_tree.get("children", []):
23-
convert_html_props_to_reactjs(child)
24-
2519
return vdom_tree
2620

2721

2822
def convert_textarea_children_to_prop(vdom_tree: VdomDict) -> VdomDict:
2923
"""Transformation that converts the text content of a <textarea> to the 'value' prop."""
30-
if not isinstance(vdom_tree, dict):
31-
return vdom_tree
32-
33-
# On each node, if the 'tagName' is 'textarea', move the text content to the 'value' prop.
24+
# If the current tag is <textarea>, move the text content to the 'value' prop.
3425
if vdom_tree["tagName"] == "textarea" and "children" in vdom_tree and vdom_tree["children"]:
3526
text_content = vdom_tree.pop("children")
3627
text_content = "".join([child for child in text_content if isinstance(child, str)])
3728
default_value = vdom_tree["attributes"].pop("defaultValue", "")
3829
vdom_tree["attributes"]["defaultValue"] = text_content or default_value
3930

40-
for child in vdom_tree.get("children", []):
41-
convert_textarea_children_to_prop(child)
42-
4331
return vdom_tree
4432

4533

4634
def set_value_prop_on_select_element(vdom_tree: VdomDict) -> VdomDict:
4735
"""Use the `value` prop on <select> instead of setting `selected` on <option>."""
48-
if not isinstance(vdom_tree, dict):
49-
return vdom_tree
50-
5136
# If the current tag is <select>, remove 'selected' prop from any <option> children and
5237
# instead set the 'value' prop on the <select> tag.
5338
if vdom_tree["tagName"] == "select" and "children" in vdom_tree:
@@ -58,48 +43,33 @@ def set_value_prop_on_select_element(vdom_tree: VdomDict) -> VdomDict:
5843
if selected_options and multiple_choice:
5944
vdom_tree["attributes"]["defaultValue"] = selected_options
6045

61-
for child in vdom_tree.get("children", []):
62-
set_value_prop_on_select_element(child)
63-
6446
return vdom_tree
6547

6648

6749
def ensure_input_elements_are_controlled(vdom_tree: VdomDict) -> VdomDict:
6850
"""Adds an onChange handler on form <input> elements, since ReactJS doesn't like uncontrolled inputs."""
69-
if not isinstance(vdom_tree, dict):
70-
return vdom_tree
71-
7251
vdom_tree.setdefault("eventHandlers", {})
7352
if vdom_tree["tagName"] == "input" and "onChange" not in vdom_tree["eventHandlers"]:
7453
vdom_tree["eventHandlers"]["onChange"] = EventHandler(to_event_handler_function(_do_nothing_event))
7554

76-
if "children" in vdom_tree:
77-
for child in vdom_tree["children"]:
78-
ensure_input_elements_are_controlled(child)
79-
8055
return vdom_tree
8156

8257

8358
def intercept_anchor_links(vdom_tree: VdomDict) -> VdomDict:
8459
"""Intercepts anchor links and prevents the default behavior.
8560
This allows ReactPy-Router to handle the navigation instead of the browser."""
86-
if not isinstance(vdom_tree, dict):
87-
return vdom_tree
88-
8961
if vdom_tree["tagName"] == "a":
9062
vdom_tree.setdefault("eventHandlers", {})
9163
vdom_tree["eventHandlers"]["onClick"] = EventHandler(
9264
to_event_handler_function(_do_nothing_event), prevent_default=True
9365
)
9466

95-
for child in vdom_tree.get("children", []):
96-
intercept_anchor_links(child)
97-
9867
return vdom_tree
9968

10069

101-
def _find_selected_options(vdom_tree: VdomDict) -> list[str]:
102-
"""Recursively iterate through the tree of dictionaries to find an <option> with the 'selected' prop."""
70+
def _find_selected_options(vdom_tree: VdomDict | Any) -> list[str]:
71+
"""Recursively iterate through the tree to find all <option> tags with the 'selected' prop.
72+
Removes the 'selected' prop and returns a list of the 'value' prop of each selected <option>."""
10373
if not isinstance(vdom_tree, dict):
10474
return []
10575

@@ -122,8 +92,8 @@ def _normalize_prop_name(prop_name: str) -> str:
12292
return REACT_PROP_SUBSTITUTIONS.get(prop_name, prop_name)
12393

12494

125-
def _react_props_set(string: str) -> set[str]:
126-
"""Extracts the props from a string of React props."""
95+
def _parse_react_props(string: str) -> set[str]:
96+
"""Extracts the props from a string of React props (copy-pasted from ReactJS docs)."""
12797
lines = string.strip().split("\n")
12898
props = set()
12999

@@ -138,34 +108,12 @@ def _react_props_set(string: str) -> set[str]:
138108
return props
139109

140110

141-
def _add_on_change_event(event_func, vdom_tree: VdomDict) -> VdomDict:
142-
"""Recursively adds an onChange event handler to all elements in the tree."""
143-
if not isinstance(vdom_tree, dict):
144-
return vdom_tree
145-
146-
vdom_tree.setdefault("eventHandlers", {})
147-
if vdom_tree["tagName"] in {"input", "textarea"}:
148-
if "onChange" in vdom_tree["eventHandlers"]:
149-
pass
150-
elif isinstance(event_func, EventHandler):
151-
vdom_tree["eventHandlers"]["onChange"] = event_func
152-
else:
153-
vdom_tree["eventHandlers"]["onChange"] = EventHandler(to_event_handler_function(event_func))
154-
155-
if "children" in vdom_tree:
156-
for child in vdom_tree["children"]:
157-
_add_on_change_event(event_func, child)
158-
159-
return vdom_tree
160-
161-
162111
def _do_nothing_event(*args, **kwargs):
163-
pass
112+
"""A placeholder event function that does nothing."""
164113

165114

166-
# TODO: Create some kind of script that will generate this during build time, rather than hardcoding it.
115+
# TODO: Create some kind of script that will generate the react docs copy-pasta during build time, rather than hardcoding it.
167116
# https://react.dev/reference/react-dom/components/common#common-props
168-
169117
SPECIAL_PROPS = r"""
170118
children: A React node (an element, a string, a number, a portal, an empty node like null, undefined and booleans, or an array of other React nodes). Specifies the content inside the component. When you use JSX, you will usually specify the children prop implicitly by nesting tags like <div><span /></div>.
171119
dangerouslySetInnerHTML: An object of the form { __html: '<p>some html</p>' } with a raw HTML string inside. Overrides the innerHTML property of the DOM node and displays the passed HTML inside. This should be used with extreme caution! If the HTML inside isn't trusted (for example, if it's based on user data), you risk introducing an XSS vulnerability. Read more about using dangerouslySetInnerHTML.
@@ -496,7 +444,7 @@ def _do_nothing_event(*args, **kwargs):
496444
type: a string. Says whether the script is a classic script, ES module, or import map.
497445
"""
498446

499-
KNOWN_REACT_PROPS = _react_props_set(
447+
KNOWN_REACT_PROPS = _parse_react_props(
500448
SPECIAL_PROPS
501449
+ STANDARD_PROPS
502450
+ FORM_PROPS

0 commit comments

Comments
 (0)