Repository navigation
matrix(), matrix3d() and the 3D transform functions compile to an empty entry, so React Native's processTransform throws on the whole transform #553
Description
Before submitting a new issue
- I tested using the latest version of the library, as the bug might be already fixed.
- I tested using a supported version of react native.
- I checked for possible duplicate issues, with possible answers.
Bug summary
transform: matrix(1, 0, 0, 1, 5, 6) renders transform: [[]]. Each of matrix(), matrix3d(), translate3d(), translateZ(), scale3d(), scaleZ() and rotate3d() becomes an empty list in the transform, and React Native's processTransform refuses it with You must specify exactly one property per transform object. Passed properties: [], so the whole transform throws in development, a rotate(45deg) beside it included.
Four of them have a form React Native's transform draws, and processTransform takes each:
| CSS | React Native |
|---|---|
matrix(a, b, c, d, e, f) |
{ matrix: [a, b, 0, c, d, 0, e, f, 1] } |
matrix3d(…16 numbers) |
{ matrix: [the 16 numbers] } |
translate3d(x, y, z) |
{ translate: [x, y, z] } |
translateZ(z) |
{ translate: [0, 0, z] } |
React Native takes a matrix entry only as the transform's one entry, so a matrix() beside other functions would need them folded into it. scale3d(), scaleZ() and rotate3d() have no React Native form, but leaving them out of the list would let the rest of the transform apply, rather than the empty entry that makes it throw.
parseTransform (src/compiler/declarations.ts) returns [[]] from its translate3d, translateZ, scaleZ, scale3d, rotate3d, matrix and matrix3d arms. #389 changes those arms to return nothing, which stops the throw but still drops matrix(), matrix3d(), translate3d() and translateZ().
Library version
main at a37903f (0.0.0-nightly.a37903f)
Environment info
Windows 11 10.0.26300, Node 26.12.1, Yarn 4.9.2, react-native 0.86.3 (the repository's dev dependency).
No app or native build is involved: the output comes from the repository's jest setup, which runs React Native's own processTransform.Steps to reproduce
- Clone the reproducer branch, which is
mainat a37903f with one file added,src/__tests__/native/matrix-and-3d-transforms.test.tsx, and install:git clone --branch repro/553-matrix-and-3d-transforms-dropped https://github.com/YevheniiKotyrlo/react-native-css cd react-native-css yarn install - Run
yarn jest src/__tests__/native/matrix-and-3d-transforms.test.tsxand read the logged lines: each CSS case's rendered style and whetherprocessTransformtakes its transform, then the same check for React Native's own forms.
src/__tests__/native/matrix-and-3d-transforms.test.tsx:
import { StyleSheet } from "react-native";
import { render, screen } from "@testing-library/react-native";
import { View } from "react-native-css/components/View";
import { registerCSS, testID } from "react-native-css/jest";
const processTransform = jest.requireActual<{
default: (transform: unknown) => unknown;
}>("react-native/Libraries/StyleSheet/processTransform");
jest.mock("../../native/reanimated", () => ({
animatedComponentFamily: (component: unknown) => component,
}));
function readByReactNative(transform: unknown): string {
try {
processTransform.default(transform);
return "takes it";
} catch (error) {
return `throws ${(error as Error).message.split("\n")[0]}`;
}
}
test.each([
"matrix(1, 0, 0, 1, 5, 6)",
"matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5, 6, 7, 1)",
"translate3d(1px, 2px, 3px)",
"translateZ(10px)",
"translate3d(1px, 2px, 3px) rotate(45deg)",
"scale3d(1, 2, 3) rotate(45deg)",
"scaleZ(2) rotate(45deg)",
"rotate3d(0, 0, 1, 45deg) rotate(45deg)",
])("transform: %s", (transform) => {
registerCSS(`.a { transform: ${transform}; }`);
render(<View testID={testID} className="a" />);
const style = StyleSheet.flatten(screen.getByTestId(testID).props.style);
console.log(
`transform: ${transform}\n => ${JSON.stringify(style)}\n processTransform: ${readByReactNative(style.transform)}`,
);
screen.unmount();
});
test.each([
[[{ matrix: [1, 0, 0, 0, 1, 0, 5, 6, 1] }]],
[[{ matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5, 6, 7, 1] }]],
[[{ translate: [1, 2, 3] }]],
[[{ translate: [0, 0, 10] }]],
[[{ translate: [1, 2, 3] }, { rotate: "45deg" }]],
[[{ rotate: "45deg" }]],
])("React Native's form %j", (transform) => {
console.log(
`${JSON.stringify(transform)}\n processTransform: ${readByReactNative(transform)}`,
);
});Expected: matrix(1, 0, 0, 1, 5, 6) renders { matrix: [1, 0, 0, 0, 1, 0, 5, 6, 1] }, translate3d(1px, 2px, 3px) renders { translate: [1, 2, 3] } and translateZ(10px) renders { translate: [0, 0, 10] }, forms processTransform takes, as the second test shows; and no case renders an empty entry.
Actual: every case renders an empty entry ([[]], or [[], { rotate: "45deg" }] beside a rotate), and processTransform throws on it.
The run's output on main at a37903f, Windows 11, Node 26.12.1
console.log
transform: matrix(1, 0, 0, 1, 5, 6)
=> {"transform":[[]]}
processTransform: throws You must specify exactly one property per transform object. Passed properties: []
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:37:11)
console.log
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5, 6, 7, 1)
=> {"transform":[[]]}
processTransform: throws You must specify exactly one property per transform object. Passed properties: []
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:37:11)
console.log
transform: translate3d(1px, 2px, 3px)
=> {"transform":[[]]}
processTransform: throws You must specify exactly one property per transform object. Passed properties: []
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:37:11)
console.log
transform: translateZ(10px)
=> {"transform":[[]]}
processTransform: throws You must specify exactly one property per transform object. Passed properties: []
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:37:11)
console.log
transform: translate3d(1px, 2px, 3px) rotate(45deg)
=> {"transform":[[],{"rotate":"45deg"}]}
processTransform: throws You must specify exactly one property per transform object. Passed properties: []
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:37:11)
console.log
transform: scale3d(1, 2, 3) rotate(45deg)
=> {"transform":[[],{"rotate":"45deg"}]}
processTransform: throws You must specify exactly one property per transform object. Passed properties: []
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:37:11)
console.log
transform: scaleZ(2) rotate(45deg)
=> {"transform":[[],{"rotate":"45deg"}]}
processTransform: throws You must specify exactly one property per transform object. Passed properties: []
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:37:11)
console.log
transform: rotate3d(0, 0, 1, 45deg) rotate(45deg)
=> {"transform":[[],{"rotate":"45deg"}]}
processTransform: throws You must specify exactly one property per transform object. Passed properties: []
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:37:11)
console.log
[{"matrix":[1,0,0,0,1,0,5,6,1]}]
processTransform: takes it
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:51:11)
console.log
[{"matrix":[1,0,0,0,0,1,0,0,0,0,1,0,5,6,7,1]}]
processTransform: takes it
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:51:11)
console.log
[{"translate":[1,2,3]}]
processTransform: takes it
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:51:11)
console.log
[{"translate":[0,0,10]}]
processTransform: takes it
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:51:11)
console.log
[{"translate":[1,2,3]},{"rotate":"45deg"}]
processTransform: takes it
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:51:11)
console.log
[{"rotate":"45deg"}]
processTransform: takes it
at log (src/__tests__/native/matrix-and-3d-transforms.test.tsx:51:11)
PASS src/__tests__/native/matrix-and-3d-transforms.test.tsx
√ transform: matrix(1, 0, 0, 1, 5, 6) (25 ms)
√ transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5, 6, 7, 1) (5 ms)
√ transform: translate3d(1px, 2px, 3px) (3 ms)
√ transform: translateZ(10px) (1 ms)
√ transform: translate3d(1px, 2px, 3px) rotate(45deg) (2 ms)
√ transform: scale3d(1, 2, 3) rotate(45deg) (1 ms)
√ transform: scaleZ(2) rotate(45deg) (1 ms)
√ transform: rotate3d(0, 0, 1, 45deg) rotate(45deg) (1 ms)
√ React Native's form [{"matrix":[1,0,0,0,1,0,5,6,1]}] (1 ms)
√ React Native's form [{"matrix":[1,0,0,0,0,1,0,0,0,0,1,0,5,6,7,1]}] (1 ms)
√ React Native's form [{"translate":[1,2,3]}]
√ React Native's form [{"translate":[0,0,10]}]
√ React Native's form [{"translate":[1,2,3]},{"rotate":"45deg"}] (1 ms)
√ React Native's form [{"rotate":"45deg"}]
Test Suites: 1 passed, 1 total
Tests: 14 passed, 14 total
Snapshots: 0 total
Time: 1.649 s, estimated 2 s
Ran all test suites matching /src\\__tests__\\native\\matrix-and-3d-transforms.test.tsx/i.
Reproducible example repository
https://github.com/YevheniiKotyrlo/react-native-css/tree/repro/553-matrix-and-3d-transforms-dropped
Activity