Quellcode durchsuchen

components as literal vars (#4223)

* component as literal vars

* fix pyi

* use render

* fix pyi

* only render once

* add type ignore

* fix upload default value

* remove testcases if you don't pass them

* improve behavior

* fix render

* that's not how icon buttons work

* upgrade to next js 15 and remove babel and enable turbo

* upload is a silly guy

* woops

* how did this work before

* set env variable

* lower it even more

* lower it even more

* lower it even more

* only do literals as component vars
Khaleel Al-Adhami vor 7 Monaten
Ursprung
Commit
24363170d3

+ 4 - 8
reflex/.templates/jinja/web/pages/utils.js.jinja2

@@ -36,14 +36,10 @@
 {#     component: component dictionary #}
 {% macro render_tag(component) %}
 <{{component.name}} {{- render_props(component.props) }}>
-{%- if component.args is not none -%}
-  {{- render_arg_content(component) }}
-{%- else -%}
-  {{ component.contents }}
-  {% for child in component.children %}
-  {{ render(child) }}
-  {% endfor %}
-{%- endif -%}
+{{ component.contents }}
+{% for child in component.children %}
+{{ render(child) }}
+{% endfor %}
 </{{component.name}}>
 {%- endmacro %}
 

+ 1 - 3
reflex/.templates/web/utils/state.js

@@ -15,7 +15,6 @@ import {
 } from "$/utils/context.js";
 import debounce from "$/utils/helpers/debounce";
 import throttle from "$/utils/helpers/throttle";
-import * as Babel from "@babel/standalone";
 
 // Endpoint URLs.
 const EVENTURL = env.EVENT;
@@ -139,8 +138,7 @@ export const evalReactComponent = async (component) => {
   if (!window.React && window.__reflex) {
     window.React = window.__reflex.react;
   }
-  const output = Babel.transform(component, { presets: ["react"] }).code;
-  const encodedJs = encodeURIComponent(output);
+  const encodedJs = encodeURIComponent(component);
   const dataUri = "data:text/javascript;charset=utf-8," + encodedJs;
   const module = await eval(`import(dataUri)`);
   return module.default;

+ 72 - 3
reflex/components/base/bare.py

@@ -4,10 +4,11 @@ from __future__ import annotations
 
 from typing import Any, Iterator
 
-from reflex.components.component import Component
+from reflex.components.component import Component, LiteralComponentVar
 from reflex.components.tags import Tag
 from reflex.components.tags.tagless import Tagless
-from reflex.vars import ArrayVar, BooleanVar, ObjectVar, Var
+from reflex.utils.imports import ParsedImportDict
+from reflex.vars import BooleanVar, ObjectVar, Var
 
 
 class Bare(Component):
@@ -31,9 +32,77 @@ class Bare(Component):
             contents = str(contents) if contents is not None else ""
         return cls(contents=contents)  # type: ignore
 
+    def _get_all_hooks_internal(self) -> dict[str, None]:
+        """Include the hooks for the component.
+
+        Returns:
+            The hooks for the component.
+        """
+        hooks = super()._get_all_hooks_internal()
+        if isinstance(self.contents, LiteralComponentVar):
+            hooks |= self.contents._var_value._get_all_hooks_internal()
+        return hooks
+
+    def _get_all_hooks(self) -> dict[str, None]:
+        """Include the hooks for the component.
+
+        Returns:
+            The hooks for the component.
+        """
+        hooks = super()._get_all_hooks()
+        if isinstance(self.contents, LiteralComponentVar):
+            hooks |= self.contents._var_value._get_all_hooks()
+        return hooks
+
+    def _get_all_imports(self) -> ParsedImportDict:
+        """Include the imports for the component.
+
+        Returns:
+            The imports for the component.
+        """
+        imports = super()._get_all_imports()
+        if isinstance(self.contents, LiteralComponentVar):
+            var_data = self.contents._get_all_var_data()
+            if var_data:
+                imports |= {k: list(v) for k, v in var_data.imports}
+        return imports
+
+    def _get_all_dynamic_imports(self) -> set[str]:
+        """Get dynamic imports for the component.
+
+        Returns:
+            The dynamic imports.
+        """
+        dynamic_imports = super()._get_all_dynamic_imports()
+        if isinstance(self.contents, LiteralComponentVar):
+            dynamic_imports |= self.contents._var_value._get_all_dynamic_imports()
+        return dynamic_imports
+
+    def _get_all_custom_code(self) -> set[str]:
+        """Get custom code for the component.
+
+        Returns:
+            The custom code.
+        """
+        custom_code = super()._get_all_custom_code()
+        if isinstance(self.contents, LiteralComponentVar):
+            custom_code |= self.contents._var_value._get_all_custom_code()
+        return custom_code
+
+    def _get_all_refs(self) -> set[str]:
+        """Get the refs for the children of the component.
+
+        Returns:
+            The refs for the children.
+        """
+        refs = super()._get_all_refs()
+        if isinstance(self.contents, LiteralComponentVar):
+            refs |= self.contents._var_value._get_all_refs()
+        return refs
+
     def _render(self) -> Tag:
         if isinstance(self.contents, Var):
-            if isinstance(self.contents, (BooleanVar, ObjectVar, ArrayVar)):
+            if isinstance(self.contents, (BooleanVar, ObjectVar)):
                 return Tagless(contents=f"{{{str(self.contents.to_string())}}}")
             return Tagless(contents=f"{{{str(self.contents)}}}")
         return Tagless(contents=str(self.contents))

+ 210 - 1
reflex/components/component.py

@@ -3,6 +3,7 @@
 from __future__ import annotations
 
 import copy
+import dataclasses
 import typing
 from abc import ABC, abstractmethod
 from functools import lru_cache, wraps
@@ -59,7 +60,15 @@ from reflex.utils.imports import (
     parse_imports,
 )
 from reflex.vars import VarData
-from reflex.vars.base import LiteralVar, Var
+from reflex.vars.base import (
+    CachedVarOperation,
+    LiteralVar,
+    Var,
+    cached_property_no_lock,
+)
+from reflex.vars.function import ArgsFunctionOperation, FunctionStringVar
+from reflex.vars.number import ternary_operation
+from reflex.vars.object import ObjectVar
 from reflex.vars.sequence import LiteralArrayVar
 
 
@@ -2345,3 +2354,203 @@ class MemoizationLeaf(Component):
 
 
 load_dynamic_serializer()
+
+
+class ComponentVar(Var[Component], python_types=BaseComponent):
+    """A Var that represents a Component."""
+
+
+def empty_component() -> Component:
+    """Create an empty component.
+
+    Returns:
+        An empty component.
+    """
+    from reflex.components.base.bare import Bare
+
+    return Bare.create("")
+
+
+def render_dict_to_var(tag: dict | Component | str, imported_names: set[str]) -> Var:
+    """Convert a render dict to a Var.
+
+    Args:
+        tag: The render dict.
+        imported_names: The names of the imported components.
+
+    Returns:
+        The Var.
+    """
+    if not isinstance(tag, dict):
+        if isinstance(tag, Component):
+            return render_dict_to_var(tag.render(), imported_names)
+        return Var.create(tag)
+
+    if "iterable" in tag:
+        function_return = Var.create(
+            [
+                render_dict_to_var(child.render(), imported_names)
+                for child in tag["children"]
+            ]
+        )
+
+        func = ArgsFunctionOperation.create(
+            (tag["arg_var_name"], tag["index_var_name"]),
+            function_return,
+        )
+
+        return FunctionStringVar.create("Array.prototype.map.call").call(
+            tag["iterable"]
+            if not isinstance(tag["iterable"], ObjectVar)
+            else tag["iterable"].items(),
+            func,
+        )
+
+    if tag["name"] == "match":
+        element = tag["cond"]
+
+        conditionals = tag["default"]
+
+        for case in tag["match_cases"][::-1]:
+            condition = case[0].to_string() == element.to_string()
+            for pattern in case[1:-1]:
+                condition = condition | (pattern.to_string() == element.to_string())
+
+            conditionals = ternary_operation(
+                condition,
+                case[-1],
+                conditionals,
+            )
+
+        return conditionals
+
+    if "cond" in tag:
+        return ternary_operation(
+            tag["cond"],
+            render_dict_to_var(tag["true_value"], imported_names),
+            render_dict_to_var(tag["false_value"], imported_names)
+            if tag["false_value"] is not None
+            else Var.create(None),
+        )
+
+    props = {}
+
+    special_props = []
+
+    for prop_str in tag["props"]:
+        if "=" not in prop_str:
+            special_props.append(Var(prop_str).to(ObjectVar))
+            continue
+        prop = prop_str.index("=")
+        key = prop_str[:prop]
+        value = prop_str[prop + 2 : -1]
+        props[key] = value
+
+    props = Var.create({Var.create(k): Var(v) for k, v in props.items()})
+
+    for prop in special_props:
+        props = props.merge(prop)
+
+    contents = tag["contents"][1:-1] if tag["contents"] else None
+
+    raw_tag_name = tag.get("name")
+    tag_name = Var(raw_tag_name or "Fragment")
+
+    tag_name = (
+        Var.create(raw_tag_name)
+        if raw_tag_name
+        and raw_tag_name.split(".")[0] not in imported_names
+        and raw_tag_name.lower() == raw_tag_name
+        else tag_name
+    )
+
+    return FunctionStringVar.create(
+        "jsx",
+    ).call(
+        tag_name,
+        props,
+        *([Var(contents)] if contents is not None else []),
+        *[render_dict_to_var(child, imported_names) for child in tag["children"]],
+    )
+
+
+@dataclasses.dataclass(
+    eq=False,
+    frozen=True,
+)
+class LiteralComponentVar(CachedVarOperation, LiteralVar, ComponentVar):
+    """A Var that represents a Component."""
+
+    _var_value: BaseComponent = dataclasses.field(default_factory=empty_component)
+
+    @cached_property_no_lock
+    def _cached_var_name(self) -> str:
+        """Get the name of the var.
+
+        Returns:
+            The name of the var.
+        """
+        var_data = self._get_all_var_data()
+        if var_data is not None:
+            # flatten imports
+            imported_names = {j.alias or j.name for i in var_data.imports for j in i[1]}
+        else:
+            imported_names = set()
+        return str(render_dict_to_var(self._var_value.render(), imported_names))
+
+    @cached_property_no_lock
+    def _cached_get_all_var_data(self) -> VarData | None:
+        """Get the VarData for the var.
+
+        Returns:
+            The VarData for the var.
+        """
+        return VarData.merge(
+            VarData(
+                imports={
+                    "@emotion/react": [
+                        ImportVar(tag="jsx"),
+                    ],
+                }
+            ),
+            VarData(
+                imports=self._var_value._get_all_imports(),
+            ),
+            VarData(
+                imports={
+                    "react": [
+                        ImportVar(tag="Fragment"),
+                    ],
+                }
+            ),
+        )
+
+    def __hash__(self) -> int:
+        """Get the hash of the var.
+
+        Returns:
+            The hash of the var.
+        """
+        return hash((self.__class__.__name__, self._js_expr))
+
+    @classmethod
+    def create(
+        cls,
+        value: Component,
+        _var_data: VarData | None = None,
+    ):
+        """Create a var from a value.
+
+        Args:
+            value: The value of the var.
+            _var_data: Additional hooks and imports associated with the Var.
+
+        Returns:
+            The var.
+        """
+        return LiteralComponentVar(
+            _js_expr="",
+            _var_type=type(value),
+            _var_data=_var_data,
+            _var_value=value,
+        )

+ 79 - 23
reflex/components/core/upload.py

@@ -5,11 +5,17 @@ from __future__ import annotations
 from pathlib import Path
 from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple
 
-from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf
+from reflex.components.component import (
+    Component,
+    ComponentNamespace,
+    MemoizationLeaf,
+    StatefulComponent,
+)
 from reflex.components.el.elements.forms import Input
 from reflex.components.radix.themes.layout.box import Box
 from reflex.config import environment
 from reflex.constants import Dirs
+from reflex.constants.compiler import Imports
 from reflex.event import (
     CallableEventSpec,
     EventChain,
@@ -19,9 +25,10 @@ from reflex.event import (
     call_script,
     parse_args_spec,
 )
+from reflex.utils import format
 from reflex.utils.imports import ImportVar
 from reflex.vars import VarData
-from reflex.vars.base import CallableVar, LiteralVar, Var
+from reflex.vars.base import CallableVar, LiteralVar, Var, get_unique_variable_name
 from reflex.vars.sequence import LiteralStringVar
 
 DEFAULT_UPLOAD_ID: str = "default"
@@ -179,9 +186,7 @@ class Upload(MemoizationLeaf):
 
     library = "react-dropzone@14.2.10"
 
-    tag = "ReactDropzone"
-
-    is_default = True
+    tag = ""
 
     # The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as
     # values.
@@ -201,7 +206,7 @@ class Upload(MemoizationLeaf):
     min_size: Var[int]
 
     # Whether to allow multiple files to be uploaded.
-    multiple: Var[bool] = True  # type: ignore
+    multiple: Var[bool]
 
     # Whether to disable click to upload.
     no_click: Var[bool]
@@ -232,6 +237,8 @@ class Upload(MemoizationLeaf):
         # Mark the Upload component as used in the app.
         cls.is_used = True
 
+        props.setdefault("multiple", True)
+
         # Apply the default classname
         given_class_name = props.pop("class_name", [])
         if isinstance(given_class_name, str):
@@ -243,17 +250,6 @@ class Upload(MemoizationLeaf):
         upload_props = {
             key: value for key, value in props.items() if key in supported_props
         }
-        # The file input to use.
-        upload = Input.create(type="file")
-        upload.special_props = [Var(_js_expr="{...getInputProps()}", _var_type=None)]
-
-        # The dropzone to use.
-        zone = Box.create(
-            upload,
-            *children,
-            **{k: v for k, v in props.items() if k not in supported_props},
-        )
-        zone.special_props = [Var(_js_expr="{...getRootProps()}", _var_type=None)]
 
         # Create the component.
         upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID)
@@ -275,9 +271,74 @@ class Upload(MemoizationLeaf):
                     ),
                 )
             upload_props["on_drop"] = on_drop
+
+        input_props_unique_name = get_unique_variable_name()
+        root_props_unique_name = get_unique_variable_name()
+
+        event_var, callback_str = StatefulComponent._get_memoized_event_triggers(
+            Box.create(on_click=upload_props["on_drop"])  # type: ignore
+        )["on_click"]
+
+        upload_props["on_drop"] = event_var
+
+        upload_props = {
+            format.to_camel_case(key): value for key, value in upload_props.items()
+        }
+
+        use_dropzone_arguements = {
+            "onDrop": event_var,
+            **upload_props,
+        }
+
+        left_side = f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} "
+        right_side = f"useDropzone({str(Var.create(use_dropzone_arguements))})"
+
+        var_data = VarData.merge(
+            VarData(
+                imports=Imports.EVENTS,
+                hooks={
+                    "const [addEvents, connectError] = useContext(EventLoopContext);": None
+                },
+            ),
+            event_var._get_all_var_data(),
+            VarData(
+                hooks={
+                    callback_str: None,
+                    f"{left_side} = {right_side};": None,
+                },
+                imports={
+                    "react-dropzone": "useDropzone",
+                    **Imports.EVENTS,
+                },
+            ),
+        )
+
+        # The file input to use.
+        upload = Input.create(type="file")
+        upload.special_props = [
+            Var(
+                _js_expr=f"{{...{input_props_unique_name}()}}",
+                _var_type=None,
+                _var_data=var_data,
+            )
+        ]
+
+        # The dropzone to use.
+        zone = Box.create(
+            upload,
+            *children,
+            **{k: v for k, v in props.items() if k not in supported_props},
+        )
+        zone.special_props = [
+            Var(
+                _js_expr=f"{{...{root_props_unique_name}()}}",
+                _var_type=None,
+                _var_data=var_data,
+            )
+        ]
+
         return super().create(
             zone,
-            **upload_props,
         )
 
     @classmethod
