Skip to content

Commit 07a4bd8

Browse files
Yamin YassinYamin Yassin
authored andcommitted
Fix inherited animated text color on native
1 parent 78ba6bd commit 07a4bd8

9 files changed

Lines changed: 136 additions & 4 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
const animatedNodeMarker = Symbol('AnimatedNode');
9+
10+
export function createAnimatedNode(config) {
11+
return Object.defineProperty({ ...config }, animatedNodeMarker, {
12+
value: true
13+
});
14+
}
15+
16+
export default class AnimatedNode {
17+
static [Symbol.hasInstance](value) {
18+
return Boolean(value?.[animatedNodeMarker]);
19+
}
20+
}

packages/benchmarks/perf/mocks/react-native.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
* React Native mock for Node.js benchmarks
1010
*/
1111

12+
import { createAnimatedNode } from './AnimatedNode';
13+
1214
export const AccessibilityInfo = {
1315
addEventListener: () => ({
1416
remove: () => {}
@@ -25,7 +27,7 @@ export const Animated = {
2527
Text: 'Animated.Text',
2628
Value: () => {
2729
return {
28-
interpolate: (value) => value
30+
interpolate: (value) => createAnimatedNode(value)
2931
};
3032
},
3133
timing: () => {

packages/benchmarks/perf/rollup.config.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ const config = [
4444
'./mocks/ViewNativeComponent.js'
4545
)
4646
},
47+
{
48+
find: 'react-native/Libraries/Animated/nodes/AnimatedNode',
49+
replacement: path.resolve(__dirname, './mocks/AnimatedNode.js')
50+
},
4751
{
4852
find: 'react-native/Libraries/Text/TextAncestor',
4953
replacement: path.resolve(

packages/react-strict-dom/src/native/modules/useStyleProps.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
*/
99

1010
import type { CustomProperties, Style } from '../../types/styles';
11-
import type { ReactNativeProps } from '../../types/renderer.native';
12-
import type { ReactNativeStyle } from '../../types/renderer.native';
11+
import type {
12+
ReactNativeProps,
13+
ReactNativeStyle,
14+
ReactNativeStyleValue
15+
} from '../../types/renderer.native';
1316

1417
import * as css from '../css';
1518
import * as ReactNative from '../react-native';
@@ -76,6 +79,10 @@ function resolveUnitlessLineHeight(style: ReactNativeStyle): ReactNativeStyle {
7679
return style;
7780
}
7881

82+
function isAnimatedNode(value: ?ReactNativeStyleValue): boolean {
83+
return value instanceof ReactNative.AnimatedNode;
84+
}
85+
7986
/**
8087
* Produces the relevant React Native props to implement the given styles, and any
8188
* inheritable text styles that may be required.
@@ -208,6 +215,9 @@ export function useStyleProps(
208215
val = inheritedValue;
209216
}
210217
if (val != null) {
218+
if (isAnimatedNode(val)) {
219+
styleProps.animated = true;
220+
}
211221
hasInheritableStyle = true;
212222
inheritableStyle[key] = val;
213223
styleProps.style[key] = val;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
8+
*/
9+
10+
import AnimatedNode from 'react-native/Libraries/Animated/nodes/AnimatedNode';
11+
export { AnimatedNode };

packages/react-strict-dom/src/native/react-native/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export {
1717
Text,
1818
TextInput
1919
} from 'react-native';
20+
export { AnimatedNode } from './AnimatedNode';
2021
export { LayoutConformance } from './LayoutConformance';
2122
export { TextAncestorContext } from './TextAncestorContext';
2223
export { ViewNativeComponent } from './ViewNativeComponent';
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
const animatedNodeMarker = Symbol('AnimatedNode');
9+
10+
export function createAnimatedNode(config) {
11+
return Object.defineProperty({ ...config }, animatedNodeMarker, {
12+
value: true
13+
});
14+
}
15+
16+
export default class AnimatedNode {
17+
static [Symbol.hasInstance](value) {
18+
return Boolean(value?.[animatedNodeMarker]);
19+
}
20+
}

packages/react-strict-dom/tests/__mocks__/react-native/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8+
import { createAnimatedNode } from './Libraries/Animated/nodes/AnimatedNode';
9+
810
export const AccessibilityInfo = {
911
addEventListener: jest.fn().mockReturnValue({ remove: jest.fn() }),
1012
isReduceMotionEnabled: jest.fn().mockReturnValue(Promise.resolve(false))
@@ -19,7 +21,9 @@ export const Animated = {
1921
Text: 'Animated.Text',
2022
Value: jest.fn(() => {
2123
return {
22-
interpolate: jest.fn().mockImplementation((value) => value)
24+
interpolate: jest
25+
.fn()
26+
.mockImplementation((value) => createAnimatedNode(value))
2327
};
2428
}),
2529
timing: jest.fn(() => {

packages/react-strict-dom/tests/html/html-test.native.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,66 @@ describe('<html.*> (native polyfills)', () => {
593593
expect(getFontSize(secondSecond)).toBe(1.5 * 3 * 16);
594594
});
595595

596+
test('inherited color from transitioned parent', () => {
597+
const styles = css.create({
598+
container: {
599+
padding: '32px',
600+
transitionProperty: 'backgroundColor, color',
601+
transitionDuration: '300ms',
602+
backgroundColor: {
603+
default: 'blue',
604+
':active': 'darkblue'
605+
},
606+
color: {
607+
default: 'white',
608+
':active': 'green'
609+
}
610+
},
611+
text: {
612+
fontSize: '24px',
613+
color: 'inherit'
614+
}
615+
});
616+
617+
let root;
618+
act(() => {
619+
root = create(
620+
<html.button style={styles.container}>
621+
<html.span style={styles.text}>Alert Banner</html.span>
622+
</html.button>
623+
);
624+
});
625+
626+
let button = root.toJSON();
627+
let span = button.children[0];
628+
expect(span.type).toBe('Text');
629+
expect(span.props.style.color).toBe('white');
630+
631+
act(() => {
632+
button.props.onPointerDown();
633+
});
634+
635+
button = root.toJSON();
636+
span = button.children[0];
637+
expect(span.type).toBe('Animated.Text');
638+
expect(span.props.style.color).toEqual({
639+
inputRange: [0, 1],
640+
outputRange: ['white', 'green']
641+
});
642+
643+
act(() => {
644+
button.props.onPointerUp();
645+
});
646+
647+
button = root.toJSON();
648+
span = button.children[0];
649+
expect(span.type).toBe('Animated.Text');
650+
expect(span.props.style.color).toEqual({
651+
inputRange: [0, 1],
652+
outputRange: ['green', 'white']
653+
});
654+
});
655+
596656
test('inherited lineHeight (unitless)', () => {
597657
const styles = css.create({
598658
root: {

0 commit comments

Comments
 (0)