Skip to content

Commit 67b5d08

Browse files
authored
fix: multiple :is/:where and containers unmounting children (#105)
1 parent 4eb039c commit 67b5d08

File tree

4 files changed

+207
-17
lines changed

4 files changed

+207
-17
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { fireEvent, screen } from "@testing-library/react-native";
2+
import { View } from "react-native-css/components";
3+
4+
import { render } from "./_tailwind";
5+
6+
const grandparentID = "grandparentID";
7+
const parentID = "parent";
8+
const childID = "child";
9+
10+
test("Styling based on parent state (group-{modifier})", async () => {
11+
await render(
12+
<View testID={parentID} className="group">
13+
<View testID={childID} className="group-hover:text-white" />
14+
</View>,
15+
);
16+
17+
const parent = screen.getByTestId(parentID);
18+
const child = screen.getByTestId(childID);
19+
20+
expect(parent).toHaveStyle(undefined);
21+
expect(child).toHaveStyle(undefined);
22+
23+
fireEvent(parent, "hoverIn");
24+
25+
expect(child).toHaveStyle({ color: "#fff" });
26+
});
27+
28+
test("Differentiating nested groups", async () => {
29+
await render(
30+
<View testID={grandparentID} className="group/grandparent">
31+
<View testID={parentID} className="group/parent">
32+
<View className="group-hover/grandparent:text-white" />
33+
<View testID={childID} className="group-hover/parent:text-white" />
34+
</View>
35+
</View>,
36+
);
37+
38+
const grandparent = screen.getByTestId(grandparentID);
39+
const parent = screen.getByTestId(parentID);
40+
const child = screen.getByTestId(childID);
41+
42+
expect(grandparent).toHaveStyle(undefined);
43+
expect(parent).toHaveStyle(undefined);
44+
expect(child).toHaveStyle(undefined);
45+
46+
fireEvent(grandparent, "hoverIn");
47+
48+
expect(child).toHaveStyle(undefined);
49+
50+
fireEvent(parent, "hoverIn");
51+
52+
expect(child).toHaveStyle({ color: "#fff" });
53+
});
54+
55+
test("arbitrary groups - single className", async () => {
56+
const { rerender } = await render(
57+
<View testID={parentID} className="group">
58+
<View testID={childID} className="group-[.test]:text-white" />
59+
</View>,
60+
);
61+
62+
const parent = screen.getByTestId(parentID);
63+
const child = screen.getByTestId(childID);
64+
65+
expect(parent).toHaveStyle(undefined);
66+
expect(child).toHaveStyle(undefined);
67+
68+
rerender(
69+
<View testID={parentID} className="group test">
70+
<View testID={childID} className="group-[.test]:text-white" />
71+
</View>,
72+
);
73+
74+
expect(child).toHaveStyle({ color: "#fff" });
75+
});
76+
77+
test("arbitrary groups - multiple className", async () => {
78+
const { rerender } = await render(
79+
<View testID={parentID} className="group">
80+
<View testID={childID} className="group-[.test.test2]:text-white" />
81+
</View>,
82+
);
83+
84+
const parent = screen.getByTestId(parentID);
85+
const child = screen.getByTestId(childID);
86+
87+
expect(parent).toHaveStyle(undefined);
88+
expect(child).toHaveStyle(undefined);
89+
90+
rerender(
91+
<View testID={parentID} className="group test">
92+
<View testID={childID} className="group-[.test.test2]:text-white" />
93+
</View>,
94+
);
95+
96+
expect(parent).toHaveStyle(undefined);
97+
expect(child).toHaveStyle(undefined);
98+
99+
rerender(
100+
<View testID={parentID} className="group test test2">
101+
<View testID={childID} className="group-[.test.test2]:text-white" />
102+
</View>,
103+
);
104+
105+
expect(child).toHaveStyle({ color: "#fff" });
106+
});
107+
108+
test("arbitrary groups - props", async () => {
109+
const { rerender } = await render(
110+
<View testID={parentID} className="group" accessibilityLabel="test">
111+
<View
112+
testID={childID}
113+
className="group-[[accessibilityLabel=works]]:text-white"
114+
/>
115+
</View>,
116+
);
117+
118+
const parent = screen.getByTestId(parentID);
119+
const child = screen.getByTestId(childID);
120+
121+
expect(parent).toHaveStyle(undefined);
122+
expect(child).toHaveStyle(undefined);
123+
124+
rerender(
125+
<View testID={parentID} className="group" accessibilityLabel="works">
126+
<View
127+
testID={childID}
128+
className="group-[[accessibilityLabel=works]]:text-white"
129+
/>
130+
</View>,
131+
);
132+
133+
expect(child).toHaveStyle({ color: "#fff" });
134+
});

src/compiler/__tests__/selectors.test.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,69 @@ test(".my-class { &:is(:where(.my-parent, .my-second-parent):hover *) {} }", ()
8484
},
8585
]);
8686
});
87+
88+
test(".group-[.test]:text-white { &:is(:where(.group):is(.test) *) {}", () => {
89+
const result = getClassNameSelectors([
90+
[
91+
{
92+
type: "class",
93+
name: "group-[.test]:text-white",
94+
},
95+
{
96+
type: "nesting",
97+
},
98+
{
99+
type: "pseudo-class",
100+
kind: "is",
101+
selectors: [
102+
[
103+
{
104+
type: "pseudo-class",
105+
kind: "where",
106+
selectors: [
107+
[
108+
{
109+
type: "class",
110+
name: "group",
111+
},
112+
],
113+
],
114+
},
115+
{
116+
type: "pseudo-class",
117+
kind: "is",
118+
selectors: [
119+
[
120+
{
121+
type: "class",
122+
name: "test",
123+
},
124+
],
125+
],
126+
},
127+
{
128+
type: "combinator",
129+
value: "descendant",
130+
},
131+
{
132+
type: "universal",
133+
},
134+
],
135+
],
136+
},
137+
],
138+
]);
139+
140+
expect(result).toStrictEqual([
141+
{
142+
className: "group-[.test]:text-white",
143+
containerQuery: [
144+
{
145+
n: "g:group.test",
146+
},
147+
],
148+
specificity: [0, 3],
149+
type: "className",
150+
},
151+
]);
152+
});

