diff --git a/reflex/components/core/colors.py b/reflex/components/core/colors.py index fbc6825aa..cd14266e6 100644 --- a/reflex/components/core/colors.py +++ b/reflex/components/core/colors.py @@ -2,10 +2,11 @@ from reflex.constants.colors import Color, ColorType, ShadeType from reflex.utils.types import validate_parameter_literals +from reflex.vars import Var @validate_parameter_literals -def color(color: ColorType, shade: ShadeType = 7, alpha: bool = False) -> Color: +def color(color: ColorType, shade: ShadeType = 7, alpha: bool = False) -> Var: """Create a color object. Args: @@ -16,4 +17,4 @@ def color(color: ColorType, shade: ShadeType = 7, alpha: bool = False) -> Color: Returns: The color object. """ - return Color(color, shade, alpha) + return Var.create(Color(color, shade, alpha))._replace(_var_is_string=True) # type: ignore diff --git a/reflex/utils/format.py b/reflex/utils/format.py index d79fc1e50..9c67f1fc0 100644 --- a/reflex/utils/format.py +++ b/reflex/utils/format.py @@ -253,24 +253,20 @@ def format_cond( # Use Python truthiness. cond = f"isTrue({cond})" + def create_var(cond_part): + return Var.create_safe(cond_part, _var_is_string=type(cond_part) is str) + # Format prop conds. if is_prop: - if not isinstance(true_value, Var): - true_value = Var.create_safe( - true_value, - _var_is_string=type(true_value) is str, - ) + true_value = create_var(true_value) prop1 = true_value._replace( _var_is_local=True, ) - if not isinstance(false_value, Var): - false_value = Var.create_safe( - false_value, - _var_is_string=type(false_value) is str, - ) + + false_value = create_var(false_value) prop2 = false_value._replace(_var_is_local=True) - prop1, prop2 = str(prop1), str(prop2) # avoid f-string semantics for Var - return f"{cond} ? {prop1} : {prop2}".replace("{", "").replace("}", "") + # unwrap '{}' to avoid f-string semantics for Var + return f"{cond} ? {prop1._var_name_unwrapped} : {prop2._var_name_unwrapped}" # Format component conds. return wrap(f"{cond} ? {true_value} : {false_value}", "{") diff --git a/reflex/vars.py b/reflex/vars.py index 414bb357b..ac0d0a8d2 100644 --- a/reflex/vars.py +++ b/reflex/vars.py @@ -1612,13 +1612,13 @@ class Var: if types.is_generic_alias(self._var_type) else self._var_type ) - wrapped_var = str(self) + return ( wrapped_var if not self._var_state - and issubclass(type_, dict) - or issubclass(type_, Style) + and types._issubclass(type_, dict) + or types._issubclass(type_, Style) else wrapped_var.strip("{}") ) diff --git a/tests/components/core/__init__.py b/tests/components/core/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/components/core/test_colors.py b/tests/components/core/test_colors.py new file mode 100644 index 000000000..ef1fb8978 --- /dev/null +++ b/tests/components/core/test_colors.py @@ -0,0 +1,66 @@ +import pytest + +import reflex as rx + + +class ColorState(rx.State): + """Test color state.""" + + color: str = "mint" + shade: int = 4 + + +@pytest.mark.parametrize( + "color, expected", + [ + (rx.color("mint"), "{`var(--mint-7)`}"), + (rx.color("mint", 3), "{`var(--mint-3)`}"), + (rx.color("mint", 3, True), "{`var(--mint-a3)`}"), + ( + rx.color(ColorState.color, ColorState.shade), # type: ignore + "{`var(--${state__color_state.color}-${state__color_state.shade})`}", + ), + ], +) +def test_color(color, expected): + assert str(color) == expected + + +@pytest.mark.parametrize( + "cond_var, expected", + [ + ( + rx.cond(True, rx.color("mint"), rx.color("tomato", 5)), + "{isTrue(true) ? `var(--mint-7)` : `var(--tomato-5)`}", + ), + ( + rx.cond(True, rx.color(ColorState.color), rx.color(ColorState.color, 5)), # type: ignore + "{isTrue(true) ? `var(--${state__color_state.color}-7)` : `var(--${state__color_state.color}-5)`}", + ), + ( + rx.match( + "condition", + ("first", rx.color("mint")), + ("second", rx.color("tomato", 5)), + rx.color(ColorState.color, 2), # type: ignore + ), + "{(() => { switch (JSON.stringify(`condition`)) {case JSON.stringify(`first`): return (`var(--mint-7)`);" + " break;case JSON.stringify(`second`): return (`var(--tomato-5)`); break;default: " + "return (`var(--${state__color_state.color}-2)`); break;};})()}", + ), + ( + rx.match( + "condition", + ("first", rx.color(ColorState.color)), # type: ignore + ("second", rx.color(ColorState.color, 5)), # type: ignore + rx.color(ColorState.color, 2), # type: ignore + ), + "{(() => { switch (JSON.stringify(`condition`)) {case JSON.stringify(`first`): " + "return (`var(--${state__color_state.color}-7)`); break;case JSON.stringify(`second`): " + "return (`var(--${state__color_state.color}-5)`); break;default: " + "return (`var(--${state__color_state.color}-2)`); break;};})()}", + ), + ], +) +def test_color_with_conditionals(cond_var, expected): + assert str(cond_var) == expected diff --git a/tests/utils/test_serializers.py b/tests/utils/test_serializers.py index e136c5300..ba3dd993e 100644 --- a/tests/utils/test_serializers.py +++ b/tests/utils/test_serializers.py @@ -5,7 +5,7 @@ from typing import Any, Dict, List, Type import pytest from reflex.base import Base -from reflex.components.core.colors import color +from reflex.components.core.colors import Color from reflex.utils import serializers from reflex.vars import Var @@ -170,8 +170,8 @@ class BaseSubclass(Base): [datetime.timedelta(1, 1, 1), datetime.timedelta(1, 1, 2)], '["1 day, 0:00:01.000001", "1 day, 0:00:01.000002"]', ), - (color("slate", shade=1), "var(--slate-1)"), - (color("orange", shade=1, alpha=True), "var(--orange-a1)"), + (Color(color="slate", shade=1), "var(--slate-1)"), + (Color(color="orange", shade=1, alpha=True), "var(--orange-a1)"), ], ) def test_serialize(value: Any, expected: str):