Skip to content

Commit bf7ea4c

Browse files
refactor: scroll view native state (#1661)
* refactor: move scroll state into native state * refactor: simplify types * refactor: renaming
1 parent e6497d2 commit bf7ea4c

File tree

12 files changed

+45
-61
lines changed

12 files changed

+45
-61
lines changed

src/fire-event.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,6 @@ function setNativeStateIfNeeded(element: ReactTestInstance, eventName: string, v
154154
isHostTextInput(element) &&
155155
isTextInputEditable(element)
156156
) {
157-
nativeState?.elementValues.set(element, value);
157+
nativeState?.valueForElement.set(element, value);
158158
}
159159
}

src/helpers/text-input.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function getTextInputValue(element: ReactTestInstance) {
1717

1818
return (
1919
element.props.value ??
20-
nativeState?.elementValues.get(element) ??
20+
nativeState?.valueForElement.get(element) ??
2121
element.props.defaultValue ??
2222
''
2323
);

src/native-state.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import { ReactTestInstance } from 'react-test-renderer';
2+
import { Point } from './types';
23

34
/**
45
* Simulated native state for unmanaged controls.
56
*
67
* Values from `value` props (managed controls) should take precedence over these values.
78
*/
89
export type NativeState = {
9-
elementValues: WeakMap<ReactTestInstance, string>;
10+
valueForElement: WeakMap<ReactTestInstance, string>;
11+
contentOffsetForElement: WeakMap<ReactTestInstance, Point>;
1012
};
1113

1214
export let nativeState: NativeState | null = null;
1315

1416
export function initNativeState(): void {
1517
nativeState = {
16-
elementValues: new WeakMap(),
18+
valueForElement: new WeakMap(),
19+
contentOffsetForElement: new WeakMap(),
1720
};
1821
}
1922

src/types.ts

+16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/**
2+
* Location of an element.
3+
*/
4+
export interface Point {
5+
y: number;
6+
x: number;
7+
}
8+
9+
/**
10+
* Size of an element.
11+
*/
12+
export interface Size {
13+
height: number;
14+
width: number;
15+
}
16+
117
// TS autocomplete trick
218
// Ref: https://github.com/microsoft/TypeScript/issues/29729#issuecomment-567871939
319
export type StringWithAutocomplete<T> = T | (string & {});

src/user-event/event-builder/scroll-view.ts

+4-17
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,12 @@
1+
import { Point, Size } from '../../types';
12
import { baseSyntheticEvent } from './base';
23

3-
/**
4-
* Scroll position of a scrollable element.
5-
*/
6-
export interface ContentOffset {
7-
y: number;
8-
x: number;
9-
}
10-
114
/**
125
* Other options for constructing a scroll event.
136
*/
147
export type ScrollEventOptions = {
15-
contentSize?: {
16-
height: number;
17-
width: number;
18-
};
19-
layoutMeasurement?: {
20-
height: number;
21-
width: number;
22-
};
8+
contentSize?: Size;
9+
layoutMeasurement?: Size;
2310
};
2411

2512
/**
@@ -28,7 +15,7 @@ export type ScrollEventOptions = {
2815
* - Android: `{"contentInset": {"bottom": 0, "left": 0, "right": 0, "top": 0}, "contentOffset": {"x": 0, "y": 31.619047164916992}, "contentSize": {"height": 1624.761962890625, "width": 411.4285583496094}, "layoutMeasurement": {"height": 785.5238037109375, "width": 411.4285583496094}, "responderIgnoreScroll": true, "target": 139, "velocity": {"x": -1.3633992671966553, "y": -1.3633992671966553}}`
2916
*/
3017
export const ScrollViewEventBuilder = {
31-
scroll: (offset: ContentOffset = { y: 0, x: 0 }, options?: ScrollEventOptions) => {
18+
scroll: (offset: Point = { y: 0, x: 0 }, options?: ScrollEventOptions) => {
3219
return {
3320
...baseSyntheticEvent(),
3421
nativeEvent: {

src/user-event/event-builder/text-input.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ContentSize } from '../utils/content-size';
1+
import { Size } from '../../types';
22
import { TextRange } from '../utils/text-range';
33
import { baseSyntheticEvent } from './base';
44

@@ -68,7 +68,7 @@ export const TextInputEventBuilder = {
6868
* - iOS: `{"contentSize": {"height": 21.666666666666668, "width": 11.666666666666666}, "target": 75}`
6969
* - Android: `{"contentSize": {"height": 61.45454406738281, "width": 352.7272644042969}, "target": 53}`
7070
*/
71-
contentSizeChange: ({ width, height }: ContentSize) => {
71+
contentSizeChange: ({ width, height }: Size) => {
7272
return {
7373
...baseSyntheticEvent(),
7474
nativeEvent: { contentSize: { width, height }, target: 0 },

src/user-event/paste.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export async function paste(
3333
dispatchEvent(element, 'selectionChange', EventBuilder.TextInput.selectionChange(rangeToClear));
3434

3535
// 3. Paste the text
36-
nativeState?.elementValues.set(element, text);
36+
nativeState?.valueForElement.set(element, text);
3737
dispatchEvent(element, 'change', EventBuilder.TextInput.change(text));
3838
dispatchEvent(element, 'changeText', text);
3939

src/user-event/scroll/scroll-to.ts

+8-14
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,14 @@ import { EventBuilder } from '../event-builder';
55
import { ErrorWithStack } from '../../helpers/errors';
66
import { isHostScrollView } from '../../helpers/host-component-names';
77
import { pick } from '../../helpers/object';
8-
import { ContentOffset } from '../event-builder/scroll-view';
8+
import { nativeState } from '../../native-state';
9+
import { Point, Size } from '../../types';
910
import { dispatchEvent, wait } from '../utils';
1011
import { createScrollSteps, inertialInterpolator, linearInterpolator } from './utils';
11-
import { getElementScrollOffset, setElementScrollOffset } from './state';
1212

1313
interface CommonScrollToOptions {
14-
contentSize?: {
15-
height: number;
16-
width: number;
17-
};
18-
layoutMeasurement?: {
19-
height: number;
20-
width: number;
21-
};
14+
contentSize?: Size;
15+
layoutMeasurement?: Size;
2216
}
2317

2418
export interface VerticalScrollToOptions extends CommonScrollToOptions {
@@ -62,7 +56,7 @@ export async function scrollTo(
6256
options.contentSize?.height ?? 0,
6357
);
6458

65-
const initialPosition = getElementScrollOffset(element);
59+
const initialPosition = nativeState?.contentOffsetForElement.get(element) ?? { x: 0, y: 0 };
6660
const dragSteps = createScrollSteps(
6761
{ y: options.y, x: options.x },
6862
initialPosition,
@@ -79,13 +73,13 @@ export async function scrollTo(
7973
await emitMomentumScrollEvents(this.config, element, momentumSteps, options);
8074

8175
const finalPosition = momentumSteps.at(-1) ?? dragSteps.at(-1) ?? initialPosition;
82-
setElementScrollOffset(element, finalPosition);
76+
nativeState?.contentOffsetForElement.set(element, finalPosition);
8377
}
8478

8579
async function emitDragScrollEvents(
8680
config: UserEventConfig,
8781
element: ReactTestInstance,
88-
scrollSteps: ContentOffset[],
82+
scrollSteps: Point[],
8983
scrollOptions: ScrollToOptions,
9084
) {
9185
if (scrollSteps.length === 0) {
@@ -115,7 +109,7 @@ async function emitDragScrollEvents(
115109
async function emitMomentumScrollEvents(
116110
config: UserEventConfig,
117111
element: ReactTestInstance,
118-
scrollSteps: ContentOffset[],
112+
scrollSteps: Point[],
119113
scrollOptions: ScrollToOptions,
120114
) {
121115
if (scrollSteps.length === 0) {

src/user-event/scroll/state.ts

-12
This file was deleted.

src/user-event/scroll/utils.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { ContentOffset } from '../event-builder/scroll-view';
1+
import { Point } from '../../types';
22

33
const DEFAULT_STEPS_COUNT = 5;
44

55
type InterpolatorFn = (end: number, start: number, steps: number) => number[];
66

77
export function createScrollSteps(
8-
target: Partial<ContentOffset>,
9-
initialOffset: ContentOffset,
8+
target: Partial<Point>,
9+
initialOffset: Point,
1010
interpolator: InterpolatorFn,
11-
): ContentOffset[] {
11+
): Point[] {
1212
if (target.y != null) {
1313
return interpolator(target.y, initialOffset.y, DEFAULT_STEPS_COUNT).map((y) => ({
1414
y,

src/user-event/type/type.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export async function emitTypingEvents(
9595
return;
9696
}
9797

98-
nativeState?.elementValues.set(element, text);
98+
nativeState?.valueForElement.set(element, text);
9999
dispatchEvent(element, 'change', EventBuilder.TextInput.change(text));
100100
dispatchEvent(element, 'changeText', text);
101101

src/user-event/utils/content-size.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
export interface ContentSize {
2-
width: number;
3-
height: number;
4-
}
1+
import { Size } from '../../types';
52

63
/**
74
* Simple function for getting mock the size of given text.
@@ -13,8 +10,7 @@ export interface ContentSize {
1310
* @param text text to be measure
1411
* @returns width and height of the text
1512
*/
16-
17-
export function getTextContentSize(text: string): ContentSize {
13+
export function getTextContentSize(text: string): Size {
1814
const lines = text.split('\n');
1915
const maxLineLength = Math.max(...lines.map((line) => line.length));
2016

0 commit comments

Comments
 (0)