diff --git a/reflex/.templates/jinja/web/pages/utils.js.jinja2 b/reflex/.templates/jinja/web/pages/utils.js.jinja2
index 908482d24..624e3bee8 100644
--- a/reflex/.templates/jinja/web/pages/utils.js.jinja2
+++ b/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 %}
diff --git a/reflex/.templates/web/utils/state.js b/reflex/.templates/web/utils/state.js
index e577df67d..7d76b080a 100644
--- a/reflex/.templates/web/utils/state.js
+++ b/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;
diff --git a/reflex/components/base/bare.py b/reflex/components/base/bare.py
index ada511ef2..c70b4c844 100644
--- a/reflex/components/base/bare.py
+++ b/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))
diff --git a/reflex/components/component.py b/reflex/components/component.py
index 5c6234749..399becee9 100644
--- a/reflex/components/component.py
+++ b/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,
+ )
diff --git a/reflex/components/core/upload.py b/reflex/components/core/upload.py
index c454cba7d..787cca9d0 100644
--- a/reflex/components/core/upload.py
+++ b/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 {
diff --git a/reflex/components/core/upload.pyi b/reflex/components/core/upload.pyi
index 43ce3b63d..e5c73bd14 100644
--- a/reflex/components/core/upload.pyi
+++ b/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,
diff --git a/reflex/components/dynamic.py b/reflex/components/dynamic.py
index e391c9fd0..c0e172224 100644
--- a/reflex/components/dynamic.py
+++ b/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,
]
)
diff --git a/reflex/components/tags/tag.py b/reflex/components/tags/tag.py
index d577abc6e..0587c61ed 100644
--- a/reflex/components/tags/tag.py
+++ b/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)
diff --git a/tests/integration/test_form_submit.py b/tests/integration/test_form_submit.py
index a020a7e15..3bfcf6e8c 100644
--- a/tests/integration/test_form_submit.py
+++ b/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"},
diff --git a/tests/integration/test_var_operations.py b/tests/integration/test_var_operations.py
index 919a39f3b..cae56e1a8 100644
--- a/tests/integration/test_var_operations.py
+++ b/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"),
]
diff --git a/tests/units/components/forms/test_uploads.py b/tests/units/components/forms/test_uploads.py
deleted file mode 100644
index 3b2ee014f..000000000
--- a/tests/units/components/forms/test_uploads.py
+++ /dev/null
@@ -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}",
- ]
diff --git a/tests/units/components/test_component.py b/tests/units/components/test_component.py
index b7b721a92..c2d73aca5 100644
--- a/tests/units/components/test_component.py
+++ b/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"), '\n {"hi"}\n'),
+ (rx.text("hi"), '\n\n{"hi"}\n'),
(
rx.box(rx.heading("test", size="3")),
- '\n \n {"test"}\n\n',
+ '\n\n\n\n{"test"}\n\n',
),
],
)