Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: user event scroll #1445

Merged
merged 40 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
31046cc
feat: setup dummy scroll endpoint
siepra Aug 8, 2023
efd87c1
chore: use ScrollEventBuilder
siepra Aug 8, 2023
5ef78f7
chore: enhance API endpoint with parameters
siepra Aug 8, 2023
b75d214
chore: emit multiple scroll events
siepra Aug 8, 2023
e7a78c9
fix: intermediate steps offset values
siepra Aug 8, 2023
62c6ea8
feat: momentum scroll
siepra Aug 8, 2023
aa77fae
chore: add scroll to userEvent
siepra Aug 9, 2023
48b4fa4
feat: scrollToTop
siepra Aug 9, 2023
ecbfdfe
feat: remember scroll position for element
siepra Aug 10, 2023
51b265d
fix: setting and getting scroll state
siepra Aug 10, 2023
48efcd2
chore: validate host component type
siepra Aug 10, 2023
c1784d9
chore: validate and emit intermediate callbacks in scrollToTop
siepra Aug 10, 2023
a2f9ac9
chore: fix ts
mdjastrzebski Aug 14, 2023
27bbfed
refactor: rename scroll to scrollTo
mdjastrzebski Aug 17, 2023
1ae0f1a
refactor: y & x scrollTo params
mdjastrzebski Aug 17, 2023
9c5ad7f
refactor: refactor y, x params to support explicit steps
mdjastrzebski Aug 17, 2023
9379408
chore: fix typecheck
mdjastrzebski Aug 17, 2023
5d43697
refactor: improve typing
mdjastrzebski Aug 17, 2023
0f772e5
refactor: momentumY, momentumX options
mdjastrzebski Aug 17, 2023
aa1949b
docs: initial docs for `scrollTo`
mdjastrzebski Sep 3, 2023
4f7b643
refactor: descope `scrollToTop` variant
mdjastrzebski Sep 3, 2023
69e4423
docs: tweaks
mdjastrzebski Sep 3, 2023
81d4c85
refactor: improve TS typing by separating vertical/horizontal scroll
mdjastrzebski Sep 4, 2023
fe844e2
refactor: wrap scroll event in nativeEvent wrapper
mdjastrzebski Sep 4, 2023
75cb44a
refactor: clean up implementation
mdjastrzebski Sep 4, 2023
960227d
chore: add FlatList tests
mdjastrzebski Sep 4, 2023
d5eaaa6
refactor: remove explicit steps
mdjastrzebski Sep 6, 2023
5d8d628
refactor: introduce inertial interpolator for momentum scroll
mdjastrzebski Sep 7, 2023
c5ad927
docs: update docs
mdjastrzebski Sep 7, 2023
6487035
chore: update snapshots
mdjastrzebski Sep 7, 2023
38e60c5
refactor: code review changes
mdjastrzebski Sep 12, 2023
9304856
feat: validate scroll direction
mdjastrzebski Sep 12, 2023
7ce9fd3
docs: tweaks
mdjastrzebski Sep 12, 2023
32d4ee1
docs: tweak
mdjastrzebski Sep 12, 2023
3477ea6
refactor: add more tests to improve code cov
mdjastrzebski Sep 12, 2023
be8e0f4
refactor: improve errors
mdjastrzebski Sep 12, 2023
4e4c1df
refactor: code review tweaks
mdjastrzebski Sep 12, 2023
0f41835
refactor: add wait calls
mdjastrzebski Sep 12, 2023
474aef2
refactor: tweaks, tweaks, tweaks
mdjastrzebski Sep 12, 2023
4508d9d
refactor: final tweaks
mdjastrzebski Sep 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/__tests__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,19 @@ test('resetToDefaults() resets config to defaults', () => {

test('resetToDefaults() resets internal config to defaults', () => {
configureInternal({
hostComponentNames: { text: 'A', textInput: 'A', switch: 'A', modal: 'A' },
hostComponentNames: {
text: 'A',
textInput: 'A',
switch: 'A',
scrollView: 'A',
modal: 'A',
},
});
expect(getConfig().hostComponentNames).toEqual({
text: 'A',
textInput: 'A',
switch: 'A',
scrollView: 'A',
modal: 'A',
});

Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/host-component-names.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('getHostComponentNames', () => {
text: 'banana',
textInput: 'banana',
switch: 'banana',
scrollView: 'banana',
modal: 'banana',
},
});
Expand All @@ -29,6 +30,7 @@ describe('getHostComponentNames', () => {
text: 'banana',
textInput: 'banana',
switch: 'banana',
scrollView: 'banana',
modal: 'banana',
});
});
Expand All @@ -42,6 +44,7 @@ describe('getHostComponentNames', () => {
text: 'Text',
textInput: 'TextInput',
switch: 'RCTSwitch',
scrollView: 'RCTScrollView',
modal: 'Modal',
});
expect(getConfig().hostComponentNames).toBe(hostComponentNames);
Expand Down Expand Up @@ -71,6 +74,7 @@ describe('configureHostComponentNamesIfNeeded', () => {
text: 'Text',
textInput: 'TextInput',
switch: 'RCTSwitch',
scrollView: 'RCTScrollView',
modal: 'Modal',
});
});
Expand All @@ -81,6 +85,7 @@ describe('configureHostComponentNamesIfNeeded', () => {
text: 'banana',
textInput: 'banana',
switch: 'banana',
scrollView: 'banana',
modal: 'banana',
},
});
Expand All @@ -91,6 +96,7 @@ describe('configureHostComponentNamesIfNeeded', () => {
text: 'banana',
textInput: 'banana',
switch: 'banana',
scrollView: 'banana',
modal: 'banana',
});
});
Expand Down
1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export type HostComponentNames = {
text: string;
textInput: string;
switch: string;
scrollView: string;
modal: string;
};

Expand Down
14 changes: 13 additions & 1 deletion src/helpers/host-component-names.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import { ReactTestInstance } from 'react-test-renderer';
import { Modal, Switch, Text, TextInput, View } from 'react-native';
import { Modal, ScrollView, Switch, Text, TextInput, View } from 'react-native';
import { configureInternal, getConfig, HostComponentNames } from '../config';
import { renderWithAct } from '../render-act';
import { HostTestInstance } from './component-tree';
Expand Down Expand Up @@ -35,6 +35,7 @@ function detectHostComponentNames(): HostComponentNames {
<Text testID="text">Hello</Text>
<TextInput testID="textInput" />
<Switch testID="switch" />
<ScrollView testID="scrollView" />
<Modal testID="modal" />
</View>
);
Expand All @@ -43,6 +44,7 @@ function detectHostComponentNames(): HostComponentNames {
text: getByTestId(renderer.root, 'text').type as string,
textInput: getByTestId(renderer.root, 'textInput').type as string,
switch: getByTestId(renderer.root, 'switch').type as string,
scrollView: getByTestId(renderer.root, 'scrollView').type as string,
modal: getByTestId(renderer.root, 'modal').type as string,
};
} catch (error) {
Expand Down Expand Up @@ -89,6 +91,16 @@ export function isHostTextInput(
return element?.type === getHostComponentNames().textInput;
}

/**
* Checks if the given element is a host ScrollView.
* @param element The element to check.
*/
export function isHostScrollView(
element?: ReactTestInstance | null
): element is HostTestInstance {
return element?.type === getHostComponentNames().scrollView;
}

/**
* Checks if the given element is a host Modal.
* @param element The element to check.
Expand Down
10 changes: 10 additions & 0 deletions src/helpers/object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function pick<T extends {}>(object: T, keys: (keyof T)[]): Partial<T> {
const result: Partial<T> = {};
keys.forEach((key) => {
if (object[key] !== undefined) {
result[key] = object[key];
}
});

return result;
}
2 changes: 1 addition & 1 deletion src/test-utils/events.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
interface EventEntry {
export interface EventEntry {
name: string;
payload: any;
}
Expand Down
2 changes: 2 additions & 0 deletions src/user-event/event-builder/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { CommonEventBuilder } from './common';
import { ScrollViewEventBuilder } from './scroll-view';
import { TextInputEventBuilder } from './text-input';

export const EventBuilder = {
Common: CommonEventBuilder,
ScrollView: ScrollViewEventBuilder,
TextInput: TextInputEventBuilder,
};
32 changes: 32 additions & 0 deletions src/user-event/event-builder/scroll-view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Experimental values:
* - iOS: `{"contentInset": {"bottom": 0, "left": 0, "right": 0, "top": 0}, "contentOffset": {"x": 0, "y": 5.333333333333333}, "contentSize": {"height": 1676.6666259765625, "width": 390}, "layoutMeasurement": {"height": 753, "width": 390}, "zoomScale": 1}`
* - 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}}`
*/

/**
* Scroll position of a scrollable element.
*/
export interface ContentOffset {
y: number;
x: number;
}

export const ScrollViewEventBuilder = {
scroll: (offset: ContentOffset = { y: 0, x: 0 }) => {
return {
nativeEvent: {
contentInset: { bottom: 0, left: 0, right: 0, top: 0 },
contentOffset: { y: offset.y, x: offset.x },
contentSize: { height: 0, width: 0 },
layoutMeasurement: {
height: 0,
width: 0,
},
responderIgnoreScroll: true,
target: 0,
velocity: { y: 0, x: 0 },
},
};
},
};
3 changes: 3 additions & 0 deletions src/user-event/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ReactTestInstance } from 'react-test-renderer';
import { setup } from './setup';
import { PressOptions } from './press';
import { TypeOptions } from './type';
import { ScrollToOptions } from './scroll';

export { UserEventConfig } from './setup';

Expand All @@ -15,4 +16,6 @@ export const userEvent = {
type: (element: ReactTestInstance, text: string, options?: TypeOptions) =>
setup().type(element, text, options),
clear: (element: ReactTestInstance) => setup().clear(element),
scrollTo: (element: ReactTestInstance, options: ScrollToOptions) =>
setup().scrollTo(element, options),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`scrollTo() with FlatList supports vertical drag scroll: scrollTo({ y: 100 }) 1`] = `
[
{
"name": "scrollBeginDrag",
"payload": {
"nativeEvent": {
"contentInset": {
"bottom": 0,
"left": 0,
"right": 0,
"top": 0,
},
"contentOffset": {
"x": 0,
"y": 0,
},
"contentSize": {
"height": 0,
"width": 0,
},
"layoutMeasurement": {
"height": 0,
"width": 0,
},
"responderIgnoreScroll": true,
"target": 0,
"velocity": {
"x": 0,
"y": 0,
},
},
},
},
{
"name": "scroll",
"payload": {
"nativeEvent": {
"contentInset": {
"bottom": 0,
"left": 0,
"right": 0,
"top": 0,
},
"contentOffset": {
"x": 0,
"y": 25,
},
"contentSize": {
"height": 0,
"width": 0,
},
"layoutMeasurement": {
"height": 0,
"width": 0,
},
"responderIgnoreScroll": true,
"target": 0,
"velocity": {
"x": 0,
"y": 0,
},
},
},
},
{
"name": "scroll",
"payload": {
"nativeEvent": {
"contentInset": {
"bottom": 0,
"left": 0,
"right": 0,
"top": 0,
},
"contentOffset": {
"x": 0,
"y": 50,
},
"contentSize": {
"height": 0,
"width": 0,
},
"layoutMeasurement": {
"height": 0,
"width": 0,
},
"responderIgnoreScroll": true,
"target": 0,
"velocity": {
"x": 0,
"y": 0,
},
},
},
},
{
"name": "scroll",
"payload": {
"nativeEvent": {
"contentInset": {
"bottom": 0,
"left": 0,
"right": 0,
"top": 0,
},
"contentOffset": {
"x": 0,
"y": 75,
},
"contentSize": {
"height": 0,
"width": 0,
},
"layoutMeasurement": {
"height": 0,
"width": 0,
},
"responderIgnoreScroll": true,
"target": 0,
"velocity": {
"x": 0,
"y": 0,
},
},
},
},
{
"name": "scrollEndDrag",
"payload": {
"nativeEvent": {
"contentInset": {
"bottom": 0,
"left": 0,
"right": 0,
"top": 0,
},
"contentOffset": {
"x": 0,
"y": 100,
},
"contentSize": {
"height": 0,
"width": 0,
},
"layoutMeasurement": {
"height": 0,
"width": 0,
},
"responderIgnoreScroll": true,
"target": 0,
"velocity": {
"x": 0,
"y": 0,
},
},
},
},
]
`;
Loading