Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions src/__tests__/native/styled-ref-forwarding.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* eslint-disable */
// @ts-nocheck
import React from "react";
import { View } from "react-native";

import { render, screen } from "@testing-library/react-native";
import { registerCSS, testID } from "react-native-css/jest";
import { styled } from "react-native-css/runtime";

const children = undefined;

// Mock SVG component that mimics react-native-svg behavior
const MockSvg = React.forwardRef<any, any>((props: any, ref: any) => {
return <View ref={ref} {...props} testID={props.testID ?? "mock-svg"} />;
});
MockSvg.displayName = "MockSvg";

describe("styled() ref forwarding and deprecated property support", () => {
test("should work with explicit nativeStyleMapping", () => {
registerCSS(`
.svg-explicit {
height: 24px;
width: 24px;
fill: red;
}
`);

const StyledSvg = styled(MockSvg, {
className: {
target: "style",
nativeStyleMapping: {
height: "height",
width: "width",
fill: "fill",
},
},
});

render(<StyledSvg testID={testID} className="svg-explicit" />);
const component = screen.getByTestId(testID);

expect(component.props).toEqual(
expect.objectContaining({
testID,
children,
height: 24,
width: 24,
fill: "#f00",
style: {},
}),
);
});

test("should properly forward refs", () => {
registerCSS(`
.ref-test {
height: 16px;
width: 16px;
}
`);

const StyledSvg = styled(MockSvg, {
className: {
target: "style",
nativeStyleMapping: {
height: "height",
width: "width",
},
},
});

const ref = React.createRef<any>();

render(<StyledSvg ref={ref} testID={testID} className="ref-test" />);

// Ref should be properly forwarded
expect(ref.current).toBeTruthy();
expect(ref.current.props.testID).toBe(testID);
});

test("should work without explicit mapping for regular components", () => {
registerCSS(`
.regular {
padding: 10px;
background-color: green;
}
`);

const StyledView = styled(View, {
className: "style",
});

render(<StyledView testID={testID} className="regular" />);
const component = screen.getByTestId(testID);

expect(component.props).toEqual(
expect.objectContaining({
testID,
children,
style: {
padding: 10,
backgroundColor: "#008000",
},
}),
);
});
});
21 changes: 10 additions & 11 deletions src/native/api.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable */
import { useContext, useState, type ComponentType } from "react";
import { forwardRef, useContext, useState, type ComponentType } from "react";
import { Appearance } from "react-native";

import type { StyleDescriptor } from "react-native-css/compiler";
Expand Down Expand Up @@ -48,22 +48,21 @@ export const styled = <
mapping: M = defaultMapping as M,
options?: StyledOptions,
) => {
let component: any;
// const type = getComponentType(baseComponent);

const configs = mappingToConfig(mapping);
const name = baseComponent.displayName ?? baseComponent.name ?? "unknown";

let component: any;

if (options?.passThrough) {
component = (props: Record<string, any>) => {
return usePassthrough(baseComponent, props, configs);
};
component = forwardRef<any, any>((props, ref) => {
return usePassthrough(baseComponent, { ref, ...props }, configs);
});
} else {
component = (props: Record<string, any>) => {
return useNativeCss(baseComponent, props, configs);
};
component = forwardRef<any, any>((props, ref) => {
return useNativeCss(baseComponent, { ref, ...props }, configs);
});
}

const name = baseComponent.displayName ?? baseComponent.name ?? "unknown";
component.displayName = `CssInterop.${name}`;
return component;
};
Expand Down