@rific/scroll-view 0.3.0 → 0.4.0
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/README.md +2 -0
- package/dist/index.d.mts +16 -14
- package/dist/index.d.ts +16 -14
- package/dist/index.js +14 -1575
- package/dist/index.mjs +14 -1577
- package/package.json +10 -10
- package/src/redux/scrollViewSlice.ts +19 -11
- package/src/__mocks__/auto-paper.ts +0 -9
- package/src/__mocks__/flash-list.ts +0 -20
- package/src/__mocks__/react-native-gesture-handler.ts +0 -13
- package/src/__mocks__/react-native-keyboard-controller.ts +0 -11
- package/src/__mocks__/react-native-paper.ts +0 -22
- package/src/__mocks__/react-native-reanimated.ts +0 -61
- package/src/__mocks__/react-native-safe-area-context.ts +0 -2
- package/src/__mocks__/react-native.ts +0 -30
- package/src/__tests__/CustomList.test.tsx +0 -61
- package/src/__tests__/FlatList.test.tsx +0 -135
- package/src/__tests__/PullSearch.test.tsx +0 -38
- package/src/__tests__/ScrollView.test.tsx +0 -23
- package/src/__tests__/ScrollViewHeader.test.tsx +0 -28
- package/src/__tests__/ScrollViewProvider.test.tsx +0 -65
- package/src/__tests__/SectionList.test.tsx +0 -88
- package/src/__tests__/useScrollHandlerJS.test.ts +0 -119
- package/src/__tests__/useScrollInit.test.ts +0 -251
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rific/scroll-view",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Blur styled scroll system for React Native. Floating headers/footers, keyboard awareness, and scroll-away chrome",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"types": "dist/index.d.ts",
|
|
40
40
|
"files": [
|
|
41
41
|
"dist",
|
|
42
|
-
"src"
|
|
42
|
+
"src",
|
|
43
|
+
"!src/__tests__",
|
|
44
|
+
"!src/__mocks__"
|
|
43
45
|
],
|
|
44
46
|
"scripts": {
|
|
45
47
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
46
|
-
"dev": "tsup src/index.ts --format cjs,esm --dts --clean --watch",
|
|
47
48
|
"fix": "eslint --fix",
|
|
48
49
|
"lint": "eslint",
|
|
49
50
|
"prepublishOnly": "npm run build",
|
|
50
|
-
"push": "yalc publish --push",
|
|
51
51
|
"release": "git push --follow-tags",
|
|
52
52
|
"release:major": "npm version major && git push --follow-tags",
|
|
53
53
|
"release:minor": "npm version minor && git push --follow-tags",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@reduxjs/toolkit": "^2.12.0",
|
|
62
|
-
"@rific/auto-paper": "^0.
|
|
62
|
+
"@rific/auto-paper": "^0.6.0",
|
|
63
63
|
"@testing-library/dom": "^10.4.1",
|
|
64
64
|
"@testing-library/react": "^16.3.2",
|
|
65
65
|
"@types/jest": "^30.0.0",
|
|
@@ -90,14 +90,14 @@
|
|
|
90
90
|
"typescript-eslint": "^8.59.3"
|
|
91
91
|
},
|
|
92
92
|
"peerDependencies": {
|
|
93
|
-
"@rific/auto-paper": "^0.
|
|
93
|
+
"@rific/auto-paper": "^0.6.0",
|
|
94
94
|
"react": ">=18.0.0",
|
|
95
|
-
"react-native": ">=0.
|
|
96
|
-
"react-native-gesture-handler": "
|
|
97
|
-
"react-native-keyboard-controller": "
|
|
95
|
+
"react-native": ">=0.76.0",
|
|
96
|
+
"react-native-gesture-handler": ">=2.0.0",
|
|
97
|
+
"react-native-keyboard-controller": ">=1.0.0",
|
|
98
98
|
"react-native-paper": ">=5.0.0",
|
|
99
99
|
"react-native-reanimated": ">=3.0.0",
|
|
100
|
-
"react-native-safe-area-context": "
|
|
100
|
+
"react-native-safe-area-context": ">=5.0.0"
|
|
101
101
|
},
|
|
102
102
|
"publishConfig": {
|
|
103
103
|
"access": "public"
|
|
@@ -1,14 +1,22 @@
|
|
|
1
|
-
import { createSlice, type PayloadAction } from '@reduxjs/toolkit'
|
|
2
|
-
|
|
3
1
|
import { defaultScrollViewSettings, type ScrollViewSettings } from '../ScrollViewSettingsContext'
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
})
|
|
3
|
+
// Hand-rolled slice — no @reduxjs/toolkit dependency. Action types and creator
|
|
4
|
+
// behavior match the previous createSlice implementation exactly, so this works
|
|
5
|
+
// with RTK stores, vanilla Redux, or any reducer-based state container.
|
|
6
|
+
type PayloadAction<P> = { payload: P; type: string }
|
|
7
|
+
|
|
8
|
+
const createAction = <P>(type: string) => {
|
|
9
|
+
const actionCreator = (payload: P): PayloadAction<P> => ({ payload, type })
|
|
10
|
+
actionCreator.type = type
|
|
11
|
+
actionCreator.match = (action: { type: string }): action is PayloadAction<P> => action.type === type
|
|
12
|
+
return actionCreator
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const initialize = createAction<ScrollViewSettings>('scrollView/initialize')
|
|
16
|
+
|
|
17
|
+
export const scrollViewActions = { initialize }
|
|
12
18
|
|
|
13
|
-
export const
|
|
14
|
-
|
|
19
|
+
export const scrollViewReducer = (state: ScrollViewSettings = defaultScrollViewSettings, action: { type: string }): ScrollViewSettings => {
|
|
20
|
+
if (initialize.match(action)) return action.payload
|
|
21
|
+
return state
|
|
22
|
+
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
const stub = ({ children }: { children?: React.ReactNode }) => children ?? null
|
|
4
|
-
|
|
5
|
-
export const BlurView = jest.fn(stub)
|
|
6
|
-
export const Chip = jest.fn(stub)
|
|
7
|
-
export const FAB = jest.fn(stub)
|
|
8
|
-
export const usePaperDefaults = () => ({ FAB: {} })
|
|
9
|
-
export const useBlur = (blur?: boolean) => blur ?? false
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
const stub = ({ children }: { children?: React.ReactNode }) => children ?? null
|
|
4
|
-
|
|
5
|
-
// Minimal type surface so ts-jest can resolve FlashListProps without the real package
|
|
6
|
-
export type FlashListProps<T> = {
|
|
7
|
-
data?: readonly T[] | null
|
|
8
|
-
estimatedItemSize?: number
|
|
9
|
-
renderItem?: ((info: { item: T; index: number }) => React.ReactElement | null) | null
|
|
10
|
-
contentInset?: { top?: number; bottom?: number; left?: number; right?: number }
|
|
11
|
-
contentOffset?: { x: number; y: number }
|
|
12
|
-
onRefresh?: (() => void) | null
|
|
13
|
-
onScroll?: unknown
|
|
14
|
-
refreshControl?: React.ReactElement | null
|
|
15
|
-
refreshing?: boolean | null
|
|
16
|
-
scrollEventThrottle?: number
|
|
17
|
-
[key: string]: unknown
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const FlashList = jest.fn(stub)
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
const stub = ({ children }: { children?: React.ReactNode }) => children ?? null
|
|
4
|
-
|
|
5
|
-
export const GestureDetector = stub
|
|
6
|
-
export const useNativeGesture = jest.fn(() => ({}))
|
|
7
|
-
export const useSimultaneousGestures = jest.fn((...gestures: unknown[]) => gestures[0])
|
|
8
|
-
export const Gesture = {
|
|
9
|
-
Native: () => ({}),
|
|
10
|
-
Simultaneous: (..._gestures: unknown[]) => ({})
|
|
11
|
-
}
|
|
12
|
-
export type GestureType = object
|
|
13
|
-
export type SingleGesture = object
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
const stub = ({ children }: { children?: React.ReactNode }) => children ?? null
|
|
4
|
-
|
|
5
|
-
export const KeyboardAwareScrollView = stub
|
|
6
|
-
export const KeyboardAwareFlatList = stub
|
|
7
|
-
export const useKeyboardHandler = jest.fn()
|
|
8
|
-
export const useReanimatedKeyboardAnimation = () => ({
|
|
9
|
-
height: { value: 0 },
|
|
10
|
-
progress: { value: 0 }
|
|
11
|
-
})
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
const stub = ({ children }: { children?: React.ReactNode }) => children ?? null
|
|
4
|
-
|
|
5
|
-
export const ProgressBar = jest.fn(() => null)
|
|
6
|
-
export const Surface = jest.fn(stub)
|
|
7
|
-
export const Chip = jest.fn(stub)
|
|
8
|
-
export const Searchbar = jest.fn(() => null)
|
|
9
|
-
|
|
10
|
-
const BackAction = jest.fn(() => null)
|
|
11
|
-
export const Appbar = { BackAction }
|
|
12
|
-
|
|
13
|
-
export const useTheme = () => ({
|
|
14
|
-
dark: false,
|
|
15
|
-
colors: {
|
|
16
|
-
primary: '#6200ee',
|
|
17
|
-
surface: '#ffffff',
|
|
18
|
-
onSurface: '#000000',
|
|
19
|
-
onSurfaceVariant: '#49454f',
|
|
20
|
-
outlineVariant: '#cac4d0'
|
|
21
|
-
}
|
|
22
|
-
})
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
const stub = ({ children }: { children?: React.ReactNode }) => children ?? null
|
|
4
|
-
|
|
5
|
-
// Real Reanimated shared values persist their `.value` across re-renders (like useRef) — the
|
|
6
|
-
// `init` argument is only honored on the first call. A naive `{ value: init }` per render would
|
|
7
|
-
// silently discard mutations made during a previous render every time the owning component re-renders.
|
|
8
|
-
export const useSharedValue = <T,>(init: T) => {
|
|
9
|
-
const ref = React.useRef<{ value: T } | null>(null)
|
|
10
|
-
if (!ref.current) ref.current = { value: init }
|
|
11
|
-
return ref.current
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Reanimated's useAnimatedRef returns a stable callable that also exposes `.current`, so
|
|
15
|
-
// consumers can either pass it directly as a `ref` prop or read/assign `.current` imperatively.
|
|
16
|
-
export const useAnimatedRef = <T,>() => {
|
|
17
|
-
const holder = React.useRef<((node: T | null) => void) & { current: T | null }>(undefined as unknown as ((node: T | null) => void) & { current: T | null })
|
|
18
|
-
if (!holder.current) {
|
|
19
|
-
let current: T | null = null
|
|
20
|
-
const fn = ((node: T | null) => {
|
|
21
|
-
current = node
|
|
22
|
-
}) as ((node: T | null) => void) & { current: T | null }
|
|
23
|
-
Object.defineProperty(fn, 'current', {
|
|
24
|
-
get: () => current,
|
|
25
|
-
set: (node: T | null) => {
|
|
26
|
-
current = node
|
|
27
|
-
}
|
|
28
|
-
})
|
|
29
|
-
holder.current = fn
|
|
30
|
-
}
|
|
31
|
-
return holder.current
|
|
32
|
-
}
|
|
33
|
-
export const useAnimatedStyle = (_factory: () => object) => ({})
|
|
34
|
-
export const useAnimatedProps = (_factory: () => object) => ({})
|
|
35
|
-
export const useAnimatedScrollHandler = (_handler: unknown) => jest.fn()
|
|
36
|
-
export const useAnimatedReaction = jest.fn()
|
|
37
|
-
export const useDerivedValue = (factory: () => unknown) => ({ value: factory() })
|
|
38
|
-
export const withTiming = (value: number) => value
|
|
39
|
-
export const withSpring = (value: number) => value
|
|
40
|
-
export const runOnJS =
|
|
41
|
-
<T extends (...args: unknown[]) => unknown>(fn: T) =>
|
|
42
|
-
(...args: Parameters<T>) =>
|
|
43
|
-
fn(...args)
|
|
44
|
-
export const runOnUI =
|
|
45
|
-
<T extends (...args: unknown[]) => unknown>(fn: T) =>
|
|
46
|
-
(...args: Parameters<T>) =>
|
|
47
|
-
fn(...args)
|
|
48
|
-
|
|
49
|
-
export const createAnimatedComponent = (C: React.ComponentType) => C
|
|
50
|
-
|
|
51
|
-
// The default export IS the Animated namespace — `import Animated from 'reanimated'` → Animated.View
|
|
52
|
-
const Animated = {
|
|
53
|
-
View: stub,
|
|
54
|
-
Text: stub,
|
|
55
|
-
ScrollView: stub,
|
|
56
|
-
FlatList: stub,
|
|
57
|
-
createAnimatedComponent
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export { Animated }
|
|
61
|
-
export default Animated
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
const stub = ({ children }: { children?: React.ReactNode }) => children ?? null
|
|
4
|
-
|
|
5
|
-
const StyleSheet = {
|
|
6
|
-
absoluteFill: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 },
|
|
7
|
-
create: <T extends object>(styles: T): T => styles,
|
|
8
|
-
flatten: (style: unknown) => style
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const Dimensions = {
|
|
12
|
-
get: (_dim: string) => ({ width: 390, height: 844 })
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const Platform = {
|
|
16
|
-
OS: 'ios' as 'ios' | 'android',
|
|
17
|
-
select: (obj: Record<string, unknown>) => obj[Platform.OS] ?? obj.default
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export { Dimensions, Platform, StyleSheet }
|
|
21
|
-
|
|
22
|
-
export const View = stub
|
|
23
|
-
export const Text = stub
|
|
24
|
-
export const Pressable = stub
|
|
25
|
-
export const TouchableOpacity = stub
|
|
26
|
-
export const FlatList = jest.fn(stub)
|
|
27
|
-
export const RefreshControl = jest.fn(stub)
|
|
28
|
-
export const SectionList = jest.fn(stub)
|
|
29
|
-
export const ScrollView = jest.fn(stub)
|
|
30
|
-
export const useWindowDimensions = () => ({ width: 390, height: 844, fontScale: 1, scale: 1 })
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { render } from '@testing-library/react'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
|
|
4
|
-
import { CustomList } from '../CustomList'
|
|
5
|
-
import { ScrollViewProvider } from '../ScrollViewProvider'
|
|
6
|
-
|
|
7
|
-
const wrapper = ({ children }: { children: React.ReactNode }) => <ScrollViewProvider>{children}</ScrollViewProvider>
|
|
8
|
-
|
|
9
|
-
type MockListProps = {
|
|
10
|
-
contentInset?: unknown
|
|
11
|
-
contentOffset?: unknown
|
|
12
|
-
onScroll?: unknown
|
|
13
|
-
scrollEventThrottle?: number
|
|
14
|
-
onMomentumScrollEnd?: unknown
|
|
15
|
-
onScrollBeginDrag?: unknown
|
|
16
|
-
onScrollEndDrag?: unknown
|
|
17
|
-
[key: string]: unknown
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const MockList = jest.fn((_props: MockListProps) => null)
|
|
21
|
-
|
|
22
|
-
describe('CustomList', () => {
|
|
23
|
-
beforeEach(() => MockList.mockClear())
|
|
24
|
-
|
|
25
|
-
it('renders without crashing', () => {
|
|
26
|
-
render(<CustomList component={MockList} />, { wrapper })
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('wires up contentInset and contentOffset', () => {
|
|
30
|
-
render(<CustomList component={MockList} />, { wrapper })
|
|
31
|
-
const props = MockList.mock.calls[0]?.[0] as MockListProps | undefined
|
|
32
|
-
expect(props).toHaveProperty('contentInset')
|
|
33
|
-
expect(props).toHaveProperty('contentOffset')
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('passes scrollEventThrottle={16}', () => {
|
|
37
|
-
render(<CustomList component={MockList} />, { wrapper })
|
|
38
|
-
const props = MockList.mock.calls[0]?.[0] as MockListProps | undefined
|
|
39
|
-
expect(props?.scrollEventThrottle).toBe(16)
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('passes onScroll handler', () => {
|
|
43
|
-
render(<CustomList component={MockList} />, { wrapper })
|
|
44
|
-
const props = MockList.mock.calls[0]?.[0] as MockListProps | undefined
|
|
45
|
-
expect(typeof props?.onScroll).toBe('function')
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('passes momentum and drag scroll handlers', () => {
|
|
49
|
-
render(<CustomList component={MockList} />, { wrapper })
|
|
50
|
-
const props = MockList.mock.calls[0]?.[0] as MockListProps | undefined
|
|
51
|
-
expect(typeof props?.onMomentumScrollEnd).toBe('function')
|
|
52
|
-
expect(typeof props?.onScrollBeginDrag).toBe('function')
|
|
53
|
-
expect(typeof props?.onScrollEndDrag).toBe('function')
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('forwards extra props to the inner component', () => {
|
|
57
|
-
render(<CustomList component={MockList} testID='my-list' />, { wrapper })
|
|
58
|
-
const props = MockList.mock.calls[0]?.[0] as MockListProps | undefined
|
|
59
|
-
expect(props?.testID).toBe('my-list')
|
|
60
|
-
})
|
|
61
|
-
})
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { render } from '@testing-library/react'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { FlatList as RNFlatList } from 'react-native'
|
|
4
|
-
|
|
5
|
-
import { FlatList } from '../FlatList'
|
|
6
|
-
import { ScrollViewProvider } from '../ScrollViewProvider'
|
|
7
|
-
|
|
8
|
-
const wrapper = ({ children }: { children: React.ReactNode }) => <ScrollViewProvider>{children}</ScrollViewProvider>
|
|
9
|
-
|
|
10
|
-
type MockFlatListProps = {
|
|
11
|
-
data?: unknown
|
|
12
|
-
keyExtractor?: (item: never, index: number) => string
|
|
13
|
-
numColumns?: number
|
|
14
|
-
renderItem?: (info: unknown) => React.ReactNode
|
|
15
|
-
[key: string]: unknown
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const lastCallProps = () => {
|
|
19
|
-
const calls = (RNFlatList as unknown as jest.Mock).mock.calls
|
|
20
|
-
return calls[calls.length - 1]?.[0] as MockFlatListProps | undefined
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe('FlatList', () => {
|
|
24
|
-
it('renders without crashing', () => {
|
|
25
|
-
render(<FlatList data={[]} renderItem={() => null} />, { wrapper })
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('renders with data items', () => {
|
|
29
|
-
render(
|
|
30
|
-
<FlatList data={['a', 'b', 'c']} renderItem={({ item }) => <>{item}</>} keyExtractor={(item) => item} />,
|
|
31
|
-
{ wrapper }
|
|
32
|
-
)
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('renders horizontal', () => {
|
|
36
|
-
render(<FlatList data={[]} renderItem={() => null} horizontal />, { wrapper })
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('renders horizontal paged list with HorizontalDots for multiple items', () => {
|
|
40
|
-
render(
|
|
41
|
-
<FlatList
|
|
42
|
-
data={['a', 'b', 'c']}
|
|
43
|
-
renderItem={({ item }) => <>{item}</>}
|
|
44
|
-
keyExtractor={(item) => item}
|
|
45
|
-
horizontal
|
|
46
|
-
pagingEnabled
|
|
47
|
-
/>,
|
|
48
|
-
{ wrapper }
|
|
49
|
-
)
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('renders with pullSearchHeight', () => {
|
|
53
|
-
render(<FlatList data={[]} renderItem={() => null} pullSearchHeight={60} />, { wrapper })
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('renders with renderFilters', () => {
|
|
57
|
-
render(
|
|
58
|
-
<FlatList data={[]} renderItem={() => null} renderFilters={<></>} />,
|
|
59
|
-
{ wrapper }
|
|
60
|
-
)
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('renders with keyboardAware', () => {
|
|
64
|
-
render(<FlatList data={[]} renderItem={() => null} keyboardAware />, { wrapper })
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
describe('FlatList numColumns row-chunking', () => {
|
|
69
|
-
beforeEach(() => (RNFlatList as unknown as jest.Mock).mockClear())
|
|
70
|
-
|
|
71
|
-
it('passes data through unchanged when numColumns is omitted', () => {
|
|
72
|
-
const data = ['a', 'b', 'c']
|
|
73
|
-
render(<FlatList data={data} renderItem={() => null} keyExtractor={(item) => item} />, { wrapper })
|
|
74
|
-
expect(lastCallProps()?.data).toBe(data)
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
it('passes data through unchanged when numColumns is 1', () => {
|
|
78
|
-
const data = ['a', 'b', 'c']
|
|
79
|
-
render(<FlatList data={data} renderItem={() => null} keyExtractor={(item) => item} numColumns={1} />, { wrapper })
|
|
80
|
-
expect(lastCallProps()?.data).toBe(data)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it('never forwards the numColumns prop itself to the underlying RNFlatList', () => {
|
|
84
|
-
render(<FlatList data={['a', 'b', 'c']} renderItem={() => null} keyExtractor={(item) => item} numColumns={3} />, { wrapper })
|
|
85
|
-
expect(lastCallProps()?.numColumns).toBeUndefined()
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('chunks data into rows of numColumns, with a partial last row', () => {
|
|
89
|
-
render(<FlatList data={['a', 'b', 'c', 'd', 'e']} renderItem={() => null} keyExtractor={(item) => item} numColumns={2} />, { wrapper })
|
|
90
|
-
expect(lastCallProps()?.data).toEqual([['a', 'b'], ['c', 'd'], ['e']])
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
it('row keyExtractor joins per-item keys with a colon', () => {
|
|
94
|
-
render(<FlatList data={['a', 'b', 'c', 'd']} renderItem={() => null} keyExtractor={(item) => item} numColumns={2} />, { wrapper })
|
|
95
|
-
const rowKeyExtractor = lastCallProps()?.keyExtractor
|
|
96
|
-
expect(rowKeyExtractor?.(['a', 'b'] as never, 0)).toBe('a:b')
|
|
97
|
-
expect(rowKeyExtractor?.(['c', 'd'] as never, 1)).toBe('c:d')
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('row renderItem renders every item in the row via the original renderItem', () => {
|
|
101
|
-
render(
|
|
102
|
-
<FlatList
|
|
103
|
-
data={['fig', 'kiwi', 'plum']}
|
|
104
|
-
renderItem={({ item }) => <>{item}</>}
|
|
105
|
-
keyExtractor={(item) => item}
|
|
106
|
-
numColumns={2}
|
|
107
|
-
/>,
|
|
108
|
-
{ wrapper }
|
|
109
|
-
)
|
|
110
|
-
const rowRenderItem = lastCallProps()?.renderItem
|
|
111
|
-
const { container } = render(rowRenderItem?.({ index: 0, item: ['fig', 'kiwi'], separators: {} as never }) as React.ReactElement)
|
|
112
|
-
expect(container.textContent).toContain('fig')
|
|
113
|
-
expect(container.textContent).toContain('kiwi')
|
|
114
|
-
expect(container.textContent).not.toContain('plum')
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
it('renderItem indexes items by their position across the whole flattened list, not just within the row', () => {
|
|
118
|
-
const indices: number[] = []
|
|
119
|
-
render(
|
|
120
|
-
<FlatList
|
|
121
|
-
data={['a', 'b', 'c', 'd', 'e']}
|
|
122
|
-
renderItem={({ index }) => {
|
|
123
|
-
indices.push(index)
|
|
124
|
-
return null
|
|
125
|
-
}}
|
|
126
|
-
keyExtractor={(item) => item}
|
|
127
|
-
numColumns={2}
|
|
128
|
-
/>,
|
|
129
|
-
{ wrapper }
|
|
130
|
-
)
|
|
131
|
-
const rowRenderItem = lastCallProps()?.renderItem
|
|
132
|
-
render(rowRenderItem?.({ index: 2, item: ['e'], separators: {} as never }) as React.ReactElement)
|
|
133
|
-
expect(indices).toEqual([4]) // row index 2 * numColumns 2 + column 0
|
|
134
|
-
})
|
|
135
|
-
})
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { render } from '@testing-library/react'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
|
|
4
|
-
import { PullSearch, type PullSearchHandle } from '../PullSearch'
|
|
5
|
-
import { ScrollViewProvider } from '../ScrollViewProvider'
|
|
6
|
-
|
|
7
|
-
const wrapper = ({ children }: { children: React.ReactNode }) => <ScrollViewProvider>{children}</ScrollViewProvider>
|
|
8
|
-
|
|
9
|
-
describe('PullSearch', () => {
|
|
10
|
-
it('renders without crashing', () => {
|
|
11
|
-
render(<PullSearch onChangeText={jest.fn()} onHeightChange={jest.fn()} />, { wrapper })
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it('renders with placeholder', () => {
|
|
15
|
-
render(
|
|
16
|
-
<PullSearch onChangeText={jest.fn()} onHeightChange={jest.fn()} placeholder='Search items...' />,
|
|
17
|
-
{ wrapper }
|
|
18
|
-
)
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('renders with initial value', () => {
|
|
22
|
-
render(<PullSearch onChangeText={jest.fn()} onHeightChange={jest.fn()} value='hello' />, { wrapper })
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('exposes blur and focus via imperative ref', () => {
|
|
26
|
-
const ref = React.createRef<PullSearchHandle>()
|
|
27
|
-
render(<PullSearch ref={ref} onChangeText={jest.fn()} onHeightChange={jest.fn()} />, { wrapper })
|
|
28
|
-
expect(typeof ref.current?.blur).toBe('function')
|
|
29
|
-
expect(typeof ref.current?.focus).toBe('function')
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('does not throw when blur/focus called with no input mounted', () => {
|
|
33
|
-
const ref = React.createRef<PullSearchHandle>()
|
|
34
|
-
render(<PullSearch ref={ref} onChangeText={jest.fn()} onHeightChange={jest.fn()} />, { wrapper })
|
|
35
|
-
expect(() => ref.current?.blur()).not.toThrow()
|
|
36
|
-
expect(() => ref.current?.focus()).not.toThrow()
|
|
37
|
-
})
|
|
38
|
-
})
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { render } from '@testing-library/react'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
|
|
4
|
-
import { ScrollView } from '../ScrollView'
|
|
5
|
-
import { ScrollViewProvider } from '../ScrollViewProvider'
|
|
6
|
-
|
|
7
|
-
const wrapper = ({ children }: { children: React.ReactNode }) => <ScrollViewProvider>{children}</ScrollViewProvider>
|
|
8
|
-
|
|
9
|
-
describe('ScrollView', () => {
|
|
10
|
-
it('renders without crashing', () => {
|
|
11
|
-
render(<ScrollView />, { wrapper })
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it('renders children', () => {
|
|
15
|
-
const { getByText } = render(
|
|
16
|
-
<ScrollView>
|
|
17
|
-
<></>
|
|
18
|
-
</ScrollView>,
|
|
19
|
-
{ wrapper }
|
|
20
|
-
)
|
|
21
|
-
expect(getByText).toBeDefined()
|
|
22
|
-
})
|
|
23
|
-
})
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { render } from '@testing-library/react'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
|
|
4
|
-
import { ScrollViewFooter } from '../ScrollViewFooter'
|
|
5
|
-
import { ScrollViewHeader } from '../ScrollViewHeader'
|
|
6
|
-
import { ScrollViewProvider } from '../ScrollViewProvider'
|
|
7
|
-
|
|
8
|
-
const wrapper = ({ children }: { children: React.ReactNode }) => <ScrollViewProvider>{children}</ScrollViewProvider>
|
|
9
|
-
|
|
10
|
-
describe('ScrollViewHeader', () => {
|
|
11
|
-
it('renders without crashing', () => {
|
|
12
|
-
render(<ScrollViewHeader />, { wrapper })
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('renders with children', () => {
|
|
16
|
-
render(<ScrollViewHeader><></></ScrollViewHeader>, { wrapper })
|
|
17
|
-
})
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
describe('ScrollViewFooter', () => {
|
|
21
|
-
it('renders without crashing', () => {
|
|
22
|
-
render(<ScrollViewFooter />, { wrapper })
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('renders with children', () => {
|
|
26
|
-
render(<ScrollViewFooter><></></ScrollViewFooter>, { wrapper })
|
|
27
|
-
})
|
|
28
|
-
})
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { act, render } from '@testing-library/react'
|
|
2
|
-
import React, { useContext } from 'react'
|
|
3
|
-
|
|
4
|
-
import { ScrollViewContext } from '../ScrollViewContext'
|
|
5
|
-
import { ScrollViewProvider } from '../ScrollViewProvider'
|
|
6
|
-
import { useScrollView } from '../useScrollView'
|
|
7
|
-
|
|
8
|
-
const ContextReader = ({ onRead }: { onRead: (v: ReturnType<typeof useScrollView>) => void }) => {
|
|
9
|
-
const value = useScrollView()
|
|
10
|
-
onRead(value)
|
|
11
|
-
return null
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
describe('ScrollViewProvider', () => {
|
|
15
|
-
it('renders without crashing', () => {
|
|
16
|
-
render(
|
|
17
|
-
<ScrollViewProvider>
|
|
18
|
-
<></>
|
|
19
|
-
</ScrollViewProvider>
|
|
20
|
-
)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('provides default context values', () => {
|
|
24
|
-
let capturedValue: ReturnType<typeof useScrollView> | undefined
|
|
25
|
-
render(
|
|
26
|
-
<ScrollViewProvider>
|
|
27
|
-
<ContextReader onRead={(v) => { capturedValue = v }} />
|
|
28
|
-
</ScrollViewProvider>
|
|
29
|
-
)
|
|
30
|
-
expect(capturedValue?.progress).toBeNull()
|
|
31
|
-
expect(capturedValue?.progressing).toBe(false)
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('accepts headerFixed and footerFixed props', () => {
|
|
35
|
-
let capturedCtx: { headerFixed: boolean; footerFixed: boolean } | undefined
|
|
36
|
-
const Reader = () => {
|
|
37
|
-
const ctx = useContext(ScrollViewContext)
|
|
38
|
-
capturedCtx = { headerFixed: ctx.headerFixed, footerFixed: ctx.footerFixed }
|
|
39
|
-
return null
|
|
40
|
-
}
|
|
41
|
-
render(
|
|
42
|
-
<ScrollViewProvider headerFixed footerFixed>
|
|
43
|
-
<Reader />
|
|
44
|
-
</ScrollViewProvider>
|
|
45
|
-
)
|
|
46
|
-
expect(capturedCtx?.headerFixed).toBe(true)
|
|
47
|
-
expect(capturedCtx?.footerFixed).toBe(true)
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('setProgress updates progress value', () => {
|
|
51
|
-
let capturedValue: ReturnType<typeof useScrollView> | undefined
|
|
52
|
-
const { rerender } = render(
|
|
53
|
-
<ScrollViewProvider>
|
|
54
|
-
<ContextReader onRead={(v) => { capturedValue = v }} />
|
|
55
|
-
</ScrollViewProvider>
|
|
56
|
-
)
|
|
57
|
-
act(() => { capturedValue?.setProgress(0.5) })
|
|
58
|
-
rerender(
|
|
59
|
-
<ScrollViewProvider>
|
|
60
|
-
<ContextReader onRead={(v) => { capturedValue = v }} />
|
|
61
|
-
</ScrollViewProvider>
|
|
62
|
-
)
|
|
63
|
-
expect(capturedValue?.progress).toBe(0.5)
|
|
64
|
-
})
|
|
65
|
-
})
|