@@ -295,11 +356,6 @@ class Upload(MemoizationLeaf):
             return (arg_value[0], placeholder)
         return arg_value
 
-    def _render(self):
-        out = super()._render()
-        out.args = ("getRootProps", "getInputProps")
-        return out
-
     @staticmethod
     def _get_app_wrap_components() -> dict[tuple[int, str], Component]:
         return {

+ 5 - 1
reflex/components/core/upload.pyi

@@ -6,7 +6,11 @@
 from pathlib import Path
 from typing import Any, ClassVar, Dict, List, Optional, Union, overload
 
-from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf
+from reflex.components.component import (
+    Component,
+    ComponentNamespace,
+    MemoizationLeaf,
+)
 from reflex.constants import Dirs
 from reflex.event import (
     CallableEventSpec,

+ 6 - 2
reflex/components/dynamic.py

@@ -63,6 +63,9 @@ def load_dynamic_serializer():
         """
         # Causes a circular import, so we import here.
         from reflex.compiler import templates, utils
+        from reflex.components.base.bare import Bare
+
+        component = Bare.create(Var.create(component))
 
         rendered_components = {}
         # Include dynamic imports in the shared component.
@@ -127,14 +130,15 @@ def load_dynamic_serializer():
                 module_code_lines[ix] = line.replace(
                     "export function", "export default function", 1
                 )
+            line_stripped = line.strip()
+            if line_stripped.startswith("{") and line_stripped.endswith("}"):
+                module_code_lines[ix] = line_stripped[1:-1]
 
         module_code_lines.insert(0, "const React = window.__reflex.react;")
 
         return "\n".join(
             [
                 "//__reflex_evaluate",
-                "/** @jsx jsx */",
-                "const { jsx } = window.__reflex['@emotion/react']",
                 *module_code_lines,
             ]
         )

+ 1 - 4
reflex/components/tags/tag.py

@@ -3,7 +3,7 @@
 from __future__ import annotations
 
 import dataclasses
-from typing import Any, Dict, List, Optional, Tuple, Union
+from typing import Any, Dict, List, Optional, Union
 
 from reflex.event import EventChain
 from reflex.utils import format, types
@@ -23,9 +23,6 @@ class Tag:
     # The inner contents of the tag.
     contents: str = ""
 
-    # Args to pass to the tag.
-    args: Optional[Tuple[str, ...]] = None
-
     # Special props that aren't key value pairs.
     special_props: List[Var] = dataclasses.field(default_factory=list)
 

+ 1 - 1
tests/integration/test_form_submit.py

@@ -121,7 +121,7 @@ def FormSubmitName(form_component):
                         on_change=rx.console_log,
                     ),
                     rx.button("Submit", type_="submit"),
-                    rx.icon_button(FormState.val, icon=rx.icon(tag="plus")),
+                    rx.icon_button(rx.icon(tag="plus")),
                 ),
                 on_submit=FormState.form_submit,
                 custom_attrs={"action": "/invalid"},

+ 2 - 2
tests/integration/test_var_operations.py

@@ -793,8 +793,8 @@ def test_var_operations(driver, var_operations: AppHarness):
         ("foreach_list_ix", "1\n2"),
         ("foreach_list_nested", "1\n1\n2"),
         # rx.memo component with state
-        ("memo_comp", "[1,2]10"),
-        ("memo_comp_nested", "[3,4]5"),
+        ("memo_comp", "1210"),
+        ("memo_comp_nested", "345"),
         # foreach in a match
         ("foreach_in_match", "first\nsecond\nthird"),
     ]

+ 0 - 205
tests/units/components/forms/test_uploads.py

@@ -1,205 +0,0 @@
-import pytest
-
-import reflex as rx
-
-
-@pytest.fixture
-def upload_root_component():
-    """A test upload component function.
-
-    Returns:
-        A test upload component function.
-    """
-
-    def upload_root_component():
-        return rx.upload.root(
-            rx.button("select file"),
-            rx.text("Drag and drop files here or click to select files"),
-            border="1px dotted black",
-        )
-
-    return upload_root_component()
-
-
-@pytest.fixture
-def upload_component():
-    """A test upload component function.
-
-    Returns:
-        A test upload component function.
-    """
-
-    def upload_component():
-        return rx.upload(
-            rx.button("select file"),
-            rx.text("Drag and drop files here or click to select files"),
-            border="1px dotted black",
-        )
-
-    return upload_component()
-
-
-@pytest.fixture
-def upload_component_id_special():
-    def upload_component():
-        return rx.upload(
-            rx.button("select file"),
-            rx.text("Drag and drop files here or click to select files"),
-            border="1px dotted black",
-            id="#spec!`al-_98ID",
-        )
-
-    return upload_component()
-
-
-@pytest.fixture
-def upload_component_with_props():
-    """A test upload component with props function.
-
-    Returns:
-        A test upload component with props function.
-    """
-
-    def upload_component_with_props():
-        return rx.upload(
-            rx.button("select file"),
-            rx.text("Drag and drop files here or click to select files"),
-            border="1px dotted black",
-            no_drag=True,
-            max_files=2,
-        )
-
-    return upload_component_with_props()
-
-
-def test_upload_root_component_render(upload_root_component):
-    """Test that the render function is set correctly.
-
-    Args:
-        upload_root_component: component fixture
-    """
-    upload = upload_root_component.render()
-
-    # upload
-    assert upload["name"] == "ReactDropzone"
-    assert upload["props"] == [
-        'id={"default"}',
-        "multiple={true}",
-        "onDrop={e => setFilesById(filesById => {\n"
-        "    const updatedFilesById = Object.assign({}, filesById);\n"
-        '    updatedFilesById["default"] = e;\n'
-        "    return updatedFilesById;\n"
-        "  })\n"
-        "    }",
-        "ref={ref_default}",
-    ]
-    assert upload["args"] == ("getRootProps", "getInputProps")
-
-    # box inside of upload
-    [box] = upload["children"]
-    assert box["name"] == "RadixThemesBox"
-    assert box["props"] == [
-        'className={"rx-Upload"}',
-        'css={({ ["border"] : "1px dotted black" })}',
-        "{...getRootProps()}",
-    ]
-
-    # input, button and text inside of box
-    [input, button, text] = box["children"]
-    assert input["name"] == "input"
-    assert input["props"] == ['type={"file"}', "{...getInputProps()}"]
-
-    assert button["name"] == "RadixThemesButton"
-    assert button["children"][0]["contents"] == '{"select file"}'
-
-    assert text["name"] == "RadixThemesText"
-    assert (
-        text["children"][0]["contents"]
-        == '{"Drag and drop files here or click to select files"}'
-    )
-
-
-def test_upload_component_render(upload_component):
-    """Test that the render function is set correctly.
-
-    Args:
-        upload_component: component fixture
-    """
-    upload = upload_component.render()
-
-    # upload
-    assert upload["name"] == "ReactDropzone"
-    assert upload["props"] == [
-        'id={"default"}',
-        "multiple={true}",
-        "onDrop={e => setFilesById(filesById => {\n"
-        "    const updatedFilesById = Object.assign({}, filesById);\n"
-        '    updatedFilesById["default"] = e;\n'
-        "    return updatedFilesById;\n"
-        "  })\n"
-        "    }",
-        "ref={ref_default}",
-    ]
-    assert upload["args"] == ("getRootProps", "getInputProps")
-
-    # box inside of upload
-    [box] = upload["children"]
-    assert box["name"] == "RadixThemesBox"
-    assert box["props"] == [
-        'className={"rx-Upload"}',
-        'css={({ ["border"] : "1px dotted black", ["padding"] : "5em", ["textAlign"] : "center" })}',
-        "{...getRootProps()}",
-    ]
-
-    # input, button and text inside of box
-    [input, button, text] = box["children"]
-    assert input["name"] == "input"
-    assert input["props"] == ['type={"file"}', "{...getInputProps()}"]
-
-    assert button["name"] == "RadixThemesButton"
-    assert button["children"][0]["contents"] == '{"select file"}'
-
-    assert text["name"] == "RadixThemesText"
-    assert (
-        text["children"][0]["contents"]
-        == '{"Drag and drop files here or click to select files"}'
-    )
-
-
-def test_upload_component_with_props_render(upload_component_with_props):
-    """Test that the render function is set correctly.
-
-    Args:
-        upload_component_with_props: component fixture
-    """
-    upload = upload_component_with_props.render()
-
-    assert upload["props"] == [
-        'id={"default"}',
-        "maxFiles={2}",
-        "multiple={true}",
-        "noDrag={true}",
-        "onDrop={e => setFilesById(filesById => {\n"
-        "    const updatedFilesById = Object.assign({}, filesById);\n"
-        '    updatedFilesById["default"] = e;\n'
-        "    return updatedFilesById;\n"
-        "  })\n"
-        "    }",
-        "ref={ref_default}",
-    ]
-
-
-def test_upload_component_id_with_special_chars(upload_component_id_special):
-    upload = upload_component_id_special.render()
-
-    assert upload["props"] == [
-        r'id={"#spec!`al-_98ID"}',
-        "multiple={true}",
-        "onDrop={e => setFilesById(filesById => {\n"
-        "    const updatedFilesById = Object.assign({}, filesById);\n"
-        '    updatedFilesById["#spec!`al-_98ID"] = e;\n'
-        "    return updatedFilesById;\n"
-        "  })\n"
-        "    }",
-        "ref={ref__spec_al__98ID}",
-    ]

+ 2 - 17
tests/units/components/test_component.py

@@ -642,21 +642,18 @@ def test_component_create_unallowed_types(children, test_component):
                 "name": "Fragment",
                 "props": [],
                 "contents": "",
-                "args": None,
                 "special_props": [],
                 "children": [
                     {
                         "name": "RadixThemesText",
                         "props": ['as={"p"}'],
                         "contents": "",
-                        "args": None,
                         "special_props": [],
                         "children": [
                             {
                                 "name": "",
                                 "props": [],
                                 "contents": '{"first_text"}',
-                                "args": None,
                                 "special_props": [],
                                 "children": [],
                                 "autofocus": False,
@@ -671,15 +668,12 @@ def test_component_create_unallowed_types(children, test_component):
         (
             (rx.text("first_text"), rx.text("second_text")),
             {
-                "args": None,
                 "autofocus": False,
                 "children": [
                     {
-                        "args": None,
                         "autofocus": False,
                         "children": [
                             {
-                                "args": None,
                                 "autofocus": False,
                                 "children": [],
                                 "contents": '{"first_text"}',
@@ -694,11 +688,9 @@ def test_component_create_unallowed_types(children, test_component):
                         "special_props": [],
                     },
                     {
-                        "args": None,
                         "autofocus": False,
                         "children": [
                             {
-                                "args": None,
                                 "autofocus": False,
                                 "children": [],
                                 "contents": '{"second_text"}',
@@ -722,15 +714,12 @@ def test_component_create_unallowed_types(children, test_component):
         (
             (rx.text("first_text"), rx.box((rx.text("second_text"),))),
             {
-                "args": None,
                 "autofocus": False,
                 "children": [
                     {
-                        "args": None,
                         "autofocus": False,
                         "children": [
                             {
-                                "args": None,
                                 "autofocus": False,
                                 "children": [],
                                 "contents": '{"first_text"}',
@@ -745,19 +734,15 @@ def test_component_create_unallowed_types(children, test_component):
                         "special_props": [],
                     },
                     {
-                        "args": None,
                         "autofocus": False,
                         "children": [
                             {
-                                "args": None,
                                 "autofocus": False,
                                 "children": [
                                     {
-                                        "args": None,
                                         "autofocus": False,
                                         "children": [
                                             {
-                                                "args": None,
                                                 "autofocus": False,
                                                 "children": [],
                                                 "contents": '{"second_text"}',
@@ -1117,10 +1102,10 @@ def test_component_with_only_valid_children(fixture, request):
 @pytest.mark.parametrize(
     "component,rendered",
     [
-        (rx.text("hi"), '<RadixThemesText as={"p"}>\n  {"hi"}\n</RadixThemesText>'),
+        (rx.text("hi"), '<RadixThemesText as={"p"}>\n\n{"hi"}\n</RadixThemesText>'),
         (
             rx.box(rx.heading("test", size="3")),
-            '<RadixThemesBox>\n  <RadixThemesHeading size={"3"}>\n  {"test"}\n</RadixThemesHeading>\n</RadixThemesBox>',
+            '<RadixThemesBox>\n\n<RadixThemesHeading size={"3"}>\n\n{"test"}\n</RadixThemesHeading>\n</RadixThemesBox>',
         ),
     ],
 )