Skip to content

Commit 755441b

Browse files
huytdps13400zoontek
authored andcommitted
Fix inverted list wheel scrolling over its own surface
1 parent 2708107 commit 755441b

2 files changed

Lines changed: 110 additions & 1 deletion

File tree

  • packages/react-native-web/src
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import React from 'react';
2+
import { fireEvent, render } from '@testing-library/react';
3+
import VirtualizedList from '../';
4+
5+
function setScrollMetrics(node, horizontal, offset, length = 500) {
6+
const position = horizontal ? 'scrollLeft' : 'scrollTop';
7+
let current = offset;
8+
Object.defineProperties(node, {
9+
[horizontal ? 'scrollWidth' : 'scrollHeight']: {
10+
configurable: true,
11+
value: length
12+
},
13+
[horizontal ? 'clientWidth' : 'clientHeight']: {
14+
configurable: true,
15+
value: 100
16+
},
17+
[position]: {
18+
configurable: true,
19+
get: () => current,
20+
set: (value) => {
21+
current = Math.max(0, Math.min(value, length - 100));
22+
}
23+
}
24+
});
25+
return () => current;
26+
}
27+
28+
describe.each([false, true])(
29+
'VirtualizedList inverted wheel, horizontal=%s',
30+
(horizontal) => {
31+
function setup(inverted = true) {
32+
const { getByTestId } = render(
33+
<VirtualizedList
34+
data={[0]}
35+
getItem={(data, index) => data[index]}
36+
getItemCount={(data) => data.length}
37+
getItemLayout={(data, index) => ({ length: 500, offset: 0, index })}
38+
horizontal={horizontal}
39+
inverted={inverted}
40+
keyExtractor={String}
41+
renderItem={() => (
42+
<div data-testid="item">
43+
<div data-testid="nested">Nested scrollable</div>
44+
</div>
45+
)}
46+
testID="list"
47+
/>
48+
);
49+
const list = getByTestId('list');
50+
const position = setScrollMetrics(list, horizontal, 200);
51+
return { list, position, getByTestId };
52+
}
53+
54+
function wheel(target, delta) {
55+
const event = new WheelEvent('wheel', {
56+
bubbles: true,
57+
cancelable: true,
58+
[horizontal ? 'deltaX' : 'deltaY']: delta
59+
});
60+
fireEvent(target, event);
61+
return event;
62+
}
63+
64+
test.each([
65+
[200, 20, 180],
66+
[200, -20, 220],
67+
[390, 20, 370]
68+
])(
69+
'scrolls its own surface from %s by inverted delta %s',
70+
(offset, delta, expected) => {
71+
const { list } = setup();
72+
const position = setScrollMetrics(list, horizontal, offset);
73+
const event = wheel(list, delta);
74+
expect(position()).toBe(expected);
75+
expect(event.defaultPrevented).toBe(true);
76+
}
77+
);
78+
79+
test('inverts a wheel over a non-scrollable row', () => {
80+
const { position, getByTestId } = setup();
81+
const item = getByTestId('item');
82+
setScrollMetrics(item, horizontal, 0, 100);
83+
expect(wheel(item, 20).defaultPrevented).toBe(true);
84+
expect(position()).toBe(180);
85+
});
86+
87+
test.each([
88+
[50, 20, 70, 200],
89+
[195, 20, 200, 185],
90+
[5, -20, 0, 215]
91+
])(
92+
'consumes a descendant at %s before passing leftover delta %s',
93+
(offset, delta, expectedChild, expectedList) => {
94+
const { position, getByTestId } = setup();
95+
const nested = getByTestId('nested');
96+
const childPosition = setScrollMetrics(nested, horizontal, offset, 300);
97+
expect(wheel(nested, delta).defaultPrevented).toBe(true);
98+
expect(childPosition()).toBe(expectedChild);
99+
expect(position()).toBe(expectedList);
100+
}
101+
);
102+
103+
test('leaves non-inverted wheel scrolling to the browser', () => {
104+
const { list, position } = setup(false);
105+
expect(wheel(list, 20).defaultPrevented).toBe(false);
106+
expect(position()).toBe(200);
107+
});
108+
}
109+
);

‎packages/react-native-web/src/vendor/react-native/VirtualizedList/index.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
457457
const scrollOffset = this.props.horizontal ? ev.target.scrollLeft : ev.target.scrollTop;
458458
const scrollLength = this.props.horizontal ? ev.target.scrollWidth : ev.target.scrollHeight;
459459
const clientLength = this.props.horizontal ? ev.target.clientWidth : ev.target.clientHeight;
460-
const isEventTargetScrollable = scrollLength > clientLength;
460+
const isEventTargetScrollable = ev.target !== ev.currentTarget && scrollLength > clientLength;
461461
const delta = this.props.horizontal
462462
? ev.deltaX || ev.wheelDeltaX
463463
: ev.deltaY || ev.wheelDeltaY;

0 commit comments

Comments
 (0)