Skip to content

Commit 3c1e91b

Browse files
Merge branch 'main' into feat/use-screen-dimensions
2 parents 63fccb8 + be5235d commit 3c1e91b

100 files changed

Lines changed: 4744 additions & 354 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Changelog
22

3+
## v0.88.0-rc.4
4+
5+
### Changed
6+
7+
- **TypeScript**: Allow module augmentation to extend generated props and style types ([447ccd1ddb](https://github.com/react/react-native/commit/447ccd1ddbf3b9d95ce5e54410d7a5a54ce9bade) by [@zoontek](https://github.com/zoontek))
8+
9+
### Fixed
10+
11+
#### Android specific
12+
13+
- **Accessibility**: Properly re-enable a view after `accessibilityState.disabled` resets from `true`, which left `Pressable` partly untappable ([357b6998c4](https://github.com/react/react-native/commit/357b6998c4df940f2450448a20478b11deaf741b) by [@bigcupcoffee](https://github.com/bigcupcoffee))
14+
15+
316
## v0.88.0-rc.3
417

518
### Changed

‎flow-typed/npm/babel-traverse_v7.x.x.js‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2186,8 +2186,8 @@ declare module '@babel/traverse' {
21862186
declare export var visitors: Visitors;
21872187

21882188
declare export type Cache = {
2189-
path: $ReadOnlyWeakMap<BabelNode, unknown>,
2190-
scope: $ReadOnlyWeakMap<BabelNode, unknown>,
2189+
path: ReadonlyWeakMap<BabelNode, unknown>,
2190+
scope: ReadonlyWeakMap<BabelNode, unknown>,
21912191
clear(): void,
21922192
clearPath(): void,
21932193
clearScope(): void,

‎flow-typed/npm/babel-types_v7.x.x.js‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3825,13 +3825,13 @@ declare module "@babel/types" {
38253825
declare export function cloneNode<T>(n: T, deep?: boolean, withoutLoc?: boolean): T;
38263826
declare export function cloneWithoutLoc<T>(n: T): T;
38273827
declare type CommentTypeShorthand = 'leading' | 'inner' | 'trailing'
3828-
declare export function addComment<T: Node>(node: T, type: CommentTypeShorthand, content: string, line?: boolean): T
3829-
declare export function addComments<T: Node>(node: T, type: CommentTypeShorthand, comments: Array<Comment>): T
3828+
declare export function addComment<T extends Node>(node: T, type: CommentTypeShorthand, content: string, line?: boolean): T
3829+
declare export function addComments<T extends Node>(node: T, type: CommentTypeShorthand, comments: Array<Comment>): T
38303830
declare export function inheritInnerComments(node: Node, parent: Node): void
38313831
declare export function inheritLeadingComments(node: Node, parent: Node): void
3832-
declare export function inheritsComments<T: Node>(node: T, parent: Node): void
3832+
declare export function inheritsComments<T extends Node>(node: T, parent: Node): void
38333833
declare export function inheritTrailingComments(node: Node, parent: Node): void
3834-
declare export function removeComments<T: Node>(node: T): T
3834+
declare export function removeComments<T extends Node>(node: T): T
38353835
declare export function ensureBlock(node: BabelNode, key: string): BabelNodeBlockStatement
38363836
declare export function toBindingIdentifierName(name?: ?string): string
38373837
declare export function toBlock(node: BabelNodeStatement | BabelNodeExpression, parent?: BabelNodeFunction | null): BabelNodeBlockStatement
@@ -3843,7 +3843,7 @@ declare module "@babel/types" {
38433843
declare export function valueToNode(value: any): BabelNodeExpression
38443844
declare export function removeTypeDuplicates(types: Array<BabelNodeFlowType>): Array<BabelNodeFlowType>
38453845
declare export function appendToMemberExpression(member: BabelNodeMemberExpression, append: BabelNode, computed?: boolean): BabelNodeMemberExpression
3846-
declare export function inherits<T: Node>(child: T, parent: BabelNode | null | void): T
3846+
declare export function inherits<T extends Node>(child: T, parent: BabelNode | null | void): T
38473847
declare export function prependToMemberExpression(member: BabelNodeMemberExpression, prepend: BabelNodeExpression): BabelNodeMemberExpression
38483848
declare export function removeProperties<T>(n: T, opts: ?{}): void;
38493849
declare export function removePropertiesDeep<T>(n: T, opts: ?{}): T;
@@ -3879,7 +3879,7 @@ declare module "@babel/types" {
38793879
declare export function isValidIdentifier(name: string): boolean
38803880
declare export function isVar(node: BabelNode): node is VariableDeclaration
38813881
declare export function matchesPattern(node: ?BabelNode, match: string | Array<string>, allowPartial?: boolean): boolean
3882-
declare export function validate(n: BabelNode, key: string, value: mixed): void;
3882+
declare export function validate(n: BabelNode, key: string, value: unknown): void;
38833883
declare export type Node = BabelNode;
38843884
declare export type CommentBlock = BabelNodeCommentBlock;
38853885
declare export type CommentLine = BabelNodeCommentLine;

‎packages/dev-middleware/src/__tests__/InspectorProxyDeviceHandoff-test.js‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ const PAGE_DEFAULTS = {
3434
vm: 'bar-vm',
3535
};
3636

37+
// Must match PAGES_POLLING_INTERVAL in `Device.js`.
38+
const PAGES_POLLING_INTERVAL_MS = 1000;
39+
3740
describe('inspector-proxy device socket handoff', () => {
3841
const serverRef = withServerForEachTest({
3942
logger: undefined,
@@ -254,6 +257,50 @@ describe('inspector-proxy device socket handoff', () => {
254257
}
255258
});
256259

260+
test('device ID collision clears the previous pages polling interval', async () => {
261+
const setIntervalSpy = jest.spyOn(globalThis, 'setInterval');
262+
const clearIntervalSpy = jest.spyOn(globalThis, 'clearInterval');
263+
let device1, device2;
264+
try {
265+
({device: device1} = await connectDevice(
266+
'/inspector/device?device=device&name=foo&app=bar',
267+
[
268+
{
269+
...PAGE_DEFAULTS,
270+
vm: 'bar-vm',
271+
},
272+
],
273+
));
274+
275+
const pollingIntervals = setIntervalSpy.mock.calls.flatMap(
276+
(call, index) =>
277+
call[1] === PAGES_POLLING_INTERVAL_MS
278+
? [setIntervalSpy.mock.results[index].value]
279+
: [],
280+
);
281+
expect(pollingIntervals).toHaveLength(1);
282+
283+
({device: device2} = await connectDevice(
284+
'/inspector/device?device=device&name=foo&app=bar',
285+
[
286+
{
287+
...PAGE_DEFAULTS,
288+
vm: 'bar-vm-updated',
289+
},
290+
],
291+
));
292+
293+
for (const intervalId of pollingIntervals) {
294+
expect(clearIntervalSpy).toBeCalledWith(intervalId);
295+
}
296+
} finally {
297+
device1?.close();
298+
device2?.close();
299+
setIntervalSpy.mockRestore();
300+
clearIntervalSpy.mockRestore();
301+
}
302+
});
303+
257304
test.each([
258305
['app', 'name'],
259306
['name', 'app'],

‎packages/dev-middleware/src/inspector-proxy/Device.js‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ export default class Device {
301301
);
302302
}
303303

304+
// Stop polling the previous connection before its state is replaced.
305+
clearInterval(this.#pagesPollingIntervalId);
306+
304307
this.#dangerouslyConstruct(deviceOptions);
305308

306309
// Restore all debugger connections, not just the first one

‎packages/react-native/Libraries/Animated/__tests__/AnimatedBackend-itest.js‎

Lines changed: 192 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @fantom_flags useSharedAnimatedBackend:true updateRuntimeShadowNodeReferencesOnCommitThread:*
7+
* @fantom_flags useSharedAnimatedBackend:true updateRuntimeShadowNodeReferencesOnCommitThread:* optimizedAnimatedPropUpdates:*
88
* @flow strict-local
99
* @format
1010
*/
@@ -17,7 +17,7 @@ import * as Fantom from '@react-native/fantom';
1717
import nullthrows from 'nullthrows';
1818
import * as React from 'react';
1919
import {Component, createRef, memo, useEffect, useMemo, useState} from 'react';
20-
import {Animated, View, useAnimatedValue} from 'react-native';
20+
import {Animated, Easing, View, useAnimatedValue} from 'react-native';
2121

2222
// marginLeft (and the other margin props) are only on the native animated
2323
// allowlist when the shared backend is enabled. This verifies the prop is
@@ -77,6 +77,196 @@ test('animate marginLeft layout prop', () => {
7777
);
7878
});
7979

80+
test('non-layout props stay on the direct path while another view animates layout', () => {
81+
const movingRef = createRef<HostInstance>();
82+
83+
let _translateX;
84+
let _translateXAnimation;
85+
let _siblingHeight;
86+
let _siblingHeightAnimation;
87+
88+
function MyApp() {
89+
const translateX = useAnimatedValue(0);
90+
const siblingHeight = useAnimatedValue(10);
91+
_translateX = translateX;
92+
_siblingHeight = siblingHeight;
93+
return (
94+
<View collapsable={false}>
95+
<Animated.View
96+
ref={movingRef}
97+
style={{width: 100, height: 100, transform: [{translateX}]}}
98+
/>
99+
<Animated.View style={{width: 100, height: siblingHeight}} />
100+
</View>
101+
);
102+
}
103+
104+
const root = Fantom.createRoot();
105+
106+
Fantom.runTask(() => {
107+
root.render(<MyApp />);
108+
});
109+
110+
Fantom.runTask(() => {
111+
_translateXAnimation = Animated.timing(_translateX, {
112+
toValue: 100,
113+
duration: 200,
114+
useNativeDriver: true,
115+
}).start();
116+
_siblingHeightAnimation = Animated.timing(_siblingHeight, {
117+
toValue: 110,
118+
duration: 200,
119+
useNativeDriver: true,
120+
}).start();
121+
});
122+
123+
Fantom.unstable_produceFramesForDuration(100);
124+
125+
// The sibling's height went through a commit; the transform did not.
126+
expect(root.getRenderedOutput({props: ['height']}).toJSX()).toEqual(
127+
<rn-view>
128+
<rn-view key={0} height="100" />
129+
<rn-view key={1} height="60" />
130+
</rn-view>,
131+
);
132+
expect(
133+
Fantom.unstable_getDirectManipulationProps(nullthrows(movingRef.current))
134+
.transform,
135+
).toEqual([{translateX: 50}]);
136+
137+
Fantom.runTask(() => {
138+
_translateXAnimation?.stop();
139+
_siblingHeightAnimation?.stop();
140+
});
141+
});
142+
143+
test('direct-path props survive a re-render while another view animates layout', () => {
144+
let _opacity;
145+
let _opacityAnimation;
146+
let _siblingHeight;
147+
let _siblingHeightAnimation;
148+
let _setWidth;
149+
150+
function MyApp() {
151+
const opacity = useAnimatedValue(0);
152+
const siblingHeight = useAnimatedValue(10);
153+
const [width, setWidth] = useState(100);
154+
_opacity = opacity;
155+
_siblingHeight = siblingHeight;
156+
_setWidth = setWidth;
157+
return (
158+
<View collapsable={false}>
159+
<Animated.View style={{width, height: 100, opacity}} />
160+
<Animated.View style={{width: 100, height: siblingHeight}} />
161+
</View>
162+
);
163+
}
164+
165+
const root = Fantom.createRoot();
166+
167+
Fantom.runTask(() => {
168+
root.render(<MyApp />);
169+
});
170+
171+
Fantom.runTask(() => {
172+
_opacityAnimation = Animated.timing(_opacity, {
173+
toValue: 0.5,
174+
duration: 1000,
175+
useNativeDriver: true,
176+
}).start();
177+
_siblingHeightAnimation = Animated.timing(_siblingHeight, {
178+
toValue: 110,
179+
duration: 1000,
180+
useNativeDriver: true,
181+
}).start();
182+
});
183+
184+
Fantom.unstable_produceFramesForDuration(500);
185+
186+
Fantom.runTask(() => {
187+
_setWidth(150);
188+
});
189+
190+
expect(
191+
root.getRenderedOutput({props: ['opacity', 'width', 'height']}).toJSX(),
192+
).toEqual(
193+
<rn-view>
194+
<rn-view key={0} opacity="0.25" width="150" height="100" />
195+
<rn-view key={1} width="100" height="60" />
196+
</rn-view>,
197+
);
198+
199+
Fantom.runTask(() => {
200+
_opacityAnimation?.stop();
201+
_siblingHeightAnimation?.stop();
202+
});
203+
});
204+
205+
test('a view keeps writing its props directly after it starts animating layout', () => {
206+
const viewRef = createRef<HostInstance>();
207+
208+
let _opacity;
209+
let _opacityAnimation;
210+
let _height;
211+
let _heightAnimation;
212+
213+
function MyApp() {
214+
const opacity = useAnimatedValue(0);
215+
const height = useAnimatedValue(10);
216+
_opacity = opacity;
217+
_height = height;
218+
return (
219+
<Animated.View ref={viewRef} style={{width: 100, height, opacity}} />
220+
);
221+
}
222+
223+
const root = Fantom.createRoot();
224+
225+
Fantom.runTask(() => {
226+
root.render(<MyApp />);
227+
});
228+
229+
const viewElement = nullthrows(viewRef.current);
230+
231+
Fantom.runTask(() => {
232+
_opacityAnimation = Animated.timing(_opacity, {
233+
toValue: 0.5,
234+
duration: 1000,
235+
easing: Easing.linear,
236+
useNativeDriver: true,
237+
}).start();
238+
});
239+
240+
Fantom.unstable_produceFramesForDuration(500);
241+
242+
expect(
243+
Fantom.unstable_getDirectManipulationProps(viewElement).opacity,
244+
).toBeCloseTo(0.25, 0.001);
245+
246+
Fantom.runTask(() => {
247+
_heightAnimation = Animated.timing(_height, {
248+
toValue: 110,
249+
duration: 1000,
250+
easing: Easing.linear,
251+
useNativeDriver: true,
252+
}).start();
253+
});
254+
255+
Fantom.unstable_produceFramesForDuration(250);
256+
257+
expect(root.getRenderedOutput({props: ['height']}).toJSX()).toEqual(
258+
<rn-view height="35" />,
259+
);
260+
expect(
261+
Fantom.unstable_getDirectManipulationProps(viewElement).opacity,
262+
).toBeCloseTo(0.375, 0.001);
263+
264+
Fantom.runTask(() => {
265+
_opacityAnimation?.stop();
266+
_heightAnimation?.stop();
267+
});
268+
});
269+
80270
test('animated opacity', () => {
81271
let _opacity;
82272
let _opacityAnimation;

‎packages/react-native/Libraries/Animated/components/AnimatedImage.js‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* @format
99
*/
1010

11+
import type {ImageProps} from '../../Image/ImageProps';
1112
import type {AnimatedComponentType} from '../createAnimatedComponent';
1213

1314
import Image from '../../Image/Image';
@@ -18,6 +19,6 @@ export default createAnimatedComponent<
1819
$FlowFixMe,
1920
React.ElementRef<typeof Image>,
2021
>(Image as $FlowFixMe) as AnimatedComponentType<
21-
React.ElementConfig<typeof Image>,
22+
ImageProps,
2223
React.ElementRef<typeof Image>,
2324
>;

‎packages/react-native/Libraries/Animated/components/AnimatedScrollView.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ const AnimatedScrollViewWithInvertedRefreshControl =
8080
...props
8181
}: {
8282
ref?: React.RefSetter<AnimatedScrollViewInstance>,
83-
...React.ElementConfig<typeof ScrollView>,
83+
...React.ComponentProps<typeof ScrollView>,
8484
// $FlowFixMe[unclear-type] Same Flow type as `refreshControl` in ScrollView
8585
refreshControl: React.MixedElement,
8686
}) {

‎packages/react-native/Libraries/Animated/createAnimatedComponent.js‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import {useMemo} from 'react';
3030
type Nullable = void | null;
3131
type Primitive = string | number | boolean | symbol | void;
3232
type Builtin = (...ReadonlyArray<empty>) => unknown | Date | Error | RegExp;
33+
type ComponentProps<T> =
34+
T extends React.ComponentType<infer Props> ? Props : empty;
3335

3436
export type WithAnimatedValue<out T> = T extends Builtin | Nullable
3537
? T
@@ -98,7 +100,7 @@ export default function createAnimatedComponent<
98100
>(
99101
Component: TInstance,
100102
): AnimatedComponentType<
101-
Readonly<React.ElementConfig<TInstance>>,
103+
Readonly<ComponentProps<TInstance>>,
102104
React.ElementRef<TInstance>,
103105
> {
104106
return unstable_createAnimatedComponentWithAllowlist(Component, null);

0 commit comments

Comments
 (0)