|
@@ -1,5 +1,10 @@
|
|
|
+from __future__ import annotations
|
|
|
+
|
|
|
+from typing import Any
|
|
|
+
|
|
|
import pytest
|
|
|
|
|
|
+import reflex as rx
|
|
|
from reflex import style
|
|
|
from reflex.vars import Var
|
|
|
|
|
@@ -8,6 +13,12 @@ test_style = [
|
|
|
({"a": Var.create("abc")}, {"a": "abc"}),
|
|
|
({"test_case": 1}, {"testCase": 1}),
|
|
|
({"test_case": {"a": 1}}, {"testCase": {"a": 1}}),
|
|
|
+ ({":test_case": {"a": 1}}, {":testCase": {"a": 1}}),
|
|
|
+ ({"::test_case": {"a": 1}}, {"::testCase": {"a": 1}}),
|
|
|
+ (
|
|
|
+ {"::-webkit-scrollbar": {"display": "none"}},
|
|
|
+ {"::WebkitScrollbar": {"display": "none"}},
|
|
|
+ ),
|
|
|
]
|
|
|
|
|
|
|
|
@@ -38,3 +49,370 @@ def test_create_style(style_dict, expected):
|
|
|
expected: The expected formatted style.
|
|
|
"""
|
|
|
assert style.Style(style_dict) == expected
|
|
|
+
|
|
|
+
|
|
|
+def compare_dict_of_var(d1: dict[str, Any], d2: dict[str, Any]):
|
|
|
+ """Compare two dictionaries of Var objects.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ d1: The first dictionary.
|
|
|
+ d2: The second dictionary.
|
|
|
+ """
|
|
|
+ assert len(d1) == len(d2)
|
|
|
+ for key, value in d1.items():
|
|
|
+ assert key in d2
|
|
|
+ if isinstance(value, dict):
|
|
|
+ compare_dict_of_var(value, d2[key])
|
|
|
+ elif isinstance(value, Var):
|
|
|
+ assert value.equals(d2[key])
|
|
|
+ else:
|
|
|
+ assert value == d2[key]
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.parametrize(
|
|
|
+ ("kwargs", "style_dict", "expected_get_style"),
|
|
|
+ [
|
|
|
+ ({}, {}, {"css": None}),
|
|
|
+ ({"color": "hotpink"}, {}, {"css": Var.create({"color": "hotpink"})}),
|
|
|
+ ({}, {"color": "red"}, {"css": Var.create({"color": "red"})}),
|
|
|
+ (
|
|
|
+ {"color": "hotpink"},
|
|
|
+ {"color": "red"},
|
|
|
+ {"css": Var.create({"color": "hotpink"})},
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {"_hover": {"color": "hotpink"}},
|
|
|
+ {},
|
|
|
+ {"css": Var.create({"&:hover": {"color": "hotpink"}})},
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {},
|
|
|
+ {"_hover": {"color": "red"}},
|
|
|
+ {"css": Var.create({"&:hover": {"color": "red"}})},
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {},
|
|
|
+ {":hover": {"color": "red"}},
|
|
|
+ {"css": Var.create({"&:hover": {"color": "red"}})},
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {},
|
|
|
+ {"::-webkit-scrollbar": {"display": "none"}},
|
|
|
+ {"css": Var.create({"&::-webkit-scrollbar": {"display": "none"}})},
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {},
|
|
|
+ {"::-moz-progress-bar": {"background_color": "red"}},
|
|
|
+ {"css": Var.create({"&::-moz-progress-bar": {"backgroundColor": "red"}})},
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {"color": ["#111", "#222", "#333", "#444", "#555"]},
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ "css": Var.create(
|
|
|
+ {
|
|
|
+ "@media screen and (min-width: 0)": {"color": "#111"},
|
|
|
+ "@media screen and (min-width: 30em)": {"color": "#222"},
|
|
|
+ "@media screen and (min-width: 48em)": {"color": "#333"},
|
|
|
+ "@media screen and (min-width: 62em)": {"color": "#444"},
|
|
|
+ "@media screen and (min-width: 80em)": {"color": "#555"},
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {
|
|
|
+ "color": ["#111", "#222", "#333", "#444", "#555"],
|
|
|
+ "background_color": "#FFF",
|
|
|
+ },
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ "css": Var.create(
|
|
|
+ {
|
|
|
+ "@media screen and (min-width: 0)": {"color": "#111"},
|
|
|
+ "@media screen and (min-width: 30em)": {"color": "#222"},
|
|
|
+ "@media screen and (min-width: 48em)": {"color": "#333"},
|
|
|
+ "@media screen and (min-width: 62em)": {"color": "#444"},
|
|
|
+ "@media screen and (min-width: 80em)": {"color": "#555"},
|
|
|
+ "backgroundColor": "#FFF",
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {
|
|
|
+ "color": ["#111", "#222", "#333", "#444", "#555"],
|
|
|
+ "background_color": ["#FFF", "#EEE", "#DDD", "#CCC", "#BBB"],
|
|
|
+ },
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ "css": Var.create(
|
|
|
+ {
|
|
|
+ "@media screen and (min-width: 0)": {
|
|
|
+ "color": "#111",
|
|
|
+ "backgroundColor": "#FFF",
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 30em)": {
|
|
|
+ "color": "#222",
|
|
|
+ "backgroundColor": "#EEE",
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 48em)": {
|
|
|
+ "color": "#333",
|
|
|
+ "backgroundColor": "#DDD",
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 62em)": {
|
|
|
+ "color": "#444",
|
|
|
+ "backgroundColor": "#CCC",
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 80em)": {
|
|
|
+ "color": "#555",
|
|
|
+ "backgroundColor": "#BBB",
|
|
|
+ },
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {
|
|
|
+ "_hover": [
|
|
|
+ {"color": "#111"},
|
|
|
+ {"color": "#222"},
|
|
|
+ {"color": "#333"},
|
|
|
+ {"color": "#444"},
|
|
|
+ {"color": "#555"},
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ "css": Var.create(
|
|
|
+ {
|
|
|
+ "&:hover": {
|
|
|
+ "@media screen and (min-width: 0)": {"color": "#111"},
|
|
|
+ "@media screen and (min-width: 30em)": {"color": "#222"},
|
|
|
+ "@media screen and (min-width: 48em)": {"color": "#333"},
|
|
|
+ "@media screen and (min-width: 62em)": {"color": "#444"},
|
|
|
+ "@media screen and (min-width: 80em)": {"color": "#555"},
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {"_hover": {"color": ["#111", "#222", "#333", "#444", "#555"]}},
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ "css": Var.create(
|
|
|
+ {
|
|
|
+ "&:hover": {
|
|
|
+ "@media screen and (min-width: 0)": {"color": "#111"},
|
|
|
+ "@media screen and (min-width: 30em)": {"color": "#222"},
|
|
|
+ "@media screen and (min-width: 48em)": {"color": "#333"},
|
|
|
+ "@media screen and (min-width: 62em)": {"color": "#444"},
|
|
|
+ "@media screen and (min-width: 80em)": {"color": "#555"},
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {
|
|
|
+ "_hover": {
|
|
|
+ "color": ["#111", "#222", "#333", "#444", "#555"],
|
|
|
+ "background_color": ["#FFF", "#EEE", "#DDD", "#CCC", "#BBB"],
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ "css": Var.create(
|
|
|
+ {
|
|
|
+ "&:hover": {
|
|
|
+ "@media screen and (min-width: 0)": {
|
|
|
+ "color": "#111",
|
|
|
+ "backgroundColor": "#FFF",
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 30em)": {
|
|
|
+ "color": "#222",
|
|
|
+ "backgroundColor": "#EEE",
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 48em)": {
|
|
|
+ "color": "#333",
|
|
|
+ "backgroundColor": "#DDD",
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 62em)": {
|
|
|
+ "color": "#444",
|
|
|
+ "backgroundColor": "#CCC",
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 80em)": {
|
|
|
+ "color": "#555",
|
|
|
+ "backgroundColor": "#BBB",
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {
|
|
|
+ "_hover": {
|
|
|
+ "color": ["#111", "#222", "#333", "#444", "#555"],
|
|
|
+ "background_color": "#FFF",
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {},
|
|
|
+ {
|
|
|
+ "css": Var.create(
|
|
|
+ {
|
|
|
+ "&:hover": {
|
|
|
+ "@media screen and (min-width: 0)": {"color": "#111"},
|
|
|
+ "@media screen and (min-width: 30em)": {"color": "#222"},
|
|
|
+ "@media screen and (min-width: 48em)": {"color": "#333"},
|
|
|
+ "@media screen and (min-width: 62em)": {"color": "#444"},
|
|
|
+ "@media screen and (min-width: 80em)": {"color": "#555"},
|
|
|
+ "backgroundColor": "#FFF",
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+)
|
|
|
+def test_style_via_component(
|
|
|
+ kwargs: dict[str, Any],
|
|
|
+ style_dict: dict[str, Any],
|
|
|
+ expected_get_style: dict[str, Any],
|
|
|
+):
|
|
|
+ """Pass kwargs and style_dict to a component and assert the final, combined style dict.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ kwargs: The kwargs to pass to the component.
|
|
|
+ style_dict: The style_dict to pass to the component.
|
|
|
+ expected_get_style: The expected style dict.
|
|
|
+ """
|
|
|
+ comp = rx.el.div(style=style_dict, **kwargs) # type: ignore
|
|
|
+ compare_dict_of_var(comp._get_style(), expected_get_style)
|
|
|
+
|
|
|
+
|
|
|
+class StyleState(rx.State):
|
|
|
+ """Style vars in a substate."""
|
|
|
+
|
|
|
+ color: str = "hotpink"
|
|
|
+ color2: str = "red"
|
|
|
+
|
|
|
+
|
|
|
+@pytest.mark.parametrize(
|
|
|
+ ("kwargs", "expected_get_style"),
|
|
|
+ [
|
|
|
+ (
|
|
|
+ {"color": StyleState.color},
|
|
|
+ {"css": Var.create({"color": StyleState.color})},
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {"color": f"dark{StyleState.color}"},
|
|
|
+ {"css": Var.create_safe(f'{{"color": `dark{StyleState.color}`}}').to(dict)},
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {"color": StyleState.color, "_hover": {"color": StyleState.color2}},
|
|
|
+ {
|
|
|
+ "css": Var.create(
|
|
|
+ {
|
|
|
+ "color": StyleState.color,
|
|
|
+ "&:hover": {"color": StyleState.color2},
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {"color": [StyleState.color, "gray", StyleState.color2, "yellow", "blue"]},
|
|
|
+ {
|
|
|
+ "css": Var.create(
|
|
|
+ {
|
|
|
+ "@media screen and (min-width: 0)": {"color": StyleState.color},
|
|
|
+ "@media screen and (min-width: 30em)": {"color": "gray"},
|
|
|
+ "@media screen and (min-width: 48em)": {
|
|
|
+ "color": StyleState.color2
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 62em)": {"color": "yellow"},
|
|
|
+ "@media screen and (min-width: 80em)": {"color": "blue"},
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {
|
|
|
+ "_hover": [
|
|
|
+ {"color": StyleState.color},
|
|
|
+ {"color": StyleState.color2},
|
|
|
+ {"color": "#333"},
|
|
|
+ {"color": "#444"},
|
|
|
+ {"color": "#555"},
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "css": Var.create(
|
|
|
+ {
|
|
|
+ "&:hover": {
|
|
|
+ "@media screen and (min-width: 0)": {
|
|
|
+ "color": StyleState.color
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 30em)": {
|
|
|
+ "color": StyleState.color2
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 48em)": {"color": "#333"},
|
|
|
+ "@media screen and (min-width: 62em)": {"color": "#444"},
|
|
|
+ "@media screen and (min-width: 80em)": {"color": "#555"},
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ (
|
|
|
+ {
|
|
|
+ "_hover": {
|
|
|
+ "color": [
|
|
|
+ StyleState.color,
|
|
|
+ StyleState.color2,
|
|
|
+ "#333",
|
|
|
+ "#444",
|
|
|
+ "#555",
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "css": Var.create(
|
|
|
+ {
|
|
|
+ "&:hover": {
|
|
|
+ "@media screen and (min-width: 0)": {
|
|
|
+ "color": StyleState.color
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 30em)": {
|
|
|
+ "color": StyleState.color2
|
|
|
+ },
|
|
|
+ "@media screen and (min-width: 48em)": {"color": "#333"},
|
|
|
+ "@media screen and (min-width: 62em)": {"color": "#444"},
|
|
|
+ "@media screen and (min-width: 80em)": {"color": "#555"},
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+)
|
|
|
+def test_style_via_component_with_state(
|
|
|
+ kwargs: dict[str, Any],
|
|
|
+ expected_get_style: dict[str, Any],
|
|
|
+):
|
|
|
+ """Pass kwargs to a component with state vars and assert the final, combined style dict.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ kwargs: The kwargs to pass to the component.
|
|
|
+ expected_get_style: The expected style dict.
|
|
|
+ """
|
|
|
+ comp = rx.el.div(**kwargs)
|
|
|
+
|
|
|
+ assert comp.style._var_data == expected_get_style["css"]._var_data
|
|
|
+ # Remove the _var_data from the expected style, since the emotion-formatted
|
|
|
+ # style dict won't actually have it.
|
|
|
+ expected_get_style["css"]._var_data = None
|
|
|
+
|
|
|
+ # Assert that style values are equal.
|
|
|
+ compare_dict_of_var(comp._get_style(), expected_get_style)
|