@rific/scroll-view 0.3.0 → 0.4.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rific/scroll-view",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
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",
@@ -27,7 +27,7 @@
27
27
  "type": "commonjs",
28
28
  "exports": {
29
29
  ".": {
30
- "react-native": "./src/index.ts",
30
+ "react-native": "./dist/index.js",
31
31
  "types": "./dist/index.d.ts",
32
32
  "import": "./dist/index.mjs",
33
33
  "require": "./dist/index.js"
@@ -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.4.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.3.1 || ^0.4.0 || ^0.5.0",
93
+ "@rific/auto-paper": "^0.6.0",
94
94
  "react": ">=18.0.0",
95
- "react-native": ">=0.73.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
- const slice = createSlice({
6
- name: 'scrollView',
7
- initialState: defaultScrollViewSettings,
8
- reducers: {
9
- initialize: (_state, action: PayloadAction<ScrollViewSettings>) => action.payload
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 scrollViewActions = slice.actions
14
- export const scrollViewReducer = slice.reducer
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,2 +0,0 @@
1
- export const useSafeAreaInsets = () => ({ top: 44, bottom: 34, left: 0, right: 0 })
2
- export const SafeAreaProvider = ({ children }: { children?: React.ReactNode }) => children ?? null
@@ -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
- })