Skip to content

Commit bf2d907

Browse files
committed
Add snapshot tests for theme and print selectors
1 parent 91061c7 commit bf2d907

11 files changed

+214
-39
lines changed

.changeset/plenty-melons-look.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@devup-ui/wasm": patch
3+
---
4+
5+
Fix compound selector issue

libs/css/src/lib.rs

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,21 @@ impl Ord for StyleSelector {
6060

6161
impl From<&str> for StyleSelector {
6262
fn from(value: &str) -> Self {
63-
if let Some(s) = value.strip_prefix("group") {
64-
Dual("*[role=group]".to_string(), to_kebab_case(s))
63+
if value.contains(":") {
64+
let t: Vec<_> = value.split(":").collect();
65+
if let Prefix(v) = t[0].into() {
66+
Dual(v, t[1].to_string())
67+
} else {
68+
Postfix(t[1].to_string())
69+
}
70+
} else if let Some(s) = value.strip_prefix("group") {
71+
let post = to_kebab_case(s);
72+
Prefix(format!(
73+
"{}{}{}",
74+
"*[role=group]",
75+
get_selector_separator(&post),
76+
post
77+
))
6578
} else if let Some(s) = value.strip_prefix("theme") {
6679
// first character should lower case
6780
Prefix(format!(
@@ -95,15 +108,20 @@ impl Display for StyleSelector {
95108
pub fn merge_selector(class_name: &str, selector: Option<&StyleSelector>) -> String {
96109
if let Some(selector) = selector {
97110
match selector {
98-
Postfix(postfix) => match get_selector_separator(postfix) {
99-
SelectorSeparator::Single => format!(".{}:{}", class_name, postfix),
100-
SelectorSeparator::Double => format!(".{}::{}", class_name, postfix),
101-
},
111+
Postfix(postfix) => format!(
112+
".{}{}{}",
113+
class_name,
114+
get_selector_separator(postfix),
115+
postfix
116+
),
102117
Prefix(prefix) => format!("{} .{}", prefix, class_name),
103-
Dual(prefix, postfix) => match get_selector_separator(postfix) {
104-
SelectorSeparator::Single => format!("{}:{} .{}", prefix, postfix, class_name),
105-
SelectorSeparator::Double => format!("{}::{} .{}", prefix, postfix, class_name),
106-
},
118+
Dual(prefix, postfix) => format!(
119+
"{} .{}{}{}",
120+
prefix,
121+
class_name,
122+
get_selector_separator(postfix),
123+
postfix
124+
),
107125
Media(_) => format!(".{}", class_name),
108126
}
109127
} else {
@@ -115,6 +133,18 @@ pub enum SelectorSeparator {
115133
Single,
116134
Double,
117135
}
136+
impl Display for SelectorSeparator {
137+
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
138+
write!(
139+
f,
140+
"{}",
141+
match self {
142+
SelectorSeparator::Single => ":",
143+
SelectorSeparator::Double => "::",
144+
}
145+
)
146+
}
147+
}
118148

119149
static DOUBLE_SEPARATOR: Lazy<HashSet<&str>> = Lazy::new(|| {
120150
let mut set = HashSet::new();
@@ -627,15 +657,23 @@ mod tests {
627657
);
628658
assert_eq!(
629659
StyleSelector::from("groupHover"),
630-
Dual("*[role=group]".to_string(), "hover".to_string())
660+
Prefix("*[role=group]:hover".to_string())
631661
);
632662
assert_eq!(
633663
StyleSelector::from("groupFocusVisible"),
634-
Dual("*[role=group]".to_string(), "focus-visible".to_string())
664+
Prefix("*[role=group]:focus-visible".to_string())
635665
);
636666
assert_eq!(
637667
StyleSelector::from("group1"),
638-
Dual("*[role=group]".to_string(), "1".to_string())
668+
Prefix("*[role=group]:1".to_string())
669+
);
670+
671+
assert_eq!(
672+
StyleSelector::from("themeDark:placeholder"),
673+
Dual(
674+
":root[data-theme=dark]".to_string(),
675+
"placeholder".to_string()
676+
)
639677
);
640678

641679
assert_eq!(Prefix(".cls".to_string()).to_string(), "-.cls-");
@@ -661,22 +699,21 @@ mod tests {
661699
assert_eq!(
662700
merge_selector(
663701
"cls",
664-
Some(&Dual(
665-
":root[data-theme=dark]".to_string(),
666-
"hover".to_string()
667-
)),
702+
Some(&Prefix(":root[data-theme=dark]:hover".to_string(),)),
668703
),
669704
":root[data-theme=dark]:hover .cls"
670705
);
671706
assert_eq!(
672707
merge_selector(
673708
"cls",
674-
Some(&Dual(
675-
":root[data-theme=dark]".to_string(),
676-
"placeholder".to_string()
677-
)),
709+
Some(&Prefix(":root[data-theme=dark]::placeholder".to_string())),
678710
),
679711
":root[data-theme=dark]::placeholder .cls"
680712
);
713+
714+
assert_eq!(
715+
merge_selector("cls", Some(&"themeDark:hover".into()),),
716+
":root[data-theme=dark] .cls:hover"
717+
);
681718
}
682719
}

libs/extractor/src/lib.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,4 +2088,33 @@ import {Button} from '@devup/ui'
20882088
)
20892089
.unwrap());
20902090
}
2091+
2092+
#[test]
2093+
#[serial]
2094+
fn theme_selector() {
2095+
reset_class_map();
2096+
assert_debug_snapshot!(extract(
2097+
"test.js",
2098+
r#"import {Box} from '@devup-ui/core'
2099+
<Box _themeDark={{ _hover:{bg:"black"} }} />
2100+
"#,
2101+
ExtractOption {
2102+
package: "@devup-ui/core".to_string(),
2103+
css_file: None
2104+
}
2105+
)
2106+
.unwrap());
2107+
reset_class_map();
2108+
assert_debug_snapshot!(extract(
2109+
"test.js",
2110+
r#"import {Box} from '@devup-ui/core'
2111+
<Box _hover={{bg:"white"}} _themeDark={{ _hover:{bg:"black"} }} />
2112+
"#,
2113+
ExtractOption {
2114+
package: "@devup-ui/core".to_string(),
2115+
css_file: None
2116+
}
2117+
)
2118+
.unwrap());
2119+
}
20912120
}

libs/extractor/src/snapshots/extractor__tests__group_selector_props.snap

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ ExtractOutput {
1010
value: "red",
1111
level: 0,
1212
selector: Some(
13-
Dual(
14-
"*[role=group]",
15-
"hover",
13+
Prefix(
14+
"*[role=group]:hover",
1615
),
1716
),
1817
basic: false,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
source: libs/extractor/src/lib.rs
3+
expression: "extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\n <Box _hover={{bg:\"white\"}} _themeDark={{ _hover:{bg:\"black\"} }} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()"
4+
---
5+
ExtractOutput {
6+
styles: [
7+
Static(
8+
ExtractStaticStyle {
9+
property: "background",
10+
value: "white",
11+
level: 0,
12+
selector: Some(
13+
Postfix(
14+
"hover",
15+
),
16+
),
17+
basic: false,
18+
},
19+
),
20+
Static(
21+
ExtractStaticStyle {
22+
property: "background",
23+
value: "black",
24+
level: 0,
25+
selector: Some(
26+
Dual(
27+
":root[data-theme=dark]",
28+
"hover",
29+
),
30+
),
31+
basic: false,
32+
},
33+
),
34+
],
35+
code: "import \"@devup-ui/core/devup-ui.css\";\n<div className=\"d0 d1\" />;\n",
36+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
source: libs/extractor/src/lib.rs
3+
expression: "extract(\"test.js\",\nr#\"import {Box} from '@devup-ui/core'\n <Box _themeDark={{ _hover:{bg:\"black\"} }} />\n \"#,\nExtractOption\n{ package: \"@devup-ui/core\".to_string(), css_file: None }).unwrap()"
4+
---
5+
ExtractOutput {
6+
styles: [
7+
Static(
8+
ExtractStaticStyle {
9+
property: "background",
10+
value: "black",
11+
level: 0,
12+
selector: Some(
13+
Dual(
14+
":root[data-theme=dark]",
15+
"hover",
16+
),
17+
),
18+
basic: false,
19+
},
20+
),
21+
],
22+
code: "import \"@devup-ui/core/devup-ui.css\";\n<div className=\"d0\" />;\n",
23+
}

libs/extractor/src/style_extractor.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ pub fn extract_style_from_expression<'a>(
7474
level: u8,
7575
selector: Option<&str>,
7676
) -> ExtractResult<'a> {
77+
println!(
78+
"extract_style_from_expression: {:?} {:?} {:?}",
79+
selector, name, expression
80+
);
7781
let mut typo = false;
7882

7983
if name.is_none() && selector.is_none() {
@@ -240,13 +244,20 @@ pub fn extract_style_from_expression<'a>(
240244
// };
241245
}
242246

243-
if let Some(selector) = name.strip_prefix("_") {
247+
if let Some(new_selector) = name.strip_prefix("_") {
244248
return extract_style_from_expression(
245249
ast_builder,
246250
None,
247251
expression,
248252
level,
249-
Some(selector),
253+
Some(
254+
if let Some(selector) = selector {
255+
format!("{}:{}", selector, new_selector)
256+
} else {
257+
new_selector.to_string()
258+
}
259+
.as_str(),
260+
),
250261
);
251262
}
252263
typo = name == "typography";

libs/sheet/src/lib.rs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -188,36 +188,35 @@ impl StyleSheet {
188188
.unwrap_or_else(|| self.theme.break_points.last().cloned().unwrap_or(0)),
189189
)
190190
};
191-
for (media, props) in medias {
192-
let inner_css = props
191+
if !sorted_props.is_empty() {
192+
let inner_css = sorted_props
193193
.into_iter()
194194
.map(ExtractStyle::extract)
195195
.collect::<Vec<String>>()
196196
.join("");
197197
css.push_str(
198198
if let Some(break_point) = break_point {
199-
format!(
200-
"\n@media (min-width:{}px) and {}{{{}}}",
201-
break_point, media, inner_css
202-
)
199+
format!("\n@media (min-width:{}px){{{}}}", break_point, inner_css)
203200
} else {
204-
format!("\n@media {}{{{}}}", media, inner_css.as_str())
201+
inner_css
205202
}
206203
.as_str(),
207204
);
208205
}
209-
210-
if !sorted_props.is_empty() {
211-
let inner_css = sorted_props
206+
for (media, props) in medias {
207+
let inner_css = props
212208
.into_iter()
213209
.map(ExtractStyle::extract)
214210
.collect::<Vec<String>>()
215211
.join("");
216212
css.push_str(
217213
if let Some(break_point) = break_point {
218-
format!("\n@media (min-width:{}px){{{}}}", break_point, inner_css)
214+
format!(
215+
"\n@media (min-width:{}px) and {}{{{}}}",
216+
break_point, media, inner_css
217+
)
219218
} else {
220-
inner_css
219+
format!("\n@media {}{{{}}}", media, inner_css.as_str())
221220
}
222221
.as_str(),
223222
);
@@ -400,7 +399,7 @@ mod tests {
400399
"bg",
401400
0,
402401
"red",
403-
Some(&StyleSelector::Dual("*".to_string(), "hover".to_string())),
402+
Some(&StyleSelector::Prefix("*:hover".to_string())),
404403
false,
405404
);
406405
sheet.add_property(
@@ -412,6 +411,21 @@ mod tests {
412411
false,
413412
);
414413
assert_debug_snapshot!(sheet.create_css());
414+
415+
let mut sheet = StyleSheet::default();
416+
sheet.add_property(
417+
"test",
418+
"bg",
419+
0,
420+
"red",
421+
Some(&"themeDark:hover".into()),
422+
false,
423+
);
424+
assert_debug_snapshot!(sheet.create_css());
425+
426+
let mut sheet = StyleSheet::default();
427+
sheet.add_property("test", "bg", 0, "red", Some(&"wrong:hover".into()), false);
428+
assert_debug_snapshot!(sheet.create_css());
415429
}
416430

417431
#[test]
@@ -454,6 +468,12 @@ mod tests {
454468
sheet.add_property("test", "mx", 1, "40px", Some(&"print".into()), false);
455469
sheet.add_property("test", "my", 1, "40px", Some(&"print".into()), false);
456470
assert_debug_snapshot!(sheet.create_css());
471+
472+
let mut sheet = StyleSheet::default();
473+
sheet.add_property("test", "mx", 0, "40px", Some(&"print".into()), false);
474+
sheet.add_property("test", "my", 0, "40px", None, false);
475+
476+
assert_debug_snapshot!(sheet.create_css());
457477
}
458478

459479
#[test]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: libs/sheet/src/lib.rs
3+
expression: sheet.create_css()
4+
---
5+
":root[data-theme=dark] .test:hover{background:red}"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: libs/sheet/src/lib.rs
3+
expression: sheet.create_css()
4+
---
5+
".test:hover{background:red}"

0 commit comments

Comments
 (0)