src/compiler/selector-builder.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -437,11 +437,6 @@ function parseIsWhereComponents(
437437
}
438438
case "where":
439439
case "is": {
440-
// :is() and :where() need to be at the start of the selector,
441-
if (index !== 0) {
442-
return null;
443-
}
444-
445440
// Now get the selectors inside the `is` or `where` pseudo-class
446441
queries = component.selectors.flatMap((selector) => {
447442
return parseIsWhereComponents(type, selector, 0, queries) ?? [];

src/runtime/native/react/rules.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export function updateRules(
4242
const inlineVariables = new Set<InlineVariable>();
4343

4444
let animated = false;
45+
let pressable = false;
4546

4647
for (const config of state.configs) {
4748
const source = currentProps?.[config.source];
@@ -94,22 +95,19 @@ export function updateRules(
9495
}
9596

9697
for (let rule of styleRuleSet) {
97-
// Even if a rule does not match, make sure we register that it could set
98-
// a variable or container or be animated.
99-
if (rule.v) usesVariables = true;
100-
if (rule.c) containers ??= inheritedContainers;
101-
if (rule.a) animated = true;
102-
10398
usesVariables ||= Boolean(rule.dv);
10499

105100
// We do this even if the rule doesn't match so we can maintain a consistent render tree
106101
// We we need to inject React context
102+
if (rule.a) animated = true;
103+
107104
if (rule.v) {
108105
variables ??= inheritedVariables;
109106
}
110107

111108
if (rule.c) {
112109
containers ??= inheritedContainers;
110+
activeFamily(state.ruleEffectGetter);
113111
}
114112

115113
if (
@@ -171,7 +169,7 @@ export function updateRules(
171169

172170
if (process.env.NODE_ENV !== "production") {
173171
if (isRerender) {
174-
let pressable = activeFamily.has(state.ruleEffectGetter);
172+
const pressable = activeFamily.has(state.ruleEffectGetter);
175173

176174
if (Boolean(variables) !== Boolean(state.variables)) {
177175
console.log(
@@ -194,11 +192,7 @@ export function updateRules(
194192
}
195193
}
196194

197-
// We only track this in development
198-
let pressable =
199-
process.env.NODE_ENV === "production"
200-
? undefined
201-
: activeFamily.has(state.ruleEffectGetter);
195+
pressable = activeFamily.has(state.ruleEffectGetter);
202196

203197
if (!rules.size && !state.stylesObs && !inlineVariables.size) {
204198
return {
@@ -208,6 +202,7 @@ export function updateRules(
208202
animated,
209203
pressable,
210204
variables,
205+
containers,
211206
};
212207
}
213208

0 commit comments

Comments
 (0)