From 8c99889f355fbc064d84de4880a5e491a839240a Mon Sep 17 00:00:00 2001 From: telanflow Date: Tue, 9 Jun 2026 16:35:58 +0800 Subject: [PATCH] fix: handle boolean shorthand in nativeStyleMapping A nativeStyleMapping entry value may be `true` (the same-name shorthand permitted by the NativeStyleMapping type: `true | DotNotation`), but the runtime called path.split(".") unconditionally, throwing "true.split is not a function" once the mapped style was present. This crashed shipped components that use the shorthand - TextInput (textAlign) and ImageBackground (backgroundColor) - as soon as a class like text-right or bg-* was applied; the style was only skipped when absent (the styleValue === undefined guard), making it look intermittent. Map `true` onto a prop with the same name, and add a regression test. Closes #348 --- src/__tests__/native/components.test.tsx | 34 +++++++++++++++++++++++- src/native/styles/index.ts | 2 +- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/__tests__/native/components.test.tsx b/src/__tests__/native/components.test.tsx index 31fcb84f..f6bf8d18 100644 --- a/src/__tests__/native/components.test.tsx +++ b/src/__tests__/native/components.test.tsx @@ -1,4 +1,9 @@ -import { Button as RNButton, type ButtonProps } from "react-native"; +import { + Button as RNButton, + View as RNView, + type ButtonProps, + type ViewProps, +} from "react-native"; import { render } from "@testing-library/react-native"; import { copyComponentProperties } from "react-native-css/components/copyComponentProperties"; @@ -54,3 +59,30 @@ test("Component preserves props when mapping specifies 'target: false'", () => { expect(titleElement.props.style).toHaveLength(2); expect(titleElement.props.style[1]).toEqual({ color: "#ffa500" }); }); + +test("Component maps style onto a same-named prop when nativeStyleMapping uses the `true` shorthand", () => { + registerCSS(`.fill { background-color: red; }`); + + const mapping: StyledConfiguration = { + className: { + target: "style", + nativeStyleMapping: { + backgroundColor: true, + }, + }, + }; + + const Component = copyComponentProperties( + RNView, + (props: StyledProps) => { + return useCssElement(RNView, props, mapping); + }, + ); + + const result = render(); + + const renderedProps = result.getByTestId(testID).props; + + expect(renderedProps.backgroundColor).toBe("#f00"); + expect(renderedProps.style?.backgroundColor).toBeUndefined(); +}); diff --git a/src/native/styles/index.ts b/src/native/styles/index.ts index 9ac5cc4d..934870c1 100644 --- a/src/native/styles/index.ts +++ b/src/native/styles/index.ts @@ -504,7 +504,7 @@ function nativeStyleMapping( } let target = props; - const tokens = path.split("."); + const tokens = (typeof path === "string" ? path : key).split("."); const lastToken = tokens.pop(); for (const token of tokens) { target[token] ??= {};