diff --git a/src/__tests__/native/className-with-style.test.tsx b/src/__tests__/native/className-with-style.test.tsx new file mode 100644 index 0000000..5ac156d --- /dev/null +++ b/src/__tests__/native/className-with-style.test.tsx @@ -0,0 +1,48 @@ +import { render } from "@testing-library/react-native"; +import { Text } from "react-native-css/components/Text"; +import { registerCSS, testID } from "react-native-css/jest"; + +test("className with inline style props should coexist when different properties", () => { + registerCSS(`.text-red { color: red; }`); + + const component = render( + , + ).getByTestId(testID); + + // Both className and style props should be applied as array + expect(component.props.style).toEqual([ + { color: "#f00" }, // Changed from "red" to "#f00" + { fontSize: 16 }, + ]); +}); + +test("className with inline style props should favor inline when same property", () => { + registerCSS(`.text-red { color: red; }`); + + const component = render( + , + ).getByTestId(testID); + + // When same property exists, inline style should win (not array) + expect(component.props.style).toEqual({ color: "blue" }); +}); + +test("only className should not create array", () => { + registerCSS(`.text-red { color: red; }`); + + const component = render( + , + ).getByTestId(testID); + + // Only className should be a flat object + expect(component.props.style).toEqual({ color: "#f00" }); // Changed from "red" to "#f00" +}); + +test("only inline style should not create array", () => { + const component = render( + , + ).getByTestId(testID); + + // Only inline style should be a flat object + expect(component.props.style).toEqual({ color: "blue" }); +}); diff --git a/src/native/styles/index.ts b/src/native/styles/index.ts index 09ab073..0e2319a 100644 --- a/src/native/styles/index.ts +++ b/src/native/styles/index.ts @@ -135,7 +135,41 @@ function deepMergeConfig( return { ...left }; } - let result = config.target ? Object.assign({}, left, right) : { ...left }; + // Handle style merging to support both className and inline style props + let result: Record; + if (config.target) { + if ( + Array.isArray(config.target) && + config.target.length === 1 && + config.target[0] === "style" + ) { + // Special handling for style target when we have inline styles + result = { ...left, ...right }; + // More performant approach - check for non-overlapping properties without Sets + if (left?.style && right?.style && rightIsInline) { + const leftStyle = left.style; + const rightStyle = right.style; + + // Quick check: do any left properties NOT exist in right? + let hasNonOverlappingProperties = false; + for (const key in leftStyle) { + if (!(key in rightStyle)) { + hasNonOverlappingProperties = true; + break; // Early exit for performance + } + } + + if (hasNonOverlappingProperties) { + result.style = [leftStyle, rightStyle]; + } + // Otherwise, Object.assign above will handle the override correctly + } + } else { + result = Object.assign({}, left, right); + } + } else { + result = { ...left }; + } if ( right &&