123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543 |
- from __future__ import annotations
- from typing import Any, Dict
- import pytest
- import reflex as rx
- from reflex import style
- from reflex.components.component import evaluate_style_namespaces
- from reflex.style import Style
- from reflex.vars import VarData
- from reflex.vars.base import LiteralVar, Var
- test_style = [
- ({"a": 1}, {"a": 1}),
- ({"a": LiteralVar.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"}},
- {"::-webkit-scrollbar": {"display": "none"}},
- ),
- ({"margin_y": "2rem"}, {"marginTop": "2rem", "marginBottom": "2rem"}),
- ({"marginY": "2rem"}, {"marginTop": "2rem", "marginBottom": "2rem"}),
- (
- {"::-webkit-scrollbar": {"bgColor": "red"}},
- {"::-webkit-scrollbar": {"backgroundColor": "red"}},
- ),
- (
- {"paddingX": ["2rem", "3rem"]},
- {
- "paddingInlineStart": ["2rem", "3rem"],
- "paddingInlineEnd": ["2rem", "3rem"],
- },
- ),
- ]
- @pytest.mark.parametrize(
- "style_dict,expected",
- test_style,
- )
- def test_convert(style_dict, expected):
- """Test Format a style dictionary.
- Args:
- style_dict: The style to check.
- expected: The expected formatted style.
- """
- converted_dict, _var_data = style.convert(style_dict)
- assert LiteralVar.create(converted_dict).equals(LiteralVar.create(expected))
- @pytest.mark.parametrize(
- "style_dict,expected",
- test_style,
- )
- def test_create_style(style_dict, expected):
- """Test style dictionary.
- Args:
- style_dict: The style to check.
- expected: The expected formatted style.
- """
- assert LiteralVar.create(style.Style(style_dict)).equals(
- LiteralVar.create(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"),
- [
- ({}, {}, {}),
- (
- {"color": "hotpink"},
- {},
- {"css": LiteralVar.create(Style({"color": "hotpink"}))},
- ),
- ({}, {"color": "red"}, {"css": LiteralVar.create(Style({"color": "red"}))}),
- (
- {"color": "hotpink"},
- {"color": "red"},
- {"css": LiteralVar.create(Style({"color": "hotpink"}))},
- ),
- (
- {"_hover": {"color": "hotpink"}},
- {},
- {"css": LiteralVar.create(Style({"&:hover": {"color": "hotpink"}}))},
- ),
- (
- {},
- {"_hover": {"color": "red"}},
- {"css": LiteralVar.create(Style({"&:hover": {"color": "red"}}))},
- ),
- (
- {},
- {":hover": {"color": "red"}},
- {"css": LiteralVar.create(Style({"&:hover": {"color": "red"}}))},
- ),
- (
- {},
- {"::-webkit-scrollbar": {"display": "none"}},
- {
- "css": LiteralVar.create(
- Style({"&::-webkit-scrollbar": {"display": "none"}})
- )
- },
- ),
- (
- {},
- {"::-moz-progress-bar": {"background_color": "red"}},
- {
- "css": LiteralVar.create(
- Style({"&::-moz-progress-bar": {"backgroundColor": "red"}})
- )
- },
- ),
- (
- {"color": ["#111", "#222", "#333", "#444", "#555"]},
- {},
- {
- "css": LiteralVar.create(
- Style(
- {
- "@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": LiteralVar.create(
- Style(
- {
- "@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": LiteralVar.create(
- Style(
- {
- "@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": LiteralVar.create(
- Style(
- {
- "&: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": LiteralVar.create(
- Style(
- {
- "&: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": LiteralVar.create(
- Style(
- {
- "&: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": LiteralVar.create(
- Style(
- {
- "&: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": LiteralVar.create(Style({"color": StyleState.color}))},
- ),
- (
- {"color": f"dark{StyleState.color}"},
- {
- "css": Var(
- _js_expr=f'({{ ["color"] : ("dark"+{StyleState.color}) }})'
- ).to(Dict[str, str])
- },
- ),
- (
- {"color": StyleState.color, "_hover": {"color": StyleState.color2}},
- {
- "css": LiteralVar.create(
- Style(
- {
- "color": StyleState.color,
- "&:hover": {"color": StyleState.color2},
- }
- )
- )
- },
- ),
- (
- {"color": [StyleState.color, "gray", StyleState.color2, "yellow", "blue"]},
- {
- "css": LiteralVar.create(
- Style(
- {
- "@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": LiteralVar.create(
- Style(
- {
- "&: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": LiteralVar.create(
- Style(
- {
- "&: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 (
- VarData.merge(comp.style._var_data)
- == expected_get_style["css"]._get_all_var_data()
- )
- # Assert that style values are equal.
- compare_dict_of_var(comp._get_style(), expected_get_style)
- def test_evaluate_style_namespaces():
- """Test that namespaces get converted to component create functions."""
- style_dict = {rx.text: {"color": "blue"}}
- assert rx.text.__call__ not in style_dict
- style_dict = evaluate_style_namespaces(style_dict) # type: ignore
- assert rx.text.__call__ in style_dict
- def test_style_update_with_var_data():
- """Test that .update with a Style containing VarData works."""
- red_var = LiteralVar.create("red")._replace(
- merge_var_data=VarData(hooks={"const red = true": None}), # type: ignore
- )
- blue_var = LiteralVar.create("blue")._replace(
- merge_var_data=VarData(hooks={"const blue = true": None}), # type: ignore
- )
- s1 = Style(
- {
- "color": red_var,
- }
- )
- s2 = Style()
- s2.update(s1, background_color=f"{blue_var}ish")
- assert str(LiteralVar.create(s2)) == str(
- LiteralVar.create({"color": "red", "backgroundColor": "blueish"})
- )
- assert s2._var_data is not None
- assert "const red = true" in s2._var_data.hooks
- assert "const blue = true" in s2._var_data.hooks
|