From 6b4c0c8abfcfea941827649e6ea63ecfa6901291 Mon Sep 17 00:00:00 2001 From: Dan Stepanov Date: Wed, 8 Apr 2026 18:08:24 -0700 Subject: [PATCH] fix: handle boolean true in nativeStyleMapping When nativeStyleMapping uses `true` (e.g. `textAlign: true`), the value should be treated as the key name itself. Previously, `true.split(".")` would throw a TypeError at runtime, breaking TextInput and ImageBackground components that use this pattern. Converts boolean `true` values to the key name during config creation in useNativeCss so the runtime always receives strings. Fixes #232 --- src/__tests__/native/components.test.tsx | 62 +++++++++++++++++++++++- src/native/react/useNativeCss.ts | 11 +++-- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/__tests__/native/components.test.tsx b/src/__tests__/native/components.test.tsx index 31fcb84f..3a596038 100644 --- a/src/__tests__/native/components.test.tsx +++ b/src/__tests__/native/components.test.tsx @@ -1,7 +1,13 @@ -import { Button as RNButton, type ButtonProps } from "react-native"; +import { + Button as RNButton, + TextInput as RNTextInput, + type ButtonProps, + type TextInputProps, +} from "react-native"; import { render } from "@testing-library/react-native"; import { copyComponentProperties } from "react-native-css/components/copyComponentProperties"; +import { TextInput } from "react-native-css/components/TextInput"; import { registerCSS, testID } from "react-native-css/jest"; import { useCssElement } from "react-native-css/native"; import type { @@ -54,3 +60,57 @@ test("Component preserves props when mapping specifies 'target: false'", () => { expect(titleElement.props.style).toHaveLength(2); expect(titleElement.props.style[1]).toEqual({ color: "#ffa500" }); }); + +test("nativeStyleMapping with boolean true extracts style prop using key name", () => { + registerCSS(`.text-center { text-align: center; }`); + + const component = render( + , + ).getByTestId(testID); + + // textAlign should be extracted from style and mapped to the textAlign prop + expect(component.props.textAlign).toBe("center"); + expect(component.props.style).not.toHaveProperty("textAlign"); +}); + +test("nativeStyleMapping with boolean true works alongside other styles", () => { + registerCSS(` + .text-center { text-align: center; } + .text-red { color: red; } + `); + + const component = render( + , + ).getByTestId(testID); + + // textAlign extracted to prop, color stays in style + expect(component.props.textAlign).toBe("center"); + expect(component.props.style).toStrictEqual({ color: "#f00" }); +}); + +test("nativeStyleMapping with boolean true on custom component", () => { + registerCSS(`.text-right { text-align: right; }`); + + const mapping: StyledConfiguration = { + className: { + target: "style", + nativeStyleMapping: { + textAlign: true, + }, + }, + }; + + const StyledTextInput = copyComponentProperties( + RNTextInput, + (props: StyledProps) => { + return useCssElement(RNTextInput, props, mapping); + }, + ); + + const component = render( + , + ).getByTestId(testID); + + expect(component.props.textAlign).toBe("right"); + expect(component.props.style).not.toHaveProperty("textAlign"); +}); diff --git a/src/native/react/useNativeCss.ts b/src/native/react/useNativeCss.ts index 43868202..11d3ede8 100644 --- a/src/native/react/useNativeCss.ts +++ b/src/native/react/useNativeCss.ts @@ -181,9 +181,14 @@ export function mappingToConfig(mapping: StyledConfiguration) { } else if (typeof value === "string") { return { source: key, target: value.split(".") }; } else if (typeof value === "object") { - const nativeStyleMapping = value.nativeStyleMapping as - | Record - | undefined; + const nativeStyleMapping = value.nativeStyleMapping + ? Object.fromEntries( + Object.entries(value.nativeStyleMapping).map(([k, v]) => [ + k, + v === true ? k : v, + ]), + ) + : undefined; if (Array.isArray(value)) { return { source: key, target: value, nativeStyleMapping };