test_style.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. from __future__ import annotations
  2. from typing import Any
  3. import pytest
  4. import reflex as rx
  5. from reflex import style
  6. from reflex.vars import Var
  7. test_style = [
  8. ({"a": 1}, {"a": 1}),
  9. ({"a": Var.create("abc")}, {"a": "abc"}),
  10. ({"test_case": 1}, {"testCase": 1}),
  11. ({"test_case": {"a": 1}}, {"testCase": {"a": 1}}),
  12. ({":test_case": {"a": 1}}, {":testCase": {"a": 1}}),
  13. ({"::test_case": {"a": 1}}, {"::testCase": {"a": 1}}),
  14. (
  15. {"::-webkit-scrollbar": {"display": "none"}},
  16. {"::-webkit-scrollbar": {"display": "none"}},
  17. ),
  18. ]
  19. @pytest.mark.parametrize(
  20. "style_dict,expected",
  21. test_style,
  22. )
  23. def test_convert(style_dict, expected):
  24. """Test Format a style dictionary.
  25. Args:
  26. style_dict: The style to check.
  27. expected: The expected formatted style.
  28. """
  29. converted_dict, _var_data = style.convert(style_dict)
  30. assert converted_dict == expected
  31. @pytest.mark.parametrize(
  32. "style_dict,expected",
  33. test_style,
  34. )
  35. def test_create_style(style_dict, expected):
  36. """Test style dictionary.
  37. Args:
  38. style_dict: The style to check.
  39. expected: The expected formatted style.
  40. """
  41. assert style.Style(style_dict) == expected
  42. def compare_dict_of_var(d1: dict[str, Any], d2: dict[str, Any]):
  43. """Compare two dictionaries of Var objects.
  44. Args:
  45. d1: The first dictionary.
  46. d2: The second dictionary.
  47. """
  48. assert len(d1) == len(d2)
  49. for key, value in d1.items():
  50. assert key in d2
  51. if isinstance(value, dict):
  52. compare_dict_of_var(value, d2[key])
  53. elif isinstance(value, Var):
  54. assert value.equals(d2[key])
  55. else:
  56. assert value == d2[key]
  57. @pytest.mark.parametrize(
  58. ("kwargs", "style_dict", "expected_get_style"),
  59. [
  60. ({}, {}, {"css": None}),
  61. ({"color": "hotpink"}, {}, {"css": Var.create({"color": "hotpink"})}),
  62. ({}, {"color": "red"}, {"css": Var.create({"color": "red"})}),
  63. (
  64. {"color": "hotpink"},
  65. {"color": "red"},
  66. {"css": Var.create({"color": "hotpink"})},
  67. ),
  68. (
  69. {"_hover": {"color": "hotpink"}},
  70. {},
  71. {"css": Var.create({"&:hover": {"color": "hotpink"}})},
  72. ),
  73. (
  74. {},
  75. {"_hover": {"color": "red"}},
  76. {"css": Var.create({"&:hover": {"color": "red"}})},
  77. ),
  78. (
  79. {},
  80. {":hover": {"color": "red"}},
  81. {"css": Var.create({"&:hover": {"color": "red"}})},
  82. ),
  83. (
  84. {},
  85. {"::-webkit-scrollbar": {"display": "none"}},
  86. {"css": Var.create({"&::-webkit-scrollbar": {"display": "none"}})},
  87. ),
  88. (
  89. {},
  90. {"::-moz-progress-bar": {"background_color": "red"}},
  91. {"css": Var.create({"&::-moz-progress-bar": {"backgroundColor": "red"}})},
  92. ),
  93. (
  94. {"color": ["#111", "#222", "#333", "#444", "#555"]},
  95. {},
  96. {
  97. "css": Var.create(
  98. {
  99. "@media screen and (min-width: 0)": {"color": "#111"},
  100. "@media screen and (min-width: 30em)": {"color": "#222"},
  101. "@media screen and (min-width: 48em)": {"color": "#333"},
  102. "@media screen and (min-width: 62em)": {"color": "#444"},
  103. "@media screen and (min-width: 80em)": {"color": "#555"},
  104. }
  105. )
  106. },
  107. ),
  108. (
  109. {
  110. "color": ["#111", "#222", "#333", "#444", "#555"],
  111. "background_color": "#FFF",
  112. },
  113. {},
  114. {
  115. "css": Var.create(
  116. {
  117. "@media screen and (min-width: 0)": {"color": "#111"},
  118. "@media screen and (min-width: 30em)": {"color": "#222"},
  119. "@media screen and (min-width: 48em)": {"color": "#333"},
  120. "@media screen and (min-width: 62em)": {"color": "#444"},
  121. "@media screen and (min-width: 80em)": {"color": "#555"},
  122. "backgroundColor": "#FFF",
  123. }
  124. )
  125. },
  126. ),
  127. (
  128. {
  129. "color": ["#111", "#222", "#333", "#444", "#555"],
  130. "background_color": ["#FFF", "#EEE", "#DDD", "#CCC", "#BBB"],
  131. },
  132. {},
  133. {
  134. "css": Var.create(
  135. {
  136. "@media screen and (min-width: 0)": {
  137. "color": "#111",
  138. "backgroundColor": "#FFF",
  139. },
  140. "@media screen and (min-width: 30em)": {
  141. "color": "#222",
  142. "backgroundColor": "#EEE",
  143. },
  144. "@media screen and (min-width: 48em)": {
  145. "color": "#333",
  146. "backgroundColor": "#DDD",
  147. },
  148. "@media screen and (min-width: 62em)": {
  149. "color": "#444",
  150. "backgroundColor": "#CCC",
  151. },
  152. "@media screen and (min-width: 80em)": {
  153. "color": "#555",
  154. "backgroundColor": "#BBB",
  155. },
  156. }
  157. )
  158. },
  159. ),
  160. (
  161. {
  162. "_hover": [
  163. {"color": "#111"},
  164. {"color": "#222"},
  165. {"color": "#333"},
  166. {"color": "#444"},
  167. {"color": "#555"},
  168. ]
  169. },
  170. {},
  171. {
  172. "css": Var.create(
  173. {
  174. "&:hover": {
  175. "@media screen and (min-width: 0)": {"color": "#111"},
  176. "@media screen and (min-width: 30em)": {"color": "#222"},
  177. "@media screen and (min-width: 48em)": {"color": "#333"},
  178. "@media screen and (min-width: 62em)": {"color": "#444"},
  179. "@media screen and (min-width: 80em)": {"color": "#555"},
  180. }
  181. }
  182. )
  183. },
  184. ),
  185. (
  186. {"_hover": {"color": ["#111", "#222", "#333", "#444", "#555"]}},
  187. {},
  188. {
  189. "css": Var.create(
  190. {
  191. "&:hover": {
  192. "@media screen and (min-width: 0)": {"color": "#111"},
  193. "@media screen and (min-width: 30em)": {"color": "#222"},
  194. "@media screen and (min-width: 48em)": {"color": "#333"},
  195. "@media screen and (min-width: 62em)": {"color": "#444"},
  196. "@media screen and (min-width: 80em)": {"color": "#555"},
  197. }
  198. }
  199. )
  200. },
  201. ),
  202. (
  203. {
  204. "_hover": {
  205. "color": ["#111", "#222", "#333", "#444", "#555"],
  206. "background_color": ["#FFF", "#EEE", "#DDD", "#CCC", "#BBB"],
  207. }
  208. },
  209. {},
  210. {
  211. "css": Var.create(
  212. {
  213. "&:hover": {
  214. "@media screen and (min-width: 0)": {
  215. "color": "#111",
  216. "backgroundColor": "#FFF",
  217. },
  218. "@media screen and (min-width: 30em)": {
  219. "color": "#222",
  220. "backgroundColor": "#EEE",
  221. },
  222. "@media screen and (min-width: 48em)": {
  223. "color": "#333",
  224. "backgroundColor": "#DDD",
  225. },
  226. "@media screen and (min-width: 62em)": {
  227. "color": "#444",
  228. "backgroundColor": "#CCC",
  229. },
  230. "@media screen and (min-width: 80em)": {
  231. "color": "#555",
  232. "backgroundColor": "#BBB",
  233. },
  234. }
  235. }
  236. )
  237. },
  238. ),
  239. (
  240. {
  241. "_hover": {
  242. "color": ["#111", "#222", "#333", "#444", "#555"],
  243. "background_color": "#FFF",
  244. }
  245. },
  246. {},
  247. {
  248. "css": Var.create(
  249. {
  250. "&:hover": {
  251. "@media screen and (min-width: 0)": {"color": "#111"},
  252. "@media screen and (min-width: 30em)": {"color": "#222"},
  253. "@media screen and (min-width: 48em)": {"color": "#333"},
  254. "@media screen and (min-width: 62em)": {"color": "#444"},
  255. "@media screen and (min-width: 80em)": {"color": "#555"},
  256. "backgroundColor": "#FFF",
  257. }
  258. }
  259. )
  260. },
  261. ),
  262. ],
  263. )
  264. def test_style_via_component(
  265. kwargs: dict[str, Any],
  266. style_dict: dict[str, Any],
  267. expected_get_style: dict[str, Any],
  268. ):
  269. """Pass kwargs and style_dict to a component and assert the final, combined style dict.
  270. Args:
  271. kwargs: The kwargs to pass to the component.
  272. style_dict: The style_dict to pass to the component.
  273. expected_get_style: The expected style dict.
  274. """
  275. comp = rx.el.div(style=style_dict, **kwargs) # type: ignore
  276. compare_dict_of_var(comp._get_style(), expected_get_style)
  277. class StyleState(rx.State):
  278. """Style vars in a substate."""
  279. color: str = "hotpink"
  280. color2: str = "red"
  281. @pytest.mark.parametrize(
  282. ("kwargs", "expected_get_style"),
  283. [
  284. (
  285. {"color": StyleState.color},
  286. {"css": Var.create({"color": StyleState.color})},
  287. ),
  288. (
  289. {"color": f"dark{StyleState.color}"},
  290. {"css": Var.create_safe(f'{{"color": `dark{StyleState.color}`}}').to(dict)},
  291. ),
  292. (
  293. {"color": StyleState.color, "_hover": {"color": StyleState.color2}},
  294. {
  295. "css": Var.create(
  296. {
  297. "color": StyleState.color,
  298. "&:hover": {"color": StyleState.color2},
  299. }
  300. )
  301. },
  302. ),
  303. (
  304. {"color": [StyleState.color, "gray", StyleState.color2, "yellow", "blue"]},
  305. {
  306. "css": Var.create(
  307. {
  308. "@media screen and (min-width: 0)": {"color": StyleState.color},
  309. "@media screen and (min-width: 30em)": {"color": "gray"},
  310. "@media screen and (min-width: 48em)": {
  311. "color": StyleState.color2
  312. },
  313. "@media screen and (min-width: 62em)": {"color": "yellow"},
  314. "@media screen and (min-width: 80em)": {"color": "blue"},
  315. }
  316. )
  317. },
  318. ),
  319. (
  320. {
  321. "_hover": [
  322. {"color": StyleState.color},
  323. {"color": StyleState.color2},
  324. {"color": "#333"},
  325. {"color": "#444"},
  326. {"color": "#555"},
  327. ]
  328. },
  329. {
  330. "css": Var.create(
  331. {
  332. "&:hover": {
  333. "@media screen and (min-width: 0)": {
  334. "color": StyleState.color
  335. },
  336. "@media screen and (min-width: 30em)": {
  337. "color": StyleState.color2
  338. },
  339. "@media screen and (min-width: 48em)": {"color": "#333"},
  340. "@media screen and (min-width: 62em)": {"color": "#444"},
  341. "@media screen and (min-width: 80em)": {"color": "#555"},
  342. }
  343. }
  344. )
  345. },
  346. ),
  347. (
  348. {
  349. "_hover": {
  350. "color": [
  351. StyleState.color,
  352. StyleState.color2,
  353. "#333",
  354. "#444",
  355. "#555",
  356. ]
  357. }
  358. },
  359. {
  360. "css": Var.create(
  361. {
  362. "&:hover": {
  363. "@media screen and (min-width: 0)": {
  364. "color": StyleState.color
  365. },
  366. "@media screen and (min-width: 30em)": {
  367. "color": StyleState.color2
  368. },
  369. "@media screen and (min-width: 48em)": {"color": "#333"},
  370. "@media screen and (min-width: 62em)": {"color": "#444"},
  371. "@media screen and (min-width: 80em)": {"color": "#555"},
  372. }
  373. }
  374. )
  375. },
  376. ),
  377. ],
  378. )
  379. def test_style_via_component_with_state(
  380. kwargs: dict[str, Any],
  381. expected_get_style: dict[str, Any],
  382. ):
  383. """Pass kwargs to a component with state vars and assert the final, combined style dict.
  384. Args:
  385. kwargs: The kwargs to pass to the component.
  386. expected_get_style: The expected style dict.
  387. """
  388. comp = rx.el.div(**kwargs)
  389. assert comp.style._var_data == expected_get_style["css"]._var_data
  390. # Remove the _var_data from the expected style, since the emotion-formatted
  391. # style dict won't actually have it.
  392. expected_get_style["css"]._var_data = None
  393. # Assert that style values are equal.
  394. compare_dict_of_var(comp._get_style(), expected_get_style)