@pixui-dev/pxw 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/pxw.js +217 -0
- package/bin/wpbuild.js +10 -0
- package/config/default.conf +21 -0
- package/config/devops.js +358 -0
- package/config/h5es.js +10 -0
- package/config/index.html +181 -0
- package/config/pfbs.js +207 -0
- package/config/testhelp.js +0 -0
- package/config/util.js +140 -0
- package/config/webpack.js +291 -0
- package/lib/assets/border.png +0 -0
- package/lib/assets/check.html +62 -0
- package/lib/assets/pixui.png +0 -0
- package/lib/assets/preact.js +4354 -0
- package/lib/assets/preact.png +0 -0
- package/lib/check/main.less +63 -0
- package/lib/check/main.tsx +41 -0
- package/lib/check/tool.js +3 -0
- package/lib/check/util.tsx +110 -0
- package/lib/grpc-web/dist/ChunkParser.js +117 -0
- package/lib/grpc-web/dist/ChunkParser.js.map +1 -0
- package/lib/grpc-web/dist/Code.js +58 -0
- package/lib/grpc-web/dist/Code.js.map +1 -0
- package/lib/grpc-web/dist/client.js +299 -0
- package/lib/grpc-web/dist/client.js.map +1 -0
- package/lib/grpc-web/dist/debug.js +16 -0
- package/lib/grpc-web/dist/debug.js.map +1 -0
- package/lib/grpc-web/dist/detach.js +7 -0
- package/lib/grpc-web/dist/detach.js.map +1 -0
- package/lib/grpc-web/dist/index.js +29 -0
- package/lib/grpc-web/dist/index.js.map +1 -0
- package/lib/grpc-web/dist/invoke.js +32 -0
- package/lib/grpc-web/dist/invoke.js.map +1 -0
- package/lib/grpc-web/dist/message.js +3 -0
- package/lib/grpc-web/dist/message.js.map +1 -0
- package/lib/grpc-web/dist/metadata.js +5 -0
- package/lib/grpc-web/dist/metadata.js.map +1 -0
- package/lib/grpc-web/dist/service.js +3 -0
- package/lib/grpc-web/dist/service.js.map +1 -0
- package/lib/grpc-web/dist/transports/Transport.js +15 -0
- package/lib/grpc-web/dist/transports/Transport.js.map +1 -0
- package/lib/grpc-web/dist/transports/http/fetch.js +117 -0
- package/lib/grpc-web/dist/transports/http/fetch.js.map +1 -0
- package/lib/grpc-web/dist/transports/http/http.js +15 -0
- package/lib/grpc-web/dist/transports/http/http.js.map +1 -0
- package/lib/grpc-web/dist/transports/http/xhr.js +136 -0
- package/lib/grpc-web/dist/transports/http/xhr.js.map +1 -0
- package/lib/grpc-web/dist/transports/http/xhrUtil.js +36 -0
- package/lib/grpc-web/dist/transports/http/xhrUtil.js.map +1 -0
- package/lib/grpc-web/dist/transports/websocket/websocket.js +95 -0
- package/lib/grpc-web/dist/transports/websocket/websocket.js.map +1 -0
- package/lib/grpc-web/dist/typings/ChunkParser.d.ts +17 -0
- package/lib/grpc-web/dist/typings/Code.d.ts +20 -0
- package/lib/grpc-web/dist/typings/client.d.ts +25 -0
- package/lib/grpc-web/dist/typings/debug.d.ts +1 -0
- package/lib/grpc-web/dist/typings/detach.d.ts +1 -0
- package/lib/grpc-web/dist/typings/index.d.ts +45 -0
- package/lib/grpc-web/dist/typings/invoke.d.ts +20 -0
- package/lib/grpc-web/dist/typings/message.d.ts +8 -0
- package/lib/grpc-web/dist/typings/metadata.d.ts +2 -0
- package/lib/grpc-web/dist/typings/service.d.ts +16 -0
- package/lib/grpc-web/dist/typings/transports/Transport.d.ts +22 -0
- package/lib/grpc-web/dist/typings/transports/http/fetch.d.ts +6 -0
- package/lib/grpc-web/dist/typings/transports/http/http.d.ts +5 -0
- package/lib/grpc-web/dist/typings/transports/http/xhr.d.ts +27 -0
- package/lib/grpc-web/dist/typings/transports/http/xhrUtil.d.ts +3 -0
- package/lib/grpc-web/dist/typings/transports/websocket/websocket.d.ts +2 -0
- package/lib/grpc-web/dist/typings/unary.d.ts +23 -0
- package/lib/grpc-web/dist/typings/util.d.ts +2 -0
- package/lib/grpc-web/dist/unary.js +44 -0
- package/lib/grpc-web/dist/unary.js.map +1 -0
- package/lib/grpc-web/dist/util.js +11 -0
- package/lib/grpc-web/dist/util.js.map +1 -0
- package/lib/grpcTransport/PixHttp2Transport.ts +107 -0
- package/lib/grpcTransport/PixLuaTransport.ts +82 -0
- package/lib/h5es-types/v1.9.2/h5es.d.ts +1698 -0
- package/lib/h5es-types/v3.5.0/h5es.d.ts +1788 -0
- package/lib/pi_component/tinyList/tinyList.js +483 -0
- package/lib/pi_component/tinyList/tinyList.tsx +517 -0
- package/lib/pika-svelte/compiler.js +29829 -0
- package/lib/pika-svelte/easing/index.js +158 -0
- package/lib/pika-svelte/index.js +72 -0
- package/lib/pika-svelte/internal/index.js +1926 -0
- package/lib/pika-svelte/motion/index.js +210 -0
- package/lib/pika-svelte/package.json +145 -0
- package/lib/pika-svelte/register.js +57 -0
- package/lib/pika-svelte/rollup.config.js +126 -0
- package/lib/pika-svelte/store/index.js +123 -0
- package/lib/pika-svelte/transition/index.js +185 -0
- package/lib/preact-router.js +395 -0
- package/lib/preact.js +4355 -0
- package/lib/preact.tq.js +4385 -0
- package/lib/react-window/src/FixedSizeGrid.js +172 -0
- package/lib/react-window/src/FixedSizeList.js +91 -0
- package/lib/react-window/src/VariableSizeGrid.js +329 -0
- package/lib/react-window/src/VariableSizeList.js +231 -0
- package/lib/react-window/src/__tests__/FixedSizeGrid.js +942 -0
- package/lib/react-window/src/__tests__/FixedSizeList.js +749 -0
- package/lib/react-window/src/__tests__/VariableSizeGrid.js +598 -0
- package/lib/react-window/src/__tests__/VariableSizeList.js +345 -0
- package/lib/react-window/src/__tests__/__snapshots__/FixedSizeGrid.js.snap +912 -0
- package/lib/react-window/src/__tests__/__snapshots__/FixedSizeList.js.snap +568 -0
- package/lib/react-window/src/__tests__/__snapshots__/VariableSizeGrid.js.snap +542 -0
- package/lib/react-window/src/__tests__/__snapshots__/VariableSizeList.js.snap +331 -0
- package/lib/react-window/src/__tests__/areEqual.js +28 -0
- package/lib/react-window/src/__tests__/shouldComponentUpdate.js +32 -0
- package/lib/react-window/src/areEqual.js +13 -0
- package/lib/react-window/src/createGridComponent.js +657 -0
- package/lib/react-window/src/createListComponent.js +574 -0
- package/lib/react-window/src/domHelpers.js +69 -0
- package/lib/react-window/src/index.js +9 -0
- package/lib/react-window/src/shallowDiffers.js +17 -0
- package/lib/react-window/src/shouldComponentUpdate.js +11 -0
- package/lib/react-window/src/test.js.flow +382 -0
- package/lib/react-window/src/timer.js +36 -0
- package/lib/svelte-tab/Tab.svelte +31 -0
- package/lib/svelte-tab/TabList.svelte +10 -0
- package/lib/svelte-tab/TabPanel.svelte +13 -0
- package/lib/svelte-tab/Tabs.svelte +68 -0
- package/lib/svelte-tab/tabs.js +4 -0
- package/lib/types/css.d.ts +7476 -0
- package/lib/types/dom.ts +17 -0
- package/lib/types/ext.d.ts +81 -0
- package/lib/types/internal.d.ts +94 -0
- package/lib/types/jsx.d.ts +309 -0
- package/lib/types/preact.d.ts +340 -0
- package/package.json +111 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
import React, { createRef, forwardRef, PureComponent } from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
import ReactTestRenderer from 'react-test-renderer';
|
|
4
|
+
import { Simulate } from 'react-dom/test-utils';
|
|
5
|
+
import { FixedSizeList } from '..';
|
|
6
|
+
|
|
7
|
+
const simulateScroll = (instance, scrollOffset, direction = 'vertical') => {
|
|
8
|
+
if (direction === 'horizontal') {
|
|
9
|
+
instance._outerRef.scrollLeft = scrollOffset;
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
instance._outerRef.scrollTop = scrollOffset;
|
|
13
|
+
}
|
|
14
|
+
Simulate.scroll(instance._outerRef);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const findScrollContainer = (rendered) => rendered.root.children[0].children[0];
|
|
18
|
+
|
|
19
|
+
describe('FixedSizeList', () => {
|
|
20
|
+
let itemRenderer, defaultProps, onItemsRendered;
|
|
21
|
+
|
|
22
|
+
// Use PureComponent to test memoization.
|
|
23
|
+
// Pass through to itemRenderer mock for easier test assertions.
|
|
24
|
+
class PureItemRenderer extends PureComponent {
|
|
25
|
+
render() {
|
|
26
|
+
return itemRenderer(this.props);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
jest.useFakeTimers();
|
|
32
|
+
|
|
33
|
+
// JSdom does not do actual layout and so doesn't return meaningful values here.
|
|
34
|
+
// For the purposes of our tests though, we can mock out semi-meaningful values.
|
|
35
|
+
// This mock is required for e.g. "onScroll" tests to work properly.
|
|
36
|
+
Object.defineProperties(HTMLElement.prototype, {
|
|
37
|
+
clientWidth: {
|
|
38
|
+
configurable: true,
|
|
39
|
+
get: function () {
|
|
40
|
+
return parseInt(this.style.width, 10) || 0;
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
clientHeight: {
|
|
44
|
+
configurable: true,
|
|
45
|
+
get: function () {
|
|
46
|
+
return parseInt(this.style.height, 10) || 0;
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
scrollHeight: {
|
|
50
|
+
configurable: true,
|
|
51
|
+
get: () => Number.MAX_SAFE_INTEGER,
|
|
52
|
+
},
|
|
53
|
+
scrollWidth: {
|
|
54
|
+
configurable: true,
|
|
55
|
+
get: () => Number.MAX_SAFE_INTEGER,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
onItemsRendered = jest.fn();
|
|
60
|
+
|
|
61
|
+
itemRenderer = jest.fn(({ style, ...rest }) => <div style={style}>{JSON.stringify(rest, null, 2)}</div>);
|
|
62
|
+
defaultProps = {
|
|
63
|
+
children: PureItemRenderer,
|
|
64
|
+
height: 100,
|
|
65
|
+
itemCount: 100,
|
|
66
|
+
itemSize: 25,
|
|
67
|
+
onItemsRendered,
|
|
68
|
+
width: 50,
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should render an empty list', () => {
|
|
73
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} itemCount={0} />);
|
|
74
|
+
expect(itemRenderer).not.toHaveBeenCalled();
|
|
75
|
+
expect(onItemsRendered).not.toHaveBeenCalled();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should render a list of rows', () => {
|
|
79
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} />);
|
|
80
|
+
expect(itemRenderer).toHaveBeenCalledTimes(6);
|
|
81
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should render a list of columns', () => {
|
|
85
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} layout="horizontal" />);
|
|
86
|
+
expect(itemRenderer).toHaveBeenCalledTimes(4);
|
|
87
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should re-render items if layout changes', () => {
|
|
91
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} layout="vertical" />);
|
|
92
|
+
expect(itemRenderer).toHaveBeenCalled();
|
|
93
|
+
itemRenderer.mockClear();
|
|
94
|
+
|
|
95
|
+
// Re-rendering should not affect pure sCU children:
|
|
96
|
+
rendered.update(<FixedSizeList {...defaultProps} layout="vertical" />);
|
|
97
|
+
expect(itemRenderer).not.toHaveBeenCalled();
|
|
98
|
+
|
|
99
|
+
// Re-rendering with new layout should re-render children:
|
|
100
|
+
rendered.update(<FixedSizeList {...defaultProps} layout="horizontal" />);
|
|
101
|
+
expect(itemRenderer).toHaveBeenCalled();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// TODO Deprecate direction "horizontal"
|
|
105
|
+
it('should re-render items if direction changes', () => {
|
|
106
|
+
spyOn(console, 'warn'); // Ingore legacy prop warning
|
|
107
|
+
|
|
108
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} direction="vertical" />);
|
|
109
|
+
expect(itemRenderer).toHaveBeenCalled();
|
|
110
|
+
itemRenderer.mockClear();
|
|
111
|
+
|
|
112
|
+
// Re-rendering should not affect pure sCU children:
|
|
113
|
+
rendered.update(<FixedSizeList {...defaultProps} direction="vertical" />);
|
|
114
|
+
expect(itemRenderer).not.toHaveBeenCalled();
|
|
115
|
+
|
|
116
|
+
// Re-rendering with new layout should re-render children:
|
|
117
|
+
rendered.update(<FixedSizeList {...defaultProps} direction="horizontal" />);
|
|
118
|
+
expect(itemRenderer).toHaveBeenCalled();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('scrollbar handling', () => {
|
|
122
|
+
it('should set width to "100%" for vertical lists to avoid unnecessary horizontal scrollbar', () => {
|
|
123
|
+
const innerRef = createRef();
|
|
124
|
+
ReactDOM.render(<FixedSizeList {...defaultProps} innerRef={innerRef} />, document.createElement('div'));
|
|
125
|
+
const style = innerRef.current.style;
|
|
126
|
+
expect(style.width).toBe('100%');
|
|
127
|
+
expect(style.height).toBe('2500px');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should set height to "100%" for horizontal lists to avoid unnecessary vertical scrollbar', () => {
|
|
131
|
+
const innerRef = createRef();
|
|
132
|
+
ReactDOM.render(<FixedSizeList {...defaultProps} layout="horizontal" innerRef={innerRef} />, document.createElement('div'));
|
|
133
|
+
const style = innerRef.current.style;
|
|
134
|
+
expect(style.width).toBe('2500px');
|
|
135
|
+
expect(style.height).toBe('100%');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('style caching', () => {
|
|
140
|
+
it('should cache styles while scrolling to avoid breaking pure sCU for items', () => {
|
|
141
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} />);
|
|
142
|
+
// Scroll a few times.
|
|
143
|
+
// Each time, make sure to render item 3.
|
|
144
|
+
rendered.getInstance().scrollToItem(1, 'start');
|
|
145
|
+
rendered.getInstance().scrollToItem(2, 'start');
|
|
146
|
+
rendered.getInstance().scrollToItem(3, 'start');
|
|
147
|
+
// Find all of the times item 3 was rendered.
|
|
148
|
+
// If we are caching props correctly, it should only be once.
|
|
149
|
+
expect(itemRenderer.mock.calls.filter(([params]) => params.index === 3)).toHaveLength(1);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should reset cached styles when scrolling stops', () => {
|
|
153
|
+
// Use ReactDOM renderer so the container ref and "onScroll" work correctly.
|
|
154
|
+
const instance = ReactDOM.render(<FixedSizeList {...defaultProps} useIsScrolling />, document.createElement('div'));
|
|
155
|
+
// Scroll, then capture the rendered style for item 1,
|
|
156
|
+
// Then let the debounce timer clear the cached styles.
|
|
157
|
+
simulateScroll(instance, 251);
|
|
158
|
+
const itemOneArgsA = itemRenderer.mock.calls.find(([params]) => params.index === 1);
|
|
159
|
+
jest.runAllTimers();
|
|
160
|
+
itemRenderer.mockClear();
|
|
161
|
+
// Scroll again, then capture the rendered style for item 1,
|
|
162
|
+
// And confirm that the style was recreated.
|
|
163
|
+
simulateScroll(instance, 0);
|
|
164
|
+
const itemOneArgsB = itemRenderer.mock.calls.find(([params]) => params.index === 1);
|
|
165
|
+
expect(itemOneArgsA[0].style).not.toBe(itemOneArgsB[0].style);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('changing itemSize updates the rendered items', () => {
|
|
170
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} />);
|
|
171
|
+
rendered.update(<FixedSizeList {...defaultProps} itemSize={50} />);
|
|
172
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('changing itemSize updates the rendered items and busts the style cache', () => {
|
|
176
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} />);
|
|
177
|
+
const oldStyle = itemRenderer.mock.calls[0][0].style;
|
|
178
|
+
itemRenderer.mockClear();
|
|
179
|
+
rendered.update(<FixedSizeList {...defaultProps} itemSize={50} />);
|
|
180
|
+
expect(itemRenderer).toHaveBeenCalled();
|
|
181
|
+
const newStyle = itemRenderer.mock.calls[0][0].style;
|
|
182
|
+
expect(oldStyle).not.toBe(newStyle);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should support momentum scrolling on iOS devices', () => {
|
|
186
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} />);
|
|
187
|
+
expect(rendered.toJSON().props.style.WebkitOverflowScrolling).toBe('touch');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should disable pointer events while scrolling', () => {
|
|
191
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} />);
|
|
192
|
+
const scrollContainer = findScrollContainer(rendered);
|
|
193
|
+
expect(scrollContainer.props.style.pointerEvents).toBe(undefined);
|
|
194
|
+
rendered.getInstance().setState({ isScrolling: true });
|
|
195
|
+
expect(scrollContainer.props.style.pointerEvents).toBe('none');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe('style overrides', () => {
|
|
199
|
+
it('should support className prop', () => {
|
|
200
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} className="custom" />);
|
|
201
|
+
expect(rendered.toJSON().props.className).toBe('custom');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should support style prop', () => {
|
|
205
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} style={{ backgroundColor: 'red' }} />);
|
|
206
|
+
expect(rendered.toJSON().props.style.backgroundColor).toBe('red');
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe('direction', () => {
|
|
211
|
+
it('should set the appropriate CSS direction style', () => {
|
|
212
|
+
const renderer = ReactTestRenderer.create(<FixedSizeList {...defaultProps} direction="ltr" />);
|
|
213
|
+
expect(renderer.toJSON().props.style.direction).toBe('ltr');
|
|
214
|
+
renderer.update(<FixedSizeList {...defaultProps} direction="rtl" />);
|
|
215
|
+
expect(renderer.toJSON().props.style.direction).toBe('rtl');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should position items correctly', () => {
|
|
219
|
+
const renderer = ReactTestRenderer.create(<FixedSizeList {...defaultProps} direction="ltr" />);
|
|
220
|
+
|
|
221
|
+
let params = itemRenderer.mock.calls[0][0];
|
|
222
|
+
expect(params.index).toBe(0);
|
|
223
|
+
let style = params.style;
|
|
224
|
+
expect(style.left).toBe(0);
|
|
225
|
+
expect(style.right).toBeUndefined();
|
|
226
|
+
|
|
227
|
+
itemRenderer.mockClear();
|
|
228
|
+
|
|
229
|
+
renderer.update(<FixedSizeList {...defaultProps} direction="rtl" />);
|
|
230
|
+
|
|
231
|
+
params = itemRenderer.mock.calls[0][0];
|
|
232
|
+
expect(params.index).toBe(0);
|
|
233
|
+
style = params.style;
|
|
234
|
+
expect(style.left).toBeUndefined();
|
|
235
|
+
expect(style.right).toBe(0);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe('overscanCount', () => {
|
|
240
|
+
it('should require a minimum of 1 overscan to support tabbing', () => {
|
|
241
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} initialScrollOffset={50} overscanCount={0} />);
|
|
242
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should overscan in the direction being scrolled', () => {
|
|
246
|
+
const instance = ReactDOM.render(<FixedSizeList {...defaultProps} initialScrollOffset={50} overscanCount={2} />, document.createElement('div'));
|
|
247
|
+
// Simulate scrolling (rather than using scrollTo) to test isScrolling state.
|
|
248
|
+
simulateScroll(instance, 100);
|
|
249
|
+
simulateScroll(instance, 50);
|
|
250
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should overscan in both directions when not scrolling', () => {
|
|
254
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} initialScrollOffset={50} />);
|
|
255
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('should accommodate a custom overscan', () => {
|
|
259
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} initialScrollOffset={100} overscanCount={3} />);
|
|
260
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should not scan past the beginning of the list', () => {
|
|
264
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} initialScrollOffset={0} />);
|
|
265
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should not scan past the end of the list', () => {
|
|
269
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} itemCount={10} initialScrollOffset={150} />);
|
|
270
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
describe('useIsScrolling', () => {
|
|
275
|
+
it('should not pass an isScrolling param to children unless requested', () => {
|
|
276
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} />);
|
|
277
|
+
expect(itemRenderer.mock.calls[0][0].isScrolling).toBe(undefined);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('should pass an isScrolling param to children if requested', () => {
|
|
281
|
+
// Use ReactDOM renderer so the container ref and "onScroll" work correctly.
|
|
282
|
+
const instance = ReactDOM.render(<FixedSizeList {...defaultProps} useIsScrolling />, document.createElement('div'));
|
|
283
|
+
expect(itemRenderer.mock.calls[0][0].isScrolling).toBe(false);
|
|
284
|
+
itemRenderer.mockClear();
|
|
285
|
+
simulateScroll(instance, 100);
|
|
286
|
+
expect(itemRenderer.mock.calls[0][0].isScrolling).toBe(true);
|
|
287
|
+
itemRenderer.mockClear();
|
|
288
|
+
jest.runAllTimers();
|
|
289
|
+
expect(itemRenderer.mock.calls[0][0].isScrolling).toBe(false);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should not re-render children unnecessarily if isScrolling param is not used', () => {
|
|
293
|
+
// Use ReactDOM renderer so the container ref and "onScroll" work correctly.
|
|
294
|
+
const instance = ReactDOM.render(<FixedSizeList {...defaultProps} overscanCount={1} />, document.createElement('div'));
|
|
295
|
+
simulateScroll(instance, 100);
|
|
296
|
+
itemRenderer.mockClear();
|
|
297
|
+
jest.runAllTimers();
|
|
298
|
+
expect(itemRenderer).not.toHaveBeenCalled();
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe('scrollTo method', () => {
|
|
303
|
+
it('should not report isScrolling', () => {
|
|
304
|
+
// Use ReactDOM renderer so the container ref and "onScroll" work correctly.
|
|
305
|
+
const instance = ReactDOM.render(<FixedSizeList {...defaultProps} useIsScrolling />, document.createElement('div'));
|
|
306
|
+
itemRenderer.mockClear();
|
|
307
|
+
instance.scrollTo(100);
|
|
308
|
+
expect(itemRenderer.mock.calls[0][0].isScrolling).toBe(false);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should ignore values less than zero', () => {
|
|
312
|
+
const onScroll = jest.fn();
|
|
313
|
+
const instance = ReactDOM.render(<FixedSizeList {...defaultProps} onScroll={onScroll} />, document.createElement('div'));
|
|
314
|
+
instance.scrollTo(100);
|
|
315
|
+
onScroll.mockClear();
|
|
316
|
+
instance.scrollTo(-1);
|
|
317
|
+
expect(onScroll.mock.calls[0][0].scrollOffset).toBe(0);
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
describe('scrollToItem method', () => {
|
|
322
|
+
it('should not set invalid offsets when the list contains few items', () => {
|
|
323
|
+
const onScroll = jest.fn();
|
|
324
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} itemCount={3} onScroll={onScroll} />);
|
|
325
|
+
expect(onItemsRendered).toMatchSnapshot();
|
|
326
|
+
onItemsRendered.mockClear();
|
|
327
|
+
rendered.getInstance().scrollToItem(0);
|
|
328
|
+
expect(onItemsRendered).not.toHaveBeenCalled();
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('should scroll to the correct item for align = "auto"', () => {
|
|
332
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} />);
|
|
333
|
+
// Scroll down enough to show item 10 at the bottom.
|
|
334
|
+
rendered.getInstance().scrollToItem(10, 'auto');
|
|
335
|
+
// No need to scroll again; item 9 is already visible.
|
|
336
|
+
// Because there's no scrolling, it won't call onItemsRendered.
|
|
337
|
+
rendered.getInstance().scrollToItem(9, 'auto');
|
|
338
|
+
// Scroll up enough to show item 2 at the top.
|
|
339
|
+
rendered.getInstance().scrollToItem(2, 'auto');
|
|
340
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('scroll with align = "auto" should work with partially-visible items', () => {
|
|
344
|
+
const rendered = ReactTestRenderer.create(
|
|
345
|
+
// Create list where items don't fit exactly into container.
|
|
346
|
+
// The container has space for 3 1/3 items.
|
|
347
|
+
<FixedSizeList {...defaultProps} itemSize={30} />,
|
|
348
|
+
);
|
|
349
|
+
// Scroll down enough to show item 10 at the bottom.
|
|
350
|
+
// Should show 4 items: 3 full and one partial at the beginning
|
|
351
|
+
rendered.getInstance().scrollToItem(10, 'auto');
|
|
352
|
+
// No need to scroll again; item 9 is already visible.
|
|
353
|
+
// Because there's no scrolling, it won't call onItemsRendered.
|
|
354
|
+
rendered.getInstance().scrollToItem(9, 'auto');
|
|
355
|
+
// Scroll to near the end. #96 will be shown as partial.
|
|
356
|
+
rendered.getInstance().scrollToItem(99, 'auto');
|
|
357
|
+
// Scroll back to show #96 fully. This will cause #99 to be shown as a
|
|
358
|
+
// partial. Because #96 was already shown previously as a partial, all
|
|
359
|
+
// props of the onItemsRendered will be the same. This means that even
|
|
360
|
+
// though a scroll happened in the DOM, onItemsRendered won't be called.
|
|
361
|
+
rendered.getInstance().scrollToItem(96, 'auto');
|
|
362
|
+
// Scroll forward again. Because item #99 was already shown partially,
|
|
363
|
+
// all props of the onItemsRendered will be the same.
|
|
364
|
+
rendered.getInstance().scrollToItem(99, 'auto');
|
|
365
|
+
// Scroll to the second item. A partial fifth item should
|
|
366
|
+
// be shown after it.
|
|
367
|
+
rendered.getInstance().scrollToItem(1, 'auto');
|
|
368
|
+
// Scroll to the first item. Now the fourth item should be a partial.
|
|
369
|
+
rendered.getInstance().scrollToItem(0, 'auto');
|
|
370
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('scroll with align = "auto" should work with very small lists and partial items', () => {
|
|
374
|
+
const rendered = ReactTestRenderer.create(
|
|
375
|
+
// Create list with only two items, one of which will be shown as a partial.
|
|
376
|
+
<FixedSizeList {...defaultProps} itemSize={60} itemCount={2} />,
|
|
377
|
+
);
|
|
378
|
+
// Show the second item fully. The first item should be a partial.
|
|
379
|
+
rendered.getInstance().scrollToItem(1, 'auto');
|
|
380
|
+
// Go back to the first item. The second should be a partial again.
|
|
381
|
+
rendered.getInstance().scrollToItem(0, 'auto');
|
|
382
|
+
// None of the scrollToItem calls above should actually cause a scroll,
|
|
383
|
+
// so there will only be one snapshot.
|
|
384
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('should scroll to the correct item for align = "start"', () => {
|
|
388
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} />);
|
|
389
|
+
// Scroll down enough to show item 10 at the top.
|
|
390
|
+
rendered.getInstance().scrollToItem(10, 'start');
|
|
391
|
+
// Scroll back up so that item 9 is at the top.
|
|
392
|
+
// Overscroll direction wil change too.
|
|
393
|
+
rendered.getInstance().scrollToItem(9, 'start');
|
|
394
|
+
// Item 99 can't align at the top because there aren't enough items.
|
|
395
|
+
// Scroll down as far as possible though.
|
|
396
|
+
// Overscroll direction wil change again.
|
|
397
|
+
rendered.getInstance().scrollToItem(99, 'start');
|
|
398
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('should scroll to the correct item for align = "end"', () => {
|
|
402
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} />);
|
|
403
|
+
// Scroll down enough to show item 10 at the bottom.
|
|
404
|
+
rendered.getInstance().scrollToItem(10, 'end');
|
|
405
|
+
// Scroll back up so that item 9 is at the bottom.
|
|
406
|
+
// Overscroll direction wil change too.
|
|
407
|
+
rendered.getInstance().scrollToItem(9, 'end');
|
|
408
|
+
// Item 1 can't align at the bottom because it's too close to the beginning.
|
|
409
|
+
// Scroll up as far as possible though.
|
|
410
|
+
// Overscroll direction wil change again.
|
|
411
|
+
rendered.getInstance().scrollToItem(1, 'end');
|
|
412
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('should scroll to the correct item for align = "center"', () => {
|
|
416
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} />);
|
|
417
|
+
// Scroll down enough to show item 10 in the middle.
|
|
418
|
+
rendered.getInstance().scrollToItem(10, 'center');
|
|
419
|
+
// Scroll back up so that item 9 is in the middle.
|
|
420
|
+
// Overscroll direction wil change too.
|
|
421
|
+
rendered.getInstance().scrollToItem(9, 'center');
|
|
422
|
+
// Item 1 can't align in the middle because it's too close to the beginning.
|
|
423
|
+
// Scroll up as far as possible though.
|
|
424
|
+
// Overscroll direction wil change again.
|
|
425
|
+
rendered.getInstance().scrollToItem(1, 'center');
|
|
426
|
+
// Item 99 can't align in the middle because it's too close to the end.
|
|
427
|
+
// Scroll down as far as possible though.
|
|
428
|
+
// Overscroll direction wil change again.
|
|
429
|
+
rendered.getInstance().scrollToItem(99, 'center');
|
|
430
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it('should scroll to the correct item for align = "smart"', () => {
|
|
434
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} />);
|
|
435
|
+
// Scroll down enough to show item 10 in the middle.
|
|
436
|
+
rendered.getInstance().scrollToItem(10, 'smart');
|
|
437
|
+
// Scrolldn't scroll at all because it's close enough.
|
|
438
|
+
rendered.getInstance().scrollToItem(9, 'smart');
|
|
439
|
+
// Should scroll but not center because it's close enough.
|
|
440
|
+
rendered.getInstance().scrollToItem(6, 'smart');
|
|
441
|
+
// Item 1 can't align in the middle because it's too close to the beginning.
|
|
442
|
+
// Scroll up as far as possible though.
|
|
443
|
+
rendered.getInstance().scrollToItem(1, 'smart');
|
|
444
|
+
// Item 99 can't align in the middle because it's too close to the end.
|
|
445
|
+
// Scroll down as far as possible though.
|
|
446
|
+
rendered.getInstance().scrollToItem(99, 'smart');
|
|
447
|
+
// This shouldn't scroll at all because it's close enough.
|
|
448
|
+
rendered.getInstance().scrollToItem(95, 'smart');
|
|
449
|
+
rendered.getInstance().scrollToItem(99, 'smart');
|
|
450
|
+
// This should scroll with the 'auto' behavior because it's within a screen.
|
|
451
|
+
rendered.getInstance().scrollToItem(94, 'smart');
|
|
452
|
+
rendered.getInstance().scrollToItem(99, 'smart');
|
|
453
|
+
// This should scroll with the 'center' behavior because it's too far.
|
|
454
|
+
rendered.getInstance().scrollToItem(90, 'smart');
|
|
455
|
+
rendered.getInstance().scrollToItem(99, 'smart');
|
|
456
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('should not report isScrolling', () => {
|
|
460
|
+
// Use ReactDOM renderer so the container ref and "onScroll" work correctly.
|
|
461
|
+
const instance = ReactDOM.render(<FixedSizeList {...defaultProps} useIsScrolling />, document.createElement('div'));
|
|
462
|
+
itemRenderer.mockClear();
|
|
463
|
+
instance.scrollToItem(15);
|
|
464
|
+
expect(itemRenderer.mock.calls[0][0].isScrolling).toBe(false);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('should ignore indexes less than zero', () => {
|
|
468
|
+
const instance = ReactDOM.render(<FixedSizeList {...defaultProps} />, document.createElement('div'));
|
|
469
|
+
instance.scrollToItem(20);
|
|
470
|
+
onItemsRendered.mockClear();
|
|
471
|
+
instance.scrollToItem(-1);
|
|
472
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('should ignore indexes greater than itemCount', () => {
|
|
476
|
+
const instance = ReactDOM.render(<FixedSizeList {...defaultProps} />, document.createElement('div'));
|
|
477
|
+
onItemsRendered.mockClear();
|
|
478
|
+
instance.scrollToItem(defaultProps.itemCount * 2);
|
|
479
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
// onItemsRendered is pretty well covered by other snapshot tests
|
|
484
|
+
describe('onScroll', () => {
|
|
485
|
+
it('should call onScroll after mount', () => {
|
|
486
|
+
const onScroll = jest.fn();
|
|
487
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} onScroll={onScroll} />);
|
|
488
|
+
expect(onScroll.mock.calls).toMatchSnapshot();
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it('should call onScroll when scroll position changes', () => {
|
|
492
|
+
const onScroll = jest.fn();
|
|
493
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} onScroll={onScroll} />);
|
|
494
|
+
rendered.getInstance().scrollTo(100);
|
|
495
|
+
rendered.getInstance().scrollTo(0);
|
|
496
|
+
expect(onScroll.mock.calls).toMatchSnapshot();
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it('should distinguish between "onScroll" events and scrollTo() calls', () => {
|
|
500
|
+
const onScroll = jest.fn();
|
|
501
|
+
// Use ReactDOM renderer so the container ref and "onScroll" event work correctly.
|
|
502
|
+
const instance = ReactDOM.render(<FixedSizeList {...defaultProps} onScroll={onScroll} />, document.createElement('div'));
|
|
503
|
+
|
|
504
|
+
onScroll.mockClear();
|
|
505
|
+
instance.scrollTo(100);
|
|
506
|
+
expect(onScroll.mock.calls[0][0].scrollUpdateWasRequested).toBe(true);
|
|
507
|
+
|
|
508
|
+
onScroll.mockClear();
|
|
509
|
+
simulateScroll(instance, 200);
|
|
510
|
+
expect(onScroll.mock.calls[0][0].scrollUpdateWasRequested).toBe(false);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('scrolling should report partial items correctly in onItemsRendered', () => {
|
|
514
|
+
// Use ReactDOM renderer so the container ref works correctly.
|
|
515
|
+
const instance = ReactDOM.render(<FixedSizeList {...defaultProps} initialScrollOffset={20} />, document.createElement('div'));
|
|
516
|
+
// Scroll 2 items fwd, but thanks to the initialScrollOffset, we should
|
|
517
|
+
// still be showing partials on both ends.
|
|
518
|
+
simulateScroll(instance, 70);
|
|
519
|
+
// Scroll a little fwd to cause partials to be hidden
|
|
520
|
+
simulateScroll(instance, 75);
|
|
521
|
+
// Scroll backwards to show partials again
|
|
522
|
+
simulateScroll(instance, 70);
|
|
523
|
+
// Scroll near the end so that the last item is shown
|
|
524
|
+
// as a partial.
|
|
525
|
+
simulateScroll(instance, 96 * 25 - 5);
|
|
526
|
+
// Scroll to the end. No partials.
|
|
527
|
+
simulateScroll(instance, 96 * 25);
|
|
528
|
+
// Verify that backwards scrolling near the end works OK.
|
|
529
|
+
simulateScroll(instance, 96 * 25 - 5);
|
|
530
|
+
expect(onItemsRendered.mock.calls).toMatchSnapshot();
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
describe('itemKey', () => {
|
|
535
|
+
it('should be used', () => {
|
|
536
|
+
const itemKey = jest.fn((index) => index);
|
|
537
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} itemCount={3} itemKey={itemKey} />);
|
|
538
|
+
expect(itemKey).toHaveBeenCalledTimes(3);
|
|
539
|
+
expect(itemKey.mock.calls[0][0]).toBe(0);
|
|
540
|
+
expect(itemKey.mock.calls[1][0]).toBe(1);
|
|
541
|
+
expect(itemKey.mock.calls[2][0]).toBe(2);
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('should allow items to be moved within the collection without causing caching problems', () => {
|
|
545
|
+
const keyMap = ['0', '1', '2'];
|
|
546
|
+
const keyMapItemRenderer = jest.fn(({ index, style }) => <div style={style}>{keyMap[index]}</div>);
|
|
547
|
+
class ItemRenderer extends PureComponent {
|
|
548
|
+
render() {
|
|
549
|
+
return keyMapItemRenderer(this.props);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const itemKey = jest.fn((index) => keyMap[index]);
|
|
553
|
+
const rendered = ReactTestRenderer.create(
|
|
554
|
+
<FixedSizeList {...defaultProps} itemCount={3} itemKey={itemKey}>
|
|
555
|
+
{ItemRenderer}
|
|
556
|
+
</FixedSizeList>,
|
|
557
|
+
);
|
|
558
|
+
expect(itemKey).toHaveBeenCalledTimes(3);
|
|
559
|
+
itemKey.mockClear();
|
|
560
|
+
|
|
561
|
+
expect(keyMapItemRenderer).toHaveBeenCalledTimes(3);
|
|
562
|
+
keyMapItemRenderer.mockClear();
|
|
563
|
+
|
|
564
|
+
// Simulate swapping the first and last items.
|
|
565
|
+
keyMap[0] = '2';
|
|
566
|
+
keyMap[2] = '0';
|
|
567
|
+
|
|
568
|
+
rendered.getInstance().forceUpdate();
|
|
569
|
+
|
|
570
|
+
// Our key getter should be called again for each key.
|
|
571
|
+
// Since we've modified the map, the first and last key will swap.
|
|
572
|
+
expect(itemKey).toHaveBeenCalledTimes(3);
|
|
573
|
+
|
|
574
|
+
// The first and third item have swapped place,
|
|
575
|
+
// So they should have been re-rendered,
|
|
576
|
+
// But the second item should not.
|
|
577
|
+
expect(keyMapItemRenderer).toHaveBeenCalledTimes(2);
|
|
578
|
+
expect(keyMapItemRenderer.mock.calls[0][0].index).toBe(0);
|
|
579
|
+
expect(keyMapItemRenderer.mock.calls[1][0].index).toBe(2);
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it('should receive a data value if itemData is provided', () => {
|
|
583
|
+
const itemKey = jest.fn((index) => index);
|
|
584
|
+
const itemData = {};
|
|
585
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} itemData={itemData} itemKey={itemKey} />);
|
|
586
|
+
expect(itemKey).toHaveBeenCalled();
|
|
587
|
+
expect(itemKey.mock.calls.filter(([index, data]) => data === itemData)).toHaveLength(itemKey.mock.calls.length);
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
describe('refs', () => {
|
|
592
|
+
it('should pass through innerRef and outerRef ref functions', () => {
|
|
593
|
+
const innerRef = jest.fn();
|
|
594
|
+
const outerRef = jest.fn();
|
|
595
|
+
ReactDOM.render(<FixedSizeList {...defaultProps} innerRef={innerRef} outerRef={outerRef} />, document.createElement('div'));
|
|
596
|
+
expect(innerRef).toHaveBeenCalled();
|
|
597
|
+
expect(innerRef.mock.calls[0][0]).toBeInstanceOf(HTMLDivElement);
|
|
598
|
+
expect(outerRef).toHaveBeenCalled();
|
|
599
|
+
expect(outerRef.mock.calls[0][0]).toBeInstanceOf(HTMLDivElement);
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it('should pass through innerRef and outerRef createRef objects', () => {
|
|
603
|
+
const innerRef = createRef();
|
|
604
|
+
const outerRef = createRef();
|
|
605
|
+
ReactDOM.render(<FixedSizeList {...defaultProps} innerRef={innerRef} outerRef={outerRef} />, document.createElement('div'));
|
|
606
|
+
expect(innerRef.current).toBeInstanceOf(HTMLDivElement);
|
|
607
|
+
expect(outerRef.current).toBeInstanceOf(HTMLDivElement);
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
describe('custom element types', () => {
|
|
612
|
+
it('should use a custom innerElementType if specified', () => {
|
|
613
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} innerElementType="section" />);
|
|
614
|
+
expect(rendered.root.findByType('section')).toBeDefined();
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
it('should use a custom outerElementType if specified', () => {
|
|
618
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} outerElementType="section" />);
|
|
619
|
+
expect(rendered.root.findByType('section')).toBeDefined();
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it('should support spreading additional, arbitrary props, e.g. id', () => {
|
|
623
|
+
const container = document.createElement('div');
|
|
624
|
+
ReactDOM.render(
|
|
625
|
+
<FixedSizeList
|
|
626
|
+
{...defaultProps}
|
|
627
|
+
innerElementType={forwardRef((props, ref) => (
|
|
628
|
+
<div ref={ref} id="inner" {...props} />
|
|
629
|
+
))}
|
|
630
|
+
outerElementType={forwardRef((props, ref) => (
|
|
631
|
+
<div ref={ref} id="outer" {...props} />
|
|
632
|
+
))}
|
|
633
|
+
/>,
|
|
634
|
+
container,
|
|
635
|
+
);
|
|
636
|
+
expect(container.firstChild.id).toBe('outer');
|
|
637
|
+
expect(container.firstChild.firstChild.id).toBe('inner');
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it('should warn if legacy innerTagName or outerTagName props are used', () => {
|
|
641
|
+
spyOn(console, 'warn');
|
|
642
|
+
|
|
643
|
+
const renderer = ReactTestRenderer.create(<FixedSizeList {...defaultProps} innerTagName="div" outerTagName="div" />);
|
|
644
|
+
expect(console.warn).toHaveBeenCalledTimes(1);
|
|
645
|
+
expect(console.warn).toHaveBeenLastCalledWith('The innerTagName and outerTagName props have been deprecated. ' + 'Please use the innerElementType and outerElementType props instead.');
|
|
646
|
+
|
|
647
|
+
renderer.update(<FixedSizeList {...defaultProps} innerTagName="div" outerTagName="div" />);
|
|
648
|
+
|
|
649
|
+
// But it should only warn once.
|
|
650
|
+
expect(console.warn).toHaveBeenCalledTimes(1);
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
it('should warn if legacy direction "horizontal" value is used', () => {
|
|
654
|
+
spyOn(console, 'warn');
|
|
655
|
+
|
|
656
|
+
const renderer = ReactTestRenderer.create(<FixedSizeList {...defaultProps} direction="horizontal" />);
|
|
657
|
+
expect(console.warn).toHaveBeenCalledTimes(1);
|
|
658
|
+
expect(console.warn).toHaveBeenLastCalledWith(
|
|
659
|
+
'The direction prop should be either "ltr" (default) or "rtl". ' + 'Please use the layout prop to specify "vertical" (default) or "horizontal" orientation.',
|
|
660
|
+
);
|
|
661
|
+
|
|
662
|
+
renderer.update(<FixedSizeList {...defaultProps} direction="horizontal" />);
|
|
663
|
+
|
|
664
|
+
// But it should only warn once.
|
|
665
|
+
expect(console.warn).toHaveBeenCalledTimes(1);
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
it('should warn if legacy direction "vertical" value is used', () => {
|
|
669
|
+
spyOn(console, 'warn');
|
|
670
|
+
|
|
671
|
+
const renderer = ReactTestRenderer.create(<FixedSizeList {...defaultProps} direction="vertical" />);
|
|
672
|
+
expect(console.warn).toHaveBeenCalledTimes(1);
|
|
673
|
+
expect(console.warn).toHaveBeenLastCalledWith(
|
|
674
|
+
'The direction prop should be either "ltr" (default) or "rtl". ' + 'Please use the layout prop to specify "vertical" (default) or "horizontal" orientation.',
|
|
675
|
+
);
|
|
676
|
+
|
|
677
|
+
renderer.update(<FixedSizeList {...defaultProps} direction="vertical" />);
|
|
678
|
+
|
|
679
|
+
// But it should only warn once.
|
|
680
|
+
expect(console.warn).toHaveBeenCalledTimes(1);
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
describe('itemData', () => {
|
|
685
|
+
it('should pass itemData to item renderers as a "data" prop', () => {
|
|
686
|
+
const itemData = {};
|
|
687
|
+
ReactTestRenderer.create(<FixedSizeList {...defaultProps} itemData={itemData} />);
|
|
688
|
+
expect(itemRenderer).toHaveBeenCalled();
|
|
689
|
+
expect(itemRenderer.mock.calls.filter(([params]) => params.data === itemData)).toHaveLength(itemRenderer.mock.calls.length);
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('should re-render items if itemData changes', () => {
|
|
693
|
+
const itemData = {};
|
|
694
|
+
const rendered = ReactTestRenderer.create(<FixedSizeList {...defaultProps} itemData={itemData} />);
|
|
695
|
+
expect(itemRenderer).toHaveBeenCalled();
|
|
696
|
+
itemRenderer.mockClear();
|
|
697
|
+
|
|
698
|
+
// Re-rendering should not affect pure sCU children:
|
|
699
|
+
rendered.update(<FixedSizeList {...defaultProps} itemData={itemData} />);
|
|
700
|
+
expect(itemRenderer).not.toHaveBeenCalled();
|
|
701
|
+
|
|
702
|
+
// Re-rendering with new itemData should re-render children:
|
|
703
|
+
const newItemData = {};
|
|
704
|
+
rendered.update(<FixedSizeList {...defaultProps} itemData={newItemData} />);
|
|
705
|
+
expect(itemRenderer).toHaveBeenCalled();
|
|
706
|
+
expect(itemRenderer.mock.calls.filter(([params]) => params.data === newItemData)).toHaveLength(itemRenderer.mock.calls.length);
|
|
707
|
+
});
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
describe('props validation', () => {
|
|
711
|
+
beforeEach(() => spyOn(console, 'error'));
|
|
712
|
+
|
|
713
|
+
it('should fail if non-numeric itemSize is provided', () => {
|
|
714
|
+
expect(() => ReactTestRenderer.create(<FixedSizeList {...defaultProps} itemSize="abc" />)).toThrow(
|
|
715
|
+
'An invalid "itemSize" prop has been specified. ' + 'Value should be a number. ' + '"string" was specified.',
|
|
716
|
+
);
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
it('should fail if no children value is provided', () => {
|
|
720
|
+
expect(() => ReactTestRenderer.create(<FixedSizeList {...defaultProps} children={undefined} />)).toThrow(
|
|
721
|
+
'An invalid "children" prop has been specified. ' + 'Value should be a React component. ' + '"undefined" was specified.',
|
|
722
|
+
);
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
it('should fail if an invalid layout is provided', () => {
|
|
726
|
+
expect(() => ReactTestRenderer.create(<FixedSizeList {...defaultProps} layout={null} />)).toThrow(
|
|
727
|
+
'An invalid "layout" prop has been specified. ' + 'Value should be either "horizontal" or "vertical". ' + '"null" was specified.',
|
|
728
|
+
);
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
it('should fail if an invalid direction is provided', () => {
|
|
732
|
+
expect(() => ReactTestRenderer.create(<FixedSizeList {...defaultProps} direction={null} />)).toThrow(
|
|
733
|
+
'An invalid "direction" prop has been specified. ' + 'Value should be either "ltr" or "rtl". ' + '"null" was specified.',
|
|
734
|
+
);
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
it('should fail if a string height is provided for a vertical list', () => {
|
|
738
|
+
expect(() => ReactTestRenderer.create(<FixedSizeList {...defaultProps} layout="vertical" height="100%" />)).toThrow(
|
|
739
|
+
'An invalid "height" prop has been specified. ' + 'Vertical lists must specify a number for height. ' + '"string" was specified.',
|
|
740
|
+
);
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
it('should fail if a string width is provided for a horizontal list', () => {
|
|
744
|
+
expect(() => ReactTestRenderer.create(<FixedSizeList {...defaultProps} layout="horizontal" width="100%" />)).toThrow(
|
|
745
|
+
'An invalid "width" prop has been specified. ' + 'Horizontal lists must specify a number for width. ' + '"string" was specified.',
|
|
746
|
+
);
|
|
747
|
+
});
|
|
748
|
+
});
|
|
749
|
+
});
|