[REF-1417] Convert underscore-prefixed style props to pseudo selector (#2266)
This commit is contained in:
parent
421be5748b
commit
e52267477c
@ -31,6 +31,12 @@ def FullyControlledInput():
|
||||
),
|
||||
rx.input(value=State.text, id="value_input", is_read_only=True),
|
||||
rx.input(on_change=State.set_text, id="on_change_input"), # type: ignore
|
||||
rx.el.input(
|
||||
value=State.text,
|
||||
id="plain_value_input",
|
||||
disabled=True,
|
||||
_disabled={"background_color": "#EEE"},
|
||||
),
|
||||
rx.button("CLEAR", on_click=rx.set_value("on_change_input", "")),
|
||||
)
|
||||
|
||||
@ -76,9 +82,15 @@ async def test_fully_controlled_input(fully_controlled_input: AppHarness):
|
||||
debounce_input = driver.find_element(By.ID, "debounce_input_input")
|
||||
value_input = driver.find_element(By.ID, "value_input")
|
||||
on_change_input = driver.find_element(By.ID, "on_change_input")
|
||||
plain_value_input = driver.find_element(By.ID, "plain_value_input")
|
||||
clear_button = driver.find_element(By.TAG_NAME, "button")
|
||||
assert fully_controlled_input.poll_for_value(debounce_input) == "initial"
|
||||
assert fully_controlled_input.poll_for_value(value_input) == "initial"
|
||||
assert fully_controlled_input.poll_for_value(plain_value_input) == "initial"
|
||||
assert (
|
||||
plain_value_input.value_of_css_property("background-color")
|
||||
== "rgba(238, 238, 238, 1)"
|
||||
)
|
||||
|
||||
# move cursor to home, then to the right and type characters
|
||||
debounce_input.send_keys(Keys.HOME, Keys.ARROW_RIGHT)
|
||||
@ -89,6 +101,7 @@ async def test_fully_controlled_input(fully_controlled_input: AppHarness):
|
||||
"state"
|
||||
].text == "ifoonitial"
|
||||
assert fully_controlled_input.poll_for_value(value_input) == "ifoonitial"
|
||||
assert fully_controlled_input.poll_for_value(plain_value_input) == "ifoonitial"
|
||||
|
||||
# clear the input on the backend
|
||||
async with fully_controlled_input.modify_state(token) as state:
|
||||
@ -109,6 +122,10 @@ async def test_fully_controlled_input(fully_controlled_input: AppHarness):
|
||||
"state"
|
||||
].text == "getting testing done"
|
||||
assert fully_controlled_input.poll_for_value(value_input) == "getting testing done"
|
||||
assert (
|
||||
fully_controlled_input.poll_for_value(plain_value_input)
|
||||
== "getting testing done"
|
||||
)
|
||||
|
||||
# type into the on_change input
|
||||
on_change_input.send_keys("overwrite the state")
|
||||
@ -119,6 +136,10 @@ async def test_fully_controlled_input(fully_controlled_input: AppHarness):
|
||||
"state"
|
||||
].text == "overwrite the state"
|
||||
assert fully_controlled_input.poll_for_value(value_input) == "overwrite the state"
|
||||
assert (
|
||||
fully_controlled_input.poll_for_value(plain_value_input)
|
||||
== "overwrite the state"
|
||||
)
|
||||
|
||||
clear_button.click()
|
||||
time.sleep(0.5)
|
||||
|
@ -35,7 +35,7 @@ def VarOperations():
|
||||
@app.add_page
|
||||
def index():
|
||||
return rx.vstack(
|
||||
rx.input(
|
||||
rx.el.input(
|
||||
id="token",
|
||||
value=VarOperationState.router.session.client_token,
|
||||
is_read_only=True,
|
||||
|
@ -40,7 +40,7 @@ from reflex.event import (
|
||||
call_event_handler,
|
||||
get_handler_args,
|
||||
)
|
||||
from reflex.style import Style
|
||||
from reflex.style import Style, format_as_emotion
|
||||
from reflex.utils import console, format, imports, types
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.utils.serializers import serializer
|
||||
@ -624,7 +624,7 @@ class Component(BaseComponent, ABC):
|
||||
Returns:
|
||||
The dictionary of the component style as value and the style notation as key.
|
||||
"""
|
||||
return {"css": self.style}
|
||||
return {"css": Var.create(format_as_emotion(self.style))}
|
||||
|
||||
def render(self) -> Dict:
|
||||
"""Render the component.
|
||||
|
130
reflex/style.py
130
reflex/style.py
@ -35,6 +35,66 @@ toggle_color_mode = BaseVar(
|
||||
_var_data=color_mode_var_data,
|
||||
)
|
||||
|
||||
breakpoints = ["0", "30em", "48em", "62em", "80em", "96em"]
|
||||
|
||||
|
||||
def media_query(breakpoint_index: int):
|
||||
"""Create a media query selector.
|
||||
|
||||
Args:
|
||||
breakpoint_index: The index of the breakpoint to use.
|
||||
|
||||
Returns:
|
||||
The media query selector used as a key in emotion css dict.
|
||||
"""
|
||||
return f"@media screen and (min-width: {breakpoints[breakpoint_index]})"
|
||||
|
||||
|
||||
def convert_item(style_item: str | Var) -> tuple[str, VarData | None]:
|
||||
"""Format a single value in a style dictionary.
|
||||
|
||||
Args:
|
||||
style_item: The style item to format.
|
||||
|
||||
Returns:
|
||||
The formatted style item and any associated VarData.
|
||||
"""
|
||||
if isinstance(style_item, Var):
|
||||
# If the value is a Var, extract the var_data and cast as str.
|
||||
return str(style_item), style_item._var_data
|
||||
|
||||
# Otherwise, convert to Var to collapse VarData encoded in f-string.
|
||||
new_var = Var.create(style_item)
|
||||
if new_var is not None and new_var._var_data:
|
||||
# The wrapped backtick is used to identify the Var for interpolation.
|
||||
return f"`{str(new_var)}`", new_var._var_data
|
||||
|
||||
return style_item, None
|
||||
|
||||
|
||||
def convert_list(
|
||||
responsive_list: list[str | dict | Var],
|
||||
) -> tuple[list[str | dict], VarData | None]:
|
||||
"""Format a responsive value list.
|
||||
|
||||
Args:
|
||||
responsive_list: The raw responsive value list (one value per breakpoint).
|
||||
|
||||
Returns:
|
||||
The recursively converted responsive value list and any associated VarData.
|
||||
"""
|
||||
converted_value = []
|
||||
item_var_datas = []
|
||||
for responsive_item in responsive_list:
|
||||
if isinstance(responsive_item, dict):
|
||||
# Recursively format nested style dictionaries.
|
||||
item, item_var_data = convert(responsive_item)
|
||||
else:
|
||||
item, item_var_data = convert_item(responsive_item)
|
||||
converted_value.append(item)
|
||||
item_var_datas.append(item_var_data)
|
||||
return converted_value, VarData.merge(*item_var_datas)
|
||||
|
||||
|
||||
def convert(style_dict):
|
||||
"""Format a style dictionary.
|
||||
@ -49,20 +109,14 @@ def convert(style_dict):
|
||||
out = {}
|
||||
for key, value in style_dict.items():
|
||||
key = format.to_camel_case(key)
|
||||
new_var_data = None
|
||||
if isinstance(value, dict):
|
||||
# Recursively format nested style dictionaries.
|
||||
out[key], new_var_data = convert(value)
|
||||
elif isinstance(value, Var):
|
||||
# If the value is a Var, extract the var_data and cast as str.
|
||||
new_var_data = value._var_data
|
||||
out[key] = str(value)
|
||||
elif isinstance(value, list):
|
||||
# Responsive value is a list of dict or value
|
||||
out[key], new_var_data = convert_list(value)
|
||||
else:
|
||||
# Otherwise, convert to Var to collapse VarData encoded in f-string.
|
||||
new_var = Var.create(value)
|
||||
if new_var is not None:
|
||||
new_var_data = new_var._var_data
|
||||
out[key] = value
|
||||
out[key], new_var_data = convert_item(value)
|
||||
# Combine all the collected VarData instances.
|
||||
var_data = VarData.merge(var_data, new_var_data)
|
||||
return out, var_data
|
||||
@ -110,3 +164,59 @@ class Style(dict):
|
||||
# Carry the imports/hooks when setting a Var as a value.
|
||||
self._var_data = VarData.merge(self._var_data, _var._var_data)
|
||||
super().__setitem__(key, value)
|
||||
|
||||
|
||||
def _format_emotion_style_pseudo_selector(key: str) -> str:
|
||||
"""Format a pseudo selector for emotion CSS-in-JS.
|
||||
|
||||
Args:
|
||||
key: Underscore-prefixed or colon-prefixed pseudo selector key (_hover).
|
||||
|
||||
Returns:
|
||||
A self-referential pseudo selector key (&:hover).
|
||||
"""
|
||||
prefix = None
|
||||
if key.startswith("_"):
|
||||
# Handle pseudo selectors in chakra style format.
|
||||
prefix = "&:"
|
||||
key = key[1:]
|
||||
if key.startswith(":"):
|
||||
# Handle pseudo selectors and elements in native format.
|
||||
prefix = "&"
|
||||
if prefix is not None:
|
||||
return prefix + format.to_kebab_case(key)
|
||||
return key
|
||||
|
||||
|
||||
def format_as_emotion(style_dict: dict[str, Any]) -> dict[str, Any] | None:
|
||||
"""Convert the style to an emotion-compatible CSS-in-JS dict.
|
||||
|
||||
Args:
|
||||
style_dict: The style dict to convert.
|
||||
|
||||
Returns:
|
||||
The emotion dict.
|
||||
"""
|
||||
emotion_style = {}
|
||||
for orig_key, value in style_dict.items():
|
||||
key = _format_emotion_style_pseudo_selector(orig_key)
|
||||
if isinstance(value, list):
|
||||
# Apply media queries from responsive value list.
|
||||
mbps = {
|
||||
media_query(bp): bp_value
|
||||
if isinstance(bp_value, dict)
|
||||
else {key: bp_value}
|
||||
for bp, bp_value in enumerate(value)
|
||||
}
|
||||
if key.startswith("&:"):
|
||||
emotion_style[key] = mbps
|
||||
else:
|
||||
for mq, style_sub_dict in mbps.items():
|
||||
emotion_style.setdefault(mq, {}).update(style_sub_dict)
|
||||
elif isinstance(value, dict):
|
||||
# Recursively format nested style dictionaries.
|
||||
emotion_style[key] = format_as_emotion(value)
|
||||
else:
|
||||
emotion_style[key] = value
|
||||
if emotion_style:
|
||||
return emotion_style
|
||||
|
@ -625,7 +625,8 @@ def unwrap_vars(value: str) -> str:
|
||||
return prefix + re.sub('\\\\"', '"', m.group(2))
|
||||
|
||||
# This substitution is necessary to unwrap var values.
|
||||
return re.sub(
|
||||
return (
|
||||
re.sub(
|
||||
pattern=r"""
|
||||
(?<!\\) # must NOT start with a backslash
|
||||
" # match opening double quote of JSON value
|
||||
@ -637,6 +638,9 @@ def unwrap_vars(value: str) -> str:
|
||||
string=value,
|
||||
flags=re.VERBOSE,
|
||||
)
|
||||
.replace('"`', "`")
|
||||
.replace('`"', "`")
|
||||
)
|
||||
|
||||
|
||||
def collect_form_dict_names(form_dict: dict[str, Any]) -> dict[str, Any]:
|
||||
|
@ -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)
|
||||
|
@ -125,6 +125,8 @@ def test_indent(text: str, indent_level: int, expected: str, windows_platform: b
|
||||
("__start_with_double_underscore", "__start_with_double_underscore"),
|
||||
("kebab-case", "kebab_case"),
|
||||
("double-kebab-case", "double_kebab_case"),
|
||||
(":start-with-colon", ":start_with_colon"),
|
||||
(":-start-with-colon-dash", ":_start_with_colon_dash"),
|
||||
],
|
||||
)
|
||||
def test_to_snake_case(input: str, output: str):
|
||||
@ -153,6 +155,8 @@ def test_to_snake_case(input: str, output: str):
|
||||
("--starts-with-double-hyphen", "--startsWithDoubleHyphen"),
|
||||
("_starts_with_underscore", "_startsWithUnderscore"),
|
||||
("__starts_with_double_underscore", "__startsWithDoubleUnderscore"),
|
||||
(":start-with-colon", ":startWithColon"),
|
||||
(":-start-with-colon-dash", ":StartWithColonDash"),
|
||||
],
|
||||
)
|
||||
def test_to_camel_case(input: str, output: str):
|
||||
@ -193,6 +197,10 @@ def test_to_title_case(input: str, output: str):
|
||||
("Hello", "hello"),
|
||||
("snake_case", "snake-case"),
|
||||
("snake_case_two", "snake-case-two"),
|
||||
(":startWithColon", ":start-with-colon"),
|
||||
(":StartWithColonDash", ":-start-with-colon-dash"),
|
||||
(":start_with_colon", ":start-with-colon"),
|
||||
(":_start_with_colon_dash", ":-start-with-colon-dash"),
|
||||
],
|
||||
)
|
||||
def test_to_kebab_case(input: str, output: str):
|
||||
|
Loading…
Reference in New Issue
Block a user