diff --git a/src/runtime/native/__tests__/container-queries.test.tsx b/src/runtime/native/__tests__/container-queries.test.tsx index e0e6688..2dd262d 100644 --- a/src/runtime/native/__tests__/container-queries.test.tsx +++ b/src/runtime/native/__tests__/container-queries.test.tsx @@ -5,6 +5,45 @@ import { registerCSS } from "react-native-css/jest"; const parentID = "parent"; const childID = "child"; +test("Unnamed containers", () => { + registerCSS(` + :root, :host { + --color-white: #fff; + } + .\\@container { + container-type: inline-size; + } + .\\@sm\\:text-white { + @container (width >= 24rem) { + color: var(--color-white); + } + } + `); + + render( + + + , + ); + + const parent = screen.getByTestId(parentID); + const child = screen.getByTestId(childID); + + expect(child).toHaveStyle(undefined); + + // Jest does not fire layout events, so we need to manually + fireEvent(parent, "layout", { + nativeEvent: { + layout: { + width: 500, + height: 200, + }, + }, + }); + + expect(child).toHaveStyle({ color: "#fff" }); +}); + test("container query width", () => { registerCSS(` .container { diff --git a/src/runtime/native/__tests__/grouping.test.tsx b/src/runtime/native/__tests__/grouping.test.tsx index 3c9f844..68f1056 100644 --- a/src/runtime/native/__tests__/grouping.test.tsx +++ b/src/runtime/native/__tests__/grouping.test.tsx @@ -10,24 +10,24 @@ const childID = "child"; jest.useFakeTimers(); test("groups", () => { - registerCSS( - `.group\\/item .my-class { + registerCSS(` + .group\\/item .my-class { color: red; - }`, - ); + } + `); - const { rerender, getByTestId } = render( - + render( + , ); - const component = getByTestId(childID); + const component = screen.getByTestId(childID); expect(component.props.style).toStrictEqual({ color: "#f00" }); - rerender( - + screen.rerender( + , ); diff --git a/src/runtime/native/__tests__/upgrading.test.tsx b/src/runtime/native/__tests__/upgrading.test.tsx index d5ef196..4793d2c 100644 --- a/src/runtime/native/__tests__/upgrading.test.tsx +++ b/src/runtime/native/__tests__/upgrading.test.tsx @@ -41,12 +41,12 @@ test("adding a group", () => { expect(log.mock.calls).toEqual([ [ - "ReactNativeCss: className 'group' added a container after the initial render. This causes the components state to be reset and all children be re-mounted. This will cause unexpected behavior. Use the className 'will-change-container' to avoid this warning. If this was caused by sibling components being added/removed, use a 'key' prop so React can track the component correctly.", + "ReactNativeCss: className 'group' added or removed a container after the initial render. This causes the components state to be reset and all children be re-mounted. This will cause unexpected behavior. Use the className 'will-change-container' to avoid this warning. If this was caused by sibling components being added/removed, use a 'key' prop so React can track the component correctly.", ], ]); }); -test.only("will-change-container", () => { +test("will-change-container", () => { registerCSS( `.group .my-class { color: red; @@ -69,9 +69,6 @@ test.only("will-change-container", () => { , ); - expect(log.mock.calls).toEqual([ - [ - "ReactNativeCss: className 'group' added or removed a container after the initial render. This causes the components state to be reset and all children be re-mounted. This will cause unexpected behavior. Use the className 'will-change-container' to avoid this warning. If this was caused by sibling components being added/removed, use a 'key' prop so React can track the component correctly.", - ], - ]); + // There shouldn't be any error, as we continued to have a container + expect(log.mock.calls).toEqual([]); }); diff --git a/src/runtime/native/injection.ts b/src/runtime/native/injection.ts index 24a3f03..3850aad 100644 --- a/src/runtime/native/injection.ts +++ b/src/runtime/native/injection.ts @@ -3,6 +3,7 @@ import type { ReactNativeCssStyleSheet, StyleRuleSet, } from "../../compiler"; +import { DEFAULT_CONTAINER_NAME } from "./conditions/container-query"; import { family, observable, @@ -27,6 +28,36 @@ StyleCollection.keyframes = family>(() => { StyleCollection.inject = function (options: ReactNativeCssStyleSheet) { observableBatch.current = new Set(); + StyleCollection.styles("will-change-variable").set([ + { + s: [0], + v: [], + }, + ]); + + StyleCollection.styles("will-change-container").set([ + { + s: [0], + c: [DEFAULT_CONTAINER_NAME], + }, + ]); + + StyleCollection.styles("will-change-animation").set([ + { + s: [0], + a: true, + }, + ]); + + StyleCollection.styles("will-change-pressable").set([ + { + s: [0], + p: { + h: 1, + }, + }, + ]); + if (options.s) { for (const style of options.s) { StyleCollection.styles(style[0]).set(style[1]); @@ -99,33 +130,3 @@ function isDeepEqual(a: unknown, b: unknown): boolean { return true; } - -StyleCollection.styles("will-change-variable").set([ - { - s: [0], - v: [], - }, -]); - -StyleCollection.styles("will-change-container").set([ - { - s: [0], - c: [], - }, -]); - -StyleCollection.styles("will-change-animation").set([ - { - s: [0], - a: true, - }, -]); - -StyleCollection.styles("will-change-pressable").set([ - { - s: [0], - p: { - h: 1, - }, - }, -]); diff --git a/src/runtime/native/react/rules.ts b/src/runtime/native/react/rules.ts index cbe33fb..0d5e53a 100644 --- a/src/runtime/native/react/rules.ts +++ b/src/runtime/native/react/rules.ts @@ -12,6 +12,7 @@ import { hoverFamily, VAR_SYMBOL, weakFamily, + type ContainerContextValue, type VariableContextValue, } from "../reactivity"; import { stylesFamily } from "../styles"; @@ -36,8 +37,8 @@ export function updateRules( let usesVariables = false; - let variables = state.variables ? inheritedVariables : undefined; - let containers = state.containers ? inheritedContainers : undefined; + let variables: VariableContextValue | undefined; + let containers: ContainerContextValue | undefined; const inlineVariables = new Set(); let animated = false; @@ -101,6 +102,16 @@ export function updateRules( usesVariables ||= Boolean(rule.dv); + // We do this even if the rule doesn't match so we can maintain a consistent render tree + // We we need to inject React context + if (rule.v) { + variables ??= inheritedVariables; + } + + if (rule.c) { + containers ??= inheritedContainers; + } + if ( !testRule( rule, @@ -114,11 +125,8 @@ export function updateRules( } if (rule.v) { - // We're going to set a value, so we need to create a new object if (variables === inheritedVariables) { variables = { ...inheritedVariables }; - } else { - variables ??= { ...inheritedVariables }; } for (const v of rule.v) { @@ -199,6 +207,7 @@ export function updateRules( guards, animated, pressable, + variables, }; }