Skip to content

Commit d1ec718

Browse files
committed
feat: add fireEvent.scroll support for scroll native events
1 parent 6143e61 commit d1ec718

File tree

3 files changed

+57
-5
lines changed

3 files changed

+57
-5
lines changed

src/fire-event.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import {
88
} from 'react-native';
99
import act from './act';
1010
import { isHostElement } from './helpers/component-tree';
11-
import { isHostTextInput } from './helpers/host-component-names';
11+
import { isHostScrollView, isHostTextInput } from './helpers/host-component-names';
1212
import { isPointerEventEnabled } from './helpers/pointer-events';
1313
import { isTextInputEditable } from './helpers/text-input';
14-
import { StringWithAutocomplete } from './types';
14+
import { Point, StringWithAutocomplete } from './types';
1515
import { nativeState } from './native-state';
1616

1717
type EventHandler = (...args: unknown[]) => unknown;
@@ -147,6 +147,14 @@ fireEvent.scroll = (element: ReactTestInstance, ...data: unknown[]) =>
147147

148148
export default fireEvent;
149149

150+
const scrollEventNames = new Set([
151+
'scroll',
152+
'scrollBeginDrag',
153+
'scrollEndDrag',
154+
'momentumScrollBegin',
155+
'momentumScrollEnd',
156+
]);
157+
150158
function setNativeStateIfNeeded(element: ReactTestInstance, eventName: string, value: unknown) {
151159
if (
152160
eventName === 'changeText' &&
@@ -156,4 +164,30 @@ function setNativeStateIfNeeded(element: ReactTestInstance, eventName: string, v
156164
) {
157165
nativeState.valueForElement.set(element, value);
158166
}
167+
168+
if (scrollEventNames.has(eventName) && isHostScrollView(element)) {
169+
const contentOffset = tryGetContentOffset(value);
170+
if (contentOffset) {
171+
nativeState.contentOffsetForElement.set(element, contentOffset);
172+
}
173+
}
174+
}
175+
176+
function tryGetContentOffset(value: unknown): Point | null {
177+
try {
178+
// @ts-expect-error: try to extract contentOffset from the event value
179+
const contentOffset = value?.nativeEvent?.contentOffset;
180+
const x = contentOffset?.x;
181+
const y = contentOffset?.y;
182+
if (typeof x === 'number' || typeof y === 'number') {
183+
return {
184+
x: Number.isFinite(x) ? x : 0,
185+
y: Number.isFinite(y) ? y : 0,
186+
};
187+
}
188+
} catch {
189+
// Do nothing
190+
}
191+
192+
return null;
159193
}

src/helpers/matchers/match-label-text.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ export function matchLabelText(
1818

1919
function matchAccessibilityLabel(
2020
element: ReactTestInstance,
21-
extpectedLabel: TextMatch,
21+
expectedLabel: TextMatch,
2222
options: TextMatchOptions,
2323
) {
24-
return matches(extpectedLabel, computeAriaLabel(element), options.normalizer, options.exact);
24+
return matches(expectedLabel, computeAriaLabel(element), options.normalizer, options.exact);
2525
}
2626

2727
function matchAccessibilityLabelledBy(

src/user-event/scroll/__tests__/scroll-to.test.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22
import { ScrollView, ScrollViewProps, View } from 'react-native';
33
import { EventEntry, createEventLogger } from '../../../test-utils';
4-
import { render, screen } from '../../..';
4+
import { fireEvent, render, screen } from '../../..';
55
import { userEvent } from '../..';
66

77
function mapEventsToShortForm(events: EventEntry[]) {
@@ -123,6 +123,24 @@ describe('scrollTo()', () => {
123123
]);
124124
});
125125

126+
test('remembers previous scroll offset from "fireEvent.scroll"', async () => {
127+
const { events } = renderScrollViewWithToolkit();
128+
const user = userEvent.setup();
129+
130+
fireEvent.scroll(screen.getByTestId('scrollView'), {
131+
nativeEvent: { contentOffset: { y: 100 } },
132+
});
133+
await user.scrollTo(screen.getByTestId('scrollView'), { y: 200 });
134+
expect(mapEventsToShortForm(events)).toEqual([
135+
['scroll', 100, undefined],
136+
['scrollBeginDrag', 100, 0],
137+
['scroll', 125, 0],
138+
['scroll', 150, 0],
139+
['scroll', 175, 0],
140+
['scrollEndDrag', 200, 0],
141+
]);
142+
});
143+
126144
it('validates vertical scroll direction', async () => {
127145
renderScrollViewWithToolkit();
128146
const user = userEvent.setup();

0 commit comments

Comments
 (0)