Improvements to custom styles in rx.markdown (#1852)

This commit is contained in:
Nikhil Rao 2023-09-25 15:59:48 -07:00 committed by GitHub
parent 2f284a08b6
commit 8231993e5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 604 additions and 313 deletions

View File

@ -21,8 +21,11 @@ def VarOperations():
float_var2: float = 5.5
list1: list = [1, 2]
list2: list = [3, 4]
list3: list = ["first", "second", "third"]
str_var1: str = "first"
str_var2: str = "second"
str_var3: str = "ThIrD"
str_var4: str = "a long string"
dict1: dict = {1: 2}
dict2: dict = {3: 4}
@ -514,6 +517,11 @@ def VarOperations():
rx.text(
VarOperationState.dict1.contains(1).to_string(), id="dict_contains"
),
rx.text(VarOperationState.str_var3.lower(), id="str_lower"),
rx.text(VarOperationState.str_var3.upper(), id="str_upper"),
rx.text(VarOperationState.str_var4.split(" ").to_string(), id="str_split"),
rx.text(VarOperationState.list3.join(""), id="list_join"),
rx.text(VarOperationState.list3.join(","), id="list_join_comma"),
)
app.compile()
@ -567,145 +575,137 @@ def test_var_operations(driver, var_operations: AppHarness):
driver: selenium WebDriver open to the app
var_operations: AppHarness for the var operations app
"""
assert var_operations.app_instance is not None, "app is not running"
# INT INT
assert driver.find_element(By.ID, "int_add_int").text == "15"
assert driver.find_element(By.ID, "int_mult_int").text == "50"
assert driver.find_element(By.ID, "int_sub_int").text == "5"
assert driver.find_element(By.ID, "int_exp_int").text == "100000"
assert driver.find_element(By.ID, "int_div_int").text == "2"
assert driver.find_element(By.ID, "int_floor_int").text == "1"
assert driver.find_element(By.ID, "int_mod_int").text == "0"
assert driver.find_element(By.ID, "int_gt_int").text == "true"
assert driver.find_element(By.ID, "int_lt_int").text == "false"
assert driver.find_element(By.ID, "int_gte_int").text == "true"
assert driver.find_element(By.ID, "int_lte_int").text == "false"
assert driver.find_element(By.ID, "int_and_int").text == "5"
assert driver.find_element(By.ID, "int_or_int").text == "10"
assert driver.find_element(By.ID, "int_eq_int").text == "false"
assert driver.find_element(By.ID, "int_neq_int").text == "true"
tests = [
# int, int
("int_add_int", "15"),
("int_mult_int", "50"),
("int_sub_int", "5"),
("int_exp_int", "100000"),
("int_div_int", "2"),
("int_floor_int", "1"),
("int_mod_int", "0"),
("int_gt_int", "true"),
("int_lt_int", "false"),
("int_gte_int", "true"),
("int_lte_int", "false"),
("int_and_int", "5"),
("int_or_int", "10"),
("int_eq_int", "false"),
("int_neq_int", "true"),
# int, float
("float_add_int", "15.5"),
("float_mult_int", "52.5"),
("float_sub_int", "5.5"),
("float_exp_int", "127628.15625"),
("float_div_int", "2.1"),
("float_floor_int", "1"),
("float_mod_int", "0.5"),
("float_gt_int", "true"),
("float_lt_int", "false"),
("float_gte_int", "true"),
("float_lte_int", "false"),
("float_eq_int", "false"),
("float_neq_int", "true"),
("float_and_int", "5"),
("float_or_int", "10.5"),
# int, dict
("int_or_dict", "10"),
("int_and_dict", '{"1":2}'),
("int_eq_dict", "false"),
("int_neq_dict", "true"),
# float, float
("float_add_float", "16"),
("float_mult_float", "57.75"),
("float_sub_float", "5"),
("float_exp_float", "413562.49323606625"),
("float_div_float", "1.9090909090909092"),
("float_floor_float", "1"),
("float_mod_float", "5"),
("float_gt_float", "true"),
("float_lt_float", "false"),
("float_gte_float", "true"),
("float_lte_float", "false"),
("float_eq_float", "false"),
("float_neq_float", "true"),
("float_and_float", "5.5"),
("float_or_float", "10.5"),
# float, str
("float_or_str", "10.5"),
("float_and_str", "first"),
("float_eq_str", "false"),
("float_neq_str", "true"),
# float, list
("float_or_list", "10.5"),
("float_and_list", "[1,2]"),
("float_eq_list", "false"),
("float_neq_list", "true"),
# float, dict
("float_or_dict", "10.5"),
("float_and_dict", '{"1":2}'),
("float_eq_dict", "false"),
("float_neq_dict", "true"),
# str, str
("str_add_str", "firstsecond"),
("str_gt_str", "false"),
("str_lt_str", "true"),
("str_gte_str", "false"),
("str_lte_str", "true"),
("str_eq_str", "false"),
("str_neq_str", "true"),
("str_and_str", "second"),
("str_or_str", "first"),
("str_contains", "true"),
("str_lower", "third"),
("str_upper", "THIRD"),
("str_split", '["a","long","string"]'),
# str, int
("str_mult_int", "firstfirstfirstfirstfirst"),
("str_and_int", "5"),
("str_or_int", "first"),
("str_eq_int", "false"),
("str_neq_int", "true"),
# str, list
("str_and_list", "[1,2]"),
("str_or_list", "first"),
("str_eq_list", "false"),
("str_neq_list", "true"),
# str, dict
("str_or_dict", "first"),
("str_and_dict", '{"1":2}'),
("str_eq_dict", "false"),
("str_neq_dict", "true"),
# list, list
("list_add_list", "[1,2,3,4]"),
("list_gt_list", "false"),
("list_lt_list", "true"),
("list_gte_list", "false"),
("list_lte_list", "true"),
("list_eq_list", "false"),
("list_neq_list", "true"),
("list_and_list", "[3,4]"),
("list_or_list", "[1,2]"),
("list_contains", "true"),
("list_reverse", "[2,1]"),
("list_join", "firstsecondthird"),
("list_join_comma", "first,second,third"),
# list, int
("list_mult_int", "[1,2,1,2,1,2,1,2,1,2]"),
("list_or_int", "[1,2]"),
("list_and_int", "10"),
("list_eq_int", "false"),
("list_neq_int", "true"),
# list, dict
("list_and_dict", '{"1":2}'),
("list_or_dict", "[1,2]"),
("list_eq_dict", "false"),
("list_neq_dict", "true"),
# dict, dict
("dict_or_dict", '{"1":2}'),
("dict_and_dict", '{"3":4}'),
("dict_eq_dict", "false"),
("dict_neq_dict", "true"),
("dict_contains", "true"),
]
# INT FLOAT OR FLOAT INT
assert driver.find_element(By.ID, "float_add_int").text == "15.5"
assert driver.find_element(By.ID, "float_mult_int").text == "52.5"
assert driver.find_element(By.ID, "float_sub_int").text == "5.5"
assert driver.find_element(By.ID, "float_exp_int").text == "127628.15625"
assert driver.find_element(By.ID, "float_div_int").text == "2.1"
assert driver.find_element(By.ID, "float_floor_int").text == "1"
assert driver.find_element(By.ID, "float_mod_int").text == "0.5"
assert driver.find_element(By.ID, "float_gt_int").text == "true"
assert driver.find_element(By.ID, "float_lt_int").text == "false"
assert driver.find_element(By.ID, "float_gte_int").text == "true"
assert driver.find_element(By.ID, "float_lte_int").text == "false"
assert driver.find_element(By.ID, "float_eq_int").text == "false"
assert driver.find_element(By.ID, "float_neq_int").text == "true"
assert driver.find_element(By.ID, "float_and_int").text == "5"
assert driver.find_element(By.ID, "float_or_int").text == "10.5"
# INT, DICT
assert driver.find_element(By.ID, "int_or_dict").text == "10"
assert driver.find_element(By.ID, "int_and_dict").text == '{"1":2}'
assert driver.find_element(By.ID, "int_eq_dict").text == "false"
assert driver.find_element(By.ID, "int_neq_dict").text == "true"
# FLOAT FLOAT
assert driver.find_element(By.ID, "float_add_float").text == "16"
assert driver.find_element(By.ID, "float_mult_float").text == "57.75"
assert driver.find_element(By.ID, "float_sub_float").text == "5"
assert driver.find_element(By.ID, "float_exp_float").text == "413562.49323606625"
assert driver.find_element(By.ID, "float_div_float").text == "1.9090909090909092"
assert driver.find_element(By.ID, "float_floor_float").text == "1"
assert driver.find_element(By.ID, "float_mod_float").text == "5"
assert driver.find_element(By.ID, "float_gt_float").text == "true"
assert driver.find_element(By.ID, "float_lt_float").text == "false"
assert driver.find_element(By.ID, "float_gte_float").text == "true"
assert driver.find_element(By.ID, "float_lte_float").text == "false"
assert driver.find_element(By.ID, "float_eq_float").text == "false"
assert driver.find_element(By.ID, "float_neq_float").text == "true"
assert driver.find_element(By.ID, "float_and_float").text == "5.5"
assert driver.find_element(By.ID, "float_or_float").text == "10.5"
# FLOAT STR
assert driver.find_element(By.ID, "float_or_str").text == "10.5"
assert driver.find_element(By.ID, "float_and_str").text == "first"
assert driver.find_element(By.ID, "float_eq_str").text == "false"
assert driver.find_element(By.ID, "float_neq_str").text == "true"
# FLOAT,LIST
assert driver.find_element(By.ID, "float_or_list").text == "10.5"
assert driver.find_element(By.ID, "float_and_list").text == "[1,2]"
assert driver.find_element(By.ID, "float_eq_list").text == "false"
assert driver.find_element(By.ID, "float_neq_list").text == "true"
# FLOAT, DICT
assert driver.find_element(By.ID, "float_or_dict").text == "10.5"
assert driver.find_element(By.ID, "float_and_dict").text == '{"1":2}'
assert driver.find_element(By.ID, "float_eq_dict").text == "false"
assert driver.find_element(By.ID, "float_neq_dict").text == "true"
# STR STR
assert driver.find_element(By.ID, "str_add_str").text == "firstsecond"
assert driver.find_element(By.ID, "str_gt_str").text == "false"
assert driver.find_element(By.ID, "str_lt_str").text == "true"
assert driver.find_element(By.ID, "str_gte_str").text == "false"
assert driver.find_element(By.ID, "str_lte_str").text == "true"
assert driver.find_element(By.ID, "str_eq_str").text == "false"
assert driver.find_element(By.ID, "str_neq_str").text == "true"
assert driver.find_element(By.ID, "str_and_str").text == "second"
assert driver.find_element(By.ID, "str_or_str").text == "first"
assert driver.find_element(By.ID, "str_contains").text == "true"
# STR INT
assert (
driver.find_element(By.ID, "str_mult_int").text == "firstfirstfirstfirstfirst"
)
assert driver.find_element(By.ID, "str_and_int").text == "5"
assert driver.find_element(By.ID, "str_or_int").text == "first"
assert driver.find_element(By.ID, "str_eq_int").text == "false"
assert driver.find_element(By.ID, "str_neq_int").text == "true"
# STR, LIST
assert driver.find_element(By.ID, "str_and_list").text == "[1,2]"
assert driver.find_element(By.ID, "str_or_list").text == "first"
assert driver.find_element(By.ID, "str_eq_list").text == "false"
assert driver.find_element(By.ID, "str_neq_list").text == "true"
# STR, DICT
assert driver.find_element(By.ID, "str_or_dict").text == "first"
assert driver.find_element(By.ID, "str_and_dict").text == '{"1":2}'
assert driver.find_element(By.ID, "str_eq_dict").text == "false"
assert driver.find_element(By.ID, "str_neq_dict").text == "true"
# LIST,LIST
assert driver.find_element(By.ID, "list_add_list").text == "[1,2,3,4]"
assert driver.find_element(By.ID, "list_gt_list").text == "false"
assert driver.find_element(By.ID, "list_lt_list").text == "true"
assert driver.find_element(By.ID, "list_gte_list").text == "false"
assert driver.find_element(By.ID, "list_lte_list").text == "true"
assert driver.find_element(By.ID, "list_eq_list").text == "false"
assert driver.find_element(By.ID, "list_neq_list").text == "true"
assert driver.find_element(By.ID, "list_and_list").text == "[3,4]"
assert driver.find_element(By.ID, "list_or_list").text == "[1,2]"
assert driver.find_element(By.ID, "list_contains").text == "true"
assert driver.find_element(By.ID, "list_reverse").text == "[2,1]"
# LIST INT
assert driver.find_element(By.ID, "list_mult_int").text == "[1,2,1,2,1,2,1,2,1,2]"
assert driver.find_element(By.ID, "list_or_int").text == "[1,2]"
assert driver.find_element(By.ID, "list_and_int").text == "10"
assert driver.find_element(By.ID, "list_eq_int").text == "false"
assert driver.find_element(By.ID, "list_neq_int").text == "true"
# LIST DICT
assert driver.find_element(By.ID, "list_and_dict").text == '{"1":2}'
assert driver.find_element(By.ID, "list_or_dict").text == "[1,2]"
assert driver.find_element(By.ID, "list_eq_dict").text == "false"
assert driver.find_element(By.ID, "list_neq_dict").text == "true"
# DICT, DICT
assert driver.find_element(By.ID, "dict_or_dict").text == '{"1":2}'
assert driver.find_element(By.ID, "dict_and_dict").text == '{"3":4}'
assert driver.find_element(By.ID, "dict_eq_dict").text == "false"
assert driver.find_element(By.ID, "dict_neq_dict").text == "true"
assert driver.find_element(By.ID, "dict_contains").text == "true"
for tag, expected in tests:
assert driver.find_element(By.ID, tag).text == expected

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar
from reflex.event import EventHandler, EventChain, EventSpec

View File

@ -447,7 +447,12 @@ class Component(Base, ABC):
return cls(children=children, **props)
def _add_style(self, style):
def _add_style(self, style: dict):
"""Add additional style to the component.
Args:
style: A style dict to apply.
"""
self.style.update(style)
def add_style(self, style: ComponentStyle) -> Component:

View File

@ -39,7 +39,7 @@ class CodeBlock(Component):
wrap_long_lines: Var[bool]
# A custom style for the code block.
custom_style: Var[Dict[str, str]]
custom_style: Dict[str, str] = {}
# Props passed down to the code tag.
code_tag_props: Var[Dict[str, str]]
@ -107,7 +107,6 @@ class CodeBlock(Component):
return code_block
def _add_style(self, style):
self.custom_style = self.custom_style or {}
self.custom_style.update(style) # type: ignore
def _render(self):

View File

@ -1,5 +1,7 @@
"""List components."""
from __future__ import annotations
from reflex.components import Component
from reflex.components.layout.foreach import Foreach
from reflex.components.libs.chakra import ChakraComponent
@ -21,7 +23,9 @@ class List(ChakraComponent):
style_type: Var[str]
@classmethod
def create(cls, *children, items=None, **props) -> Component:
def create(
cls, *children, items: list | Var[list] | None = None, **props
) -> Component:
"""Create a list component.
Args:

View File

@ -12,7 +12,7 @@ from reflex.event import EventHandler, EventChain, EventSpec
class List(ChakraComponent):
@overload
@classmethod
def create(cls, *children, items, spacing: Optional[Union[Var[str], str]] = None, style_position: Optional[Union[Var[str], str]] = None, style_type: Optional[Union[Var[str], str]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "List": # type: ignore
def create(cls, *children, items: Optional[list | Var[list] | None] = None, spacing: Optional[Union[Var[str], str]] = None, style_position: Optional[Union[Var[str], str]] = None, style_type: Optional[Union[Var[str], str]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "List": # type: ignore
"""Create a list component.
Args:
@ -49,7 +49,7 @@ class ListItem(ChakraComponent):
class OrderedList(List):
@overload
@classmethod
def create(cls, *children, items, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "OrderedList": # type: ignore
def create(cls, *children, items: Optional[list | Var[list] | None] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "OrderedList": # type: ignore
"""Create a list component.
Args:
@ -65,7 +65,7 @@ class OrderedList(List):
class UnorderedList(List):
@overload
@classmethod
def create(cls, *children, items, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "UnorderedList": # type: ignore
def create(cls, *children, items: Optional[list | Var[list] | None] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "UnorderedList": # type: ignore
"""Create a list component.
Args:

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar
@ -44,7 +44,7 @@ class PinInput(ChakraComponent):
class PinInputField(ChakraComponent):
@overload
@classmethod
def create(cls, *children, index: Optional[Union[Var[int], int]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "PinInputField": # type: ignore
def create(cls, *children, index: Optional[Var[int]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "PinInputField": # type: ignore
"""Create the component.
Args:

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, List, Optional, Union, overload
from typing import Any, List, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, List, Optional, Union, overload
from typing import Any, Dict, List, Optional, Union, overload
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar
from reflex.event import EventHandler, EventChain, EventSpec

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, List, Optional, Union, overload
from typing import Any, List, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -3,7 +3,7 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Dict, Optional, Union, overload
from typing import Any, Optional, Union, overload
from reflex.components.libs.chakra import ChakraComponent
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar

View File

@ -1,32 +1,60 @@
"""Markdown component."""
from __future__ import annotations
import textwrap
from typing import Callable, Dict, List, Union
from typing import Any, Callable, Dict, Union
from reflex.compiler import utils
from reflex.components.component import Component
from reflex.components.datadisplay.list import ListItem, OrderedList, UnorderedList
from reflex.components.navigation import Link
from reflex.components.tags.tag import Tag
from reflex.components.typography.heading import Heading
from reflex.components.typography.text import Text
from reflex.style import Style
from reflex.utils import types
from reflex.vars import BaseVar, ImportVar, Var
from reflex.utils import console, imports, types
from reflex.vars import ImportVar, Var
# Mapping from markdown tags to components.
components_by_tag: Dict[str, Callable] = {
"h1": Heading,
"h2": Heading,
"h3": Heading,
"h4": Heading,
"h5": Heading,
"h6": Heading,
"p": Text,
"ul": UnorderedList,
"ol": OrderedList,
"li": ListItem,
"a": Link,
}
# Special vars used in the component map.
_CHILDREN = Var.create_safe("children", is_local=False)
_PROPS = Var.create_safe("...props", is_local=False)
# Special remark plugins.
_REMARK_MATH = Var.create_safe("remarkMath", is_local=False)
_REMARK_GFM = Var.create_safe("remarkGfm", is_local=False)
_REMARK_PLUGINS = Var.create_safe([_REMARK_MATH, _REMARK_GFM])
# Special rehype plugins.
_REHYPE_KATEX = Var.create_safe("rehypeKatex", is_local=False)
_REHYPE_RAW = Var.create_safe("rehypeRaw", is_local=False)
_REHYPE_PLUGINS = Var.create_safe([_REHYPE_KATEX, _REHYPE_RAW])
# Component Mapping
def get_base_component_map() -> dict[str, Callable]:
"""Get the base component map.
Returns:
The base component map.
"""
from reflex.components.datadisplay.code import Code, CodeBlock
return {
"h1": lambda value: Heading.create(value, as_="h1", size="2xl"),
"h2": lambda value: Heading.create(value, as_="h2", size="xl"),
"h3": lambda value: Heading.create(value, as_="h3", size="lg"),
"h4": lambda value: Heading.create(value, as_="h4", size="md"),
"h5": lambda value: Heading.create(value, as_="h5", size="sm"),
"h6": lambda value: Heading.create(value, as_="h6", size="xs"),
"p": lambda value: Text.create(value),
"ul": lambda value: UnorderedList.create(value), # type: ignore
"ol": lambda value: OrderedList.create(value), # type: ignore
"li": lambda value: ListItem.create(value),
"a": lambda value: Link.create(value),
"code": lambda value: Code.create(value),
"codeblock": lambda *children, **props: CodeBlock.create(
*children, theme="light", **props
),
}
class Markdown(Component):
@ -38,36 +66,11 @@ class Markdown(Component):
is_default = True
# Custom defined styles for the markdown elements.
custom_styles: Dict[str, Style] = {
k: Style(v)
for k, v in {
"h1": {
"as_": "h1",
"size": "2xl",
},
"h2": {
"as_": "h2",
"size": "xl",
},
"h3": {
"as_": "h3",
"size": "lg",
},
"h4": {
"as_": "h4",
"size": "md",
},
"h5": {
"as_": "h5",
"size": "sm",
},
"h6": {
"as_": "h6",
"size": "xs",
},
}.items()
}
# The component map from a tag to a lambda that creates a component.
component_map: Dict[str, Any] = {}
# Custom styles for the markdown (deprecated in v0.2.9).
custom_styles: Dict[str, Any] = {}
@classmethod
def create(cls, *children, **props) -> Component:
@ -84,13 +87,29 @@ class Markdown(Component):
children[0], Union[str, Var]
), "Markdown component must have exactly one child containing the markdown source."
# Custom styles are deprecated.
if "custom_styles" in props:
console.deprecate(
"rx.markdown custom_styles",
"Use the component_map prop instead.",
"0.2.9",
"0.2.11",
)
# Update the base component map with the custom component map.
component_map = {**get_base_component_map(), **props.pop("component_map", {})}
# Get the markdown source.
src = children[0]
# Dedent the source.
if isinstance(src, str):
src = textwrap.dedent(src)
return super().create(src, **props)
def _get_imports(self):
# Create the component.
return super().create(src, component_map=component_map, **props)
def _get_imports(self) -> imports.ImportDict:
# Import here to avoid circular imports.
from reflex.components.datadisplay.code import Code, CodeBlock
@ -100,16 +119,22 @@ class Markdown(Component):
imports.update(
{
"": {ImportVar(tag="katex/dist/katex.min.css")},
"rehype-katex@^6.0.3": {ImportVar(tag="rehypeKatex", is_default=True)},
"remark-math@^5.1.1": {ImportVar(tag="remarkMath", is_default=True)},
"rehype-raw@^6.1.1": {ImportVar(tag="rehypeRaw", is_default=True)},
"remark-gfm@^3.0.1": {ImportVar(tag="remarkGfm", is_default=True)},
"remark-math@^5.1.1": {
ImportVar(tag=_REMARK_MATH.name, is_default=True)
},
"remark-gfm@^3.0.1": {ImportVar(tag=_REMARK_GFM.name, is_default=True)},
"rehype-katex@^6.0.3": {
ImportVar(tag=_REHYPE_KATEX.name, is_default=True)
},
"rehype-raw@^6.1.1": {ImportVar(tag=_REHYPE_RAW.name, is_default=True)},
}
)
# Get the imports for each component.
for component in components_by_tag.values():
imports = utils.merge_imports(imports, component()._get_imports())
for component in self.component_map.values():
imports = utils.merge_imports(
imports, component(Var.create("")).get_imports()
)
# Get the imports for the code components.
imports = utils.merge_imports(
@ -118,52 +143,87 @@ class Markdown(Component):
imports = utils.merge_imports(imports, Code.create()._get_imports())
return imports
def _render(self):
# Import here to avoid circular imports.
from reflex.components.datadisplay.code import Code, CodeBlock
from reflex.components.tags.tag import Tag
def get_component(self, tag: str, **props) -> Component:
"""Get the component for a tag and props.
def format_props(tag):
return "".join(
Tag(
name="", props=Style(self.custom_styles.get(tag, {}))
).format_props()
)
Args:
tag: The tag of the component.
**props: The props of the component.
Returns:
The component.
Raises:
ValueError: If the tag is invalid.
"""
# Check the tag is valid.
if tag not in self.component_map:
raise ValueError(f"No markdown component found for tag: {tag}.")
special_props = {_PROPS}
children = [_CHILDREN]
# If the children are set as a prop, don't pass them as children.
children_prop = props.pop("children", None)
if children_prop is not None:
special_props.add(Var.create_safe(f"children={str(children_prop)}"))
children = []
# Get the component.
component = self.component_map[tag](*children, **props).set(
special_props=special_props
)
component._add_style(self.custom_styles.get(tag, {}))
return component
def format_component(self, tag: str, **props) -> str:
"""Format a component for rendering in the component map.
Args:
tag: The tag of the component.
**props: Extra props to pass to the component function.
Returns:
The formatted component.
"""
return str(self.get_component(tag, **props)).replace("\n", " ")
def format_component_map(self) -> dict[str, str]:
"""Format the component map for rendering.
Returns:
The formatted component map.
"""
components = {
tag: f"{{({{node, ...props}}) => <{(component().tag)} {{...props}} {format_props(tag)} />}}"
for tag, component in components_by_tag.items()
tag: f"{{({{{_CHILDREN.name}, {_PROPS.name}}}) => {self.format_component(tag)}}}"
for tag in self.component_map
}
# Separate out inline code and code blocks.
components[
"code"
] = f"""{{({{node, inline, className, children, ...props}}) => {{
] = f"""{{({{inline, className, {_CHILDREN.name}, {_PROPS.name}}}) => {{
const match = (className || '').match(/language-(?<lang>.*)/);
const language = match ? match[1] : '';
return !inline ? (
<{CodeBlock().tag}
children={{String(children).replace(/\n$/, '')}}
language={{match ? match[1] : ''}}
style={{light}}
{{...props}}
{format_props("pre")}
/>
{self.format_component("codeblock", language=Var.create_safe("language", is_local=False), children=Var.create_safe("String(children)", is_local=False))}
) : (
<{Code.create().tag} {{...props}} {format_props("code")}>
{{children}}
</{Code.create().tag}>
{self.format_component("code")}
);
}}}}""".replace(
"\n", " "
)
return components
def _render(self) -> Tag:
return (
super()
._render()
.add_props(
components=components,
remark_plugins=BaseVar(name="[remarkMath, remarkGfm]", type_=List[str]),
rehype_plugins=BaseVar(
name="[rehypeKatex, rehypeRaw]", type_=List[str]
),
components=self.format_component_map(),
remark_plugins=_REMARK_PLUGINS,
rehype_plugins=_REHYPE_PLUGINS,
)
.remove_props("custom_components")
.remove_props("componentMap")
)

View File

@ -3,23 +3,23 @@
# This file was generated by `scripts/pyi_generator.py`!
# ------------------------------------------------------
from typing import Callable, Dict, List, Optional, Union, overload
from typing import Any, Callable, Dict, Optional, Union, overload
from reflex.components.component import Component
from reflex.vars import Var, BaseVar, ComputedVar
from reflex.event import EventHandler, EventChain, EventSpec
components_by_tag: Dict[str, Callable]
def get_base_component_map() -> dict[str, Callable]: ...
class Markdown(Component):
@overload
@classmethod
def create(cls, *children, lib_dependencies: Optional[List[str]] = None, custom_styles: Optional[Dict[str, Style]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "Markdown": # type: ignore
def create(cls, *children, component_map: Optional[Dict[str, Any]] = None, custom_styles: Optional[Dict[str, Any]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "Markdown": # type: ignore
"""Create a markdown component.
Args:
*children: The children of the component.
lib_dependencies:
custom_styles: Custom defined styles for the markdown elements.
component_map: The component map from a tag to a lambda that creates a component.
custom_styles: Custom styles for the markdown (deprecated in v0.2.9).
**props: The properties of the component.
Returns:

View File

@ -355,7 +355,7 @@ def format_props(*single_props, **key_value_props) -> list[str]:
f"{name}={format_prop(prop)}"
for name, prop in sorted(key_value_props.items())
if prop is not None
] + [str(prop) for prop in sorted(single_props)]
] + [str(prop) for prop in single_props]
def get_event_handler_parts(handler: EventHandler) -> tuple[str, str]:
@ -574,3 +574,33 @@ def json_dumps(obj: Any) -> str:
A string
"""
return json.dumps(obj, ensure_ascii=False, default=list)
def unwrap_vars(value: str) -> str:
"""Unwrap var values from a JSON string.
For example, "{var}" will be unwrapped to "var".
Args:
value: The JSON string to unwrap.
Returns:
The unwrapped JSON string.
"""
def unescape_double_quotes_in_var(m: re.Match) -> str:
# Since the outer quotes are removed, the inner escaped quotes must be unescaped.
return re.sub('\\\\"', '"', m.group(1))
# This substitution is necessary to unwrap var values.
return re.sub(
pattern=r"""
(?<!\\) # must NOT start with a backslash
" # match opening double quote of JSON value
{(.*?)} # extract the value between curly braces (non-greedy)
" # match must end with an unescaped double quote
""",
repl=unescape_double_quotes_in_var,
string=value,
flags=re.VERBOSE,
)

View File

@ -2,12 +2,12 @@
from __future__ import annotations
import re
import types as builtin_types
from datetime import date, datetime, time, timedelta
from typing import Any, Callable, Dict, Type, Union, get_type_hints
from typing import Any, Callable, Dict, List, Set, Tuple, Type, Union, get_type_hints
from reflex.utils import exceptions, types
from reflex.base import Base
from reflex.utils import exceptions, format, types
# Mapping from type to a serializer.
# The serializer should convert the type to a JSON object.
@ -126,6 +126,38 @@ def serialize_str(value: str) -> str:
return value
@serializer
def serialize_primitive(value: Union[bool, int, float, Base, None]) -> str:
"""Serialize a primitive type.
Args:
value: The number to serialize.
Returns:
The serialized number.
"""
return format.json_dumps(value)
@serializer
def serialize_list(value: Union[List, Tuple, Set]) -> str:
"""Serialize a list to a JSON string.
Args:
value: The list to serialize.
Returns:
The serialized list.
"""
from reflex.vars import Var
# Convert any var values to strings.
fprop = format.json_dumps([str(v) if isinstance(v, Var) else v for v in value])
# Unwrap var values.
return format.unwrap_vars(fprop)
@serializer
def serialize_dict(prop: Dict[str, Any]) -> str:
"""Serialize a dictionary to a JSON string.
@ -141,7 +173,6 @@ def serialize_dict(prop: Dict[str, Any]) -> str:
"""
# Import here to avoid circular imports.
from reflex.event import EventHandler
from reflex.utils.format import json_dumps, to_snake_case
from reflex.vars import Var
prop_dict = {}
@ -150,34 +181,17 @@ def serialize_dict(prop: Dict[str, Any]) -> str:
for key, value in prop.items():
if types._issubclass(type(value), Callable):
raise exceptions.InvalidStylePropError(
f"The style prop `{to_snake_case(key)}` cannot have " # type: ignore
f"The style prop `{format.to_snake_case(key)}` cannot have " # type: ignore
f"`{value.fn.__qualname__ if isinstance(value, EventHandler) else value.__qualname__ if isinstance(value, builtin_types.FunctionType) else value}`, "
f"an event handler or callable as its value"
)
prop_dict[key] = str(value) if isinstance(value, Var) else value
# Dump the dict to a string.
fprop = json_dumps(prop_dict)
fprop = format.json_dumps(prop_dict)
def unescape_double_quotes_in_var(m: re.Match) -> str:
# Since the outer quotes are removed, the inner escaped quotes must be unescaped.
return re.sub('\\\\"', '"', m.group(1))
# This substitution is necessary to unwrap var values.
fprop = re.sub(
pattern=r"""
(?<!\\) # must NOT start with a backslash
" # match opening double quote of JSON value
{(.*?)} # extract the value between curly braces (non-greedy)
" # match must end with an unescaped double quote
""",
repl=unescape_double_quotes_in_var,
string=fprop,
flags=re.VERBOSE,
)
# Return the formatted dict.
return fprop
# Unwrap var values.
return format.unwrap_vars(fprop)
@serializer

View File

@ -104,7 +104,7 @@ def _issubclass(cls: GenericType, cls_check: GenericType) -> bool:
# Special check for Any.
if cls_check == Any:
return True
if cls in [Any, Callable]:
if cls in [Any, Callable, None]:
return False
# Get the base classes.

View File

@ -122,19 +122,14 @@ class Var(ABC):
if isinstance(value, Var):
return value
type_ = type(value)
# Try to serialize the value.
serialized = serialize(value)
if serialized is not None:
value = serialized
try:
name = value if isinstance(value, str) else json.dumps(value)
except TypeError as e:
type_ = type(value)
name = serialize(value)
if name is None:
raise TypeError(
f"No JSON serializer found for var {value} of type {type_}."
) from e
)
name = name if isinstance(name, str) else format.json_dumps(name)
return BaseVar(name=name, type_=type_, is_local=is_local, is_string=is_string)
@ -202,13 +197,17 @@ class Var(ABC):
and self.is_local == other.is_local
)
def to_string(self) -> Var:
def to_string(self, json: bool = True) -> Var:
"""Convert a var to a string.
Args:
json: Whether to convert to a JSON string.
Returns:
The stringified var.
"""
return self.operation(fn="JSON.stringify", type_=str)
fn = "JSON.stringify" if json else "String"
return self.operation(fn=fn, type_=str)
def __hash__(self) -> int:
"""Define a hash function for a var.
@ -945,9 +944,7 @@ class Var(ABC):
Returns:
A var representing the contain check.
"""
if self.type_ is None or not (
types._issubclass(self.type_, Union[dict, list, tuple, str])
):
if not (types._issubclass(self.type_, Union[dict, list, tuple, str])):
raise TypeError(
f"Var {self.full_name} of type {self.type_} does not support contains check."
)
@ -987,7 +984,7 @@ class Var(ABC):
Returns:
A var with the reversed list.
"""
if self.type_ is None or not types._issubclass(self.type_, list):
if not types._issubclass(self.type_, list):
raise TypeError(f"Cannot reverse non-list var {self.full_name}.")
return BaseVar(
@ -996,6 +993,97 @@ class Var(ABC):
is_local=self.is_local,
)
def lower(self) -> Var:
"""Convert a string var to lowercase.
Returns:
A var with the lowercase string.
Raises:
TypeError: If the var is not a string.
"""
if not types._issubclass(self.type_, str):
raise TypeError(
f"Cannot convert non-string var {self.full_name} to lowercase."
)
return BaseVar(
name=f"{self.full_name}.toLowerCase()",
type_=str,
is_local=self.is_local,
)
def upper(self) -> Var:
"""Convert a string var to uppercase.
Returns:
A var with the uppercase string.
Raises:
TypeError: If the var is not a string.
"""
if not types._issubclass(self.type_, str):
raise TypeError(
f"Cannot convert non-string var {self.full_name} to uppercase."
)
return BaseVar(
name=f"{self.full_name}.toUpperCase()",
type_=str,
is_local=self.is_local,
)
def split(self, other: str | Var[str] = " ") -> Var:
"""Split a string var into a list.
Args:
other: The string to split the var with.
Returns:
A var with the list.
Raises:
TypeError: If the var is not a string.
"""
if not types._issubclass(self.type_, str):
raise TypeError(f"Cannot split non-string var {self.full_name}.")
other = Var.create_safe(json.dumps(other)) if isinstance(other, str) else other
return BaseVar(
name=f"{self.full_name}.split({other.full_name})",
type_=list[str],
is_local=self.is_local,
)
def join(self, other: str | Var[str] | None = None) -> Var:
"""Join a list var into a string.
Args:
other: The string to join the list with.
Returns:
A var with the string.
Raises:
TypeError: If the var is not a list.
"""
if not types._issubclass(self.type_, list):
raise TypeError(f"Cannot join non-list var {self.full_name}.")
if other is None:
other = Var.create_safe("")
if isinstance(other, str):
other = Var.create_safe(json.dumps(other))
else:
other = Var.create_safe(other)
return BaseVar(
name=f"{self.full_name}.join({other.full_name})",
type_=str,
is_local=self.is_local,
)
def foreach(self, fn: Callable) -> Var:
"""Return a list of components. after doing a foreach on this var.

View File

@ -233,7 +233,7 @@ class PyiGenerator:
local_variables = [
(name, obj)
for name, obj in vars(self.current_module).items()
if not name.startswith("__")
if not name.startswith("_")
and not inspect.isclass(obj)
and not inspect.isfunction(obj)
]

View File

View File

@ -0,0 +1,59 @@
import pytest
import reflex as rx
from reflex.components.typography.markdown import Markdown
@pytest.mark.parametrize(
"tag,expected",
[
("h1", "Heading"),
("h2", "Heading"),
("h3", "Heading"),
("h4", "Heading"),
("h5", "Heading"),
("h6", "Heading"),
("p", "Text"),
("ul", "UnorderedList"),
("ol", "OrderedList"),
("li", "ListItem"),
("a", "Link"),
("code", "Code"),
],
)
def test_get_component(tag, expected):
"""Test getting a component from the component map.
Args:
tag: The tag to get.
expected: The expected component.
"""
md = Markdown.create("# Hello")
assert tag in md.component_map # type: ignore
assert md.get_component(tag).tag == expected # type: ignore
def test_set_component_map():
"""Test setting the component map."""
component_map = {
"h1": lambda value: rx.box(
rx.heading(value, as_="h1", size="2xl"), padding="1em"
),
"p": lambda value: rx.box(rx.text(value), padding="1em"),
}
md = Markdown.create("# Hello", component_map=component_map)
# Check that the new tags have been added.
assert md.get_component("h1").tag == "Box" # type: ignore
assert md.get_component("p").tag == "Box" # type: ignore
# Make sure the old tags are still there.
assert md.get_component("h2").tag == "Heading" # type: ignore
def test_pass_custom_styles():
"""Test that passing custom styles works."""
md = Markdown.create("# Hello", custom_styles={"h1": {"color": "red"}})
comp = md.get_component("h1") # type: ignore
assert comp.style == {"color": "red"}

View File

@ -1,9 +1,10 @@
import datetime
from typing import Any, Dict, Type
from typing import Any, Dict, List, Type
import pytest
from reflex.utils import serializers
from reflex.vars import Var
@pytest.mark.parametrize(
@ -29,12 +30,19 @@ def test_has_serializer(type_: Type, expected: bool):
"type_,expected",
[
(str, serializers.serialize_str),
(list, serializers.serialize_list),
(tuple, serializers.serialize_list),
(set, serializers.serialize_list),
(dict, serializers.serialize_dict),
(List[str], serializers.serialize_list),
(Dict[int, int], serializers.serialize_dict),
(datetime.datetime, serializers.serialize_datetime),
(datetime.date, serializers.serialize_datetime),
(datetime.time, serializers.serialize_datetime),
(datetime.timedelta, serializers.serialize_datetime),
(int, serializers.serialize_primitive),
(float, serializers.serialize_primitive),
(bool, serializers.serialize_primitive),
],
)
def test_get_serializer(type_: Type, expected: serializers.Serializer):
@ -51,8 +59,14 @@ def test_get_serializer(type_: Type, expected: serializers.Serializer):
def test_add_serializer():
"""Test that adding a serializer works."""
def serialize_test(value: int) -> str:
"""Serialize an int to a string.
class Foo:
"""A test class."""
def __init__(self, name: str):
self.name = name
def serialize_foo(value: Foo) -> str:
"""Serialize an foo to a string.
Args:
value: The value to serialize.
@ -60,35 +74,53 @@ def test_add_serializer():
Returns:
The serialized value.
"""
return str(value)
return value.name
# Initially there should be no serializer for int.
assert not serializers.has_serializer(int)
assert serializers.serialize(5) is None
assert not serializers.has_serializer(Foo)
assert serializers.serialize(Foo("hi")) is None
# Register the serializer.
assert serializers.serializer(serialize_test) == serialize_test
assert serializers.serializer(serialize_foo) == serialize_foo
# There should now be a serializer for int.
assert serializers.has_serializer(int)
assert serializers.get_serializer(int) == serialize_test
assert serializers.serialize(5) == "5"
assert serializers.has_serializer(Foo)
assert serializers.get_serializer(Foo) == serialize_foo
assert serializers.serialize(Foo("hi")) == "hi"
# Remove the serializer.
serializers.SERIALIZERS.pop(int)
serializers.SERIALIZERS.pop(Foo)
assert not serializers.has_serializer(Foo)
@pytest.mark.parametrize(
"value,expected",
[
("test", "test"),
(1, "1"),
(1.0, "1.0"),
(True, "true"),
(False, "false"),
(None, "null"),
([1, 2, 3], "[1, 2, 3]"),
([1, "2", 3.0], '[1, "2", 3.0]'),
(
[1, Var.create_safe("hi"), Var.create_safe("bye", is_local=False)],
'[1, "hi", bye]',
),
(
(1, Var.create_safe("hi"), Var.create_safe("bye", is_local=False)),
'[1, "hi", bye]',
),
({1: 2, 3: 4}, '{"1": 2, "3": 4}'),
(
{1: Var.create_safe("hi"), 3: Var.create_safe("bye", is_local=False)},
'{"1": "hi", "3": bye}',
),
(datetime.datetime(2021, 1, 1, 1, 1, 1, 1), "2021-01-01 01:01:01.000001"),
(datetime.date(2021, 1, 1), "2021-01-01"),
(datetime.time(1, 1, 1, 1), "01:01:01.000001"),
(datetime.timedelta(1, 1, 1), "1 day, 0:00:01.000001"),
(5, None),
(None, None),
([], None),
],
)
def test_serialize(value: Any, expected: str):