Created
June 8, 2020 02:12
-
-
Save ShaswatPrabhat/75cb0e89a44618a5a1d820d521d52265 to your computer and use it in GitHub Desktop.
useAutoScroll hook to enable React-Native screens to scroll to erroneous form fields
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useRef } from 'react'; | |
import { View, Dimensions } from 'react-native'; | |
import util from 'helpers/util'; | |
const useAutoScroll = () => { | |
const yCoordinates = useRef({}); | |
let scrollRef = null; | |
const setScrollRef = ref => { | |
if (ref) scrollRef = ref; | |
}; | |
const scrollTo = errors => { | |
const firstInvalidKey = util.getFirstConditionalKey(yCoordinates.current, 'y', errors); | |
if (yCoordinates.current[firstInvalidKey].ref) { | |
yCoordinates.current[firstInvalidKey].ref.handleFocus({ | |
nativeEvent: { text: 'Dummy name' }, | |
}); | |
} | |
scrollRef.scrollTo(0, yCoordinates.current[firstInvalidKey].y); | |
}; | |
const captureRef = inputKey => ref => { | |
if (ref) { | |
if (yCoordinates.current[inputKey]) { | |
yCoordinates.current[inputKey].ref = ref; | |
} else { | |
yCoordinates.current[inputKey] = {}; | |
yCoordinates.current[inputKey].ref = ref; | |
} | |
} | |
}; | |
const scrollTracker = (component, inputKey) => { | |
let viewRef; | |
const getScrollToY = py => py - Dimensions.get('window').height / 5.5; | |
const getCoordinates = () => { | |
viewRef.measure((fx, fy, width, height, px, py) => { | |
if (yCoordinates.current[inputKey]) { | |
if (!yCoordinates.current[inputKey].y) { | |
yCoordinates.current[inputKey].y = getScrollToY(py); | |
} | |
} else { | |
yCoordinates.current[inputKey] = {}; | |
yCoordinates.current[inputKey].y = getScrollToY(py); | |
} | |
}); | |
}; | |
return ( | |
<View | |
testID={`${inputKey}Wrapper`} | |
/* eslint-disable-next-line no-return-assign */ | |
ref={ref => { | |
if (ref) viewRef = ref; | |
}} | |
onLayout={getCoordinates} | |
> | |
{component} | |
</View> | |
); | |
}; | |
return { | |
scrollTracker, | |
setScrollRef, | |
scrollTo, | |
captureRef, | |
}; | |
}; | |
export default useAutoScroll; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Dimensions, Text, TextInput as RNTextInput, View as RNView } from 'react-native'; | |
import { TextInput } from 'MyCustomTextInput'; | |
import { fireEvent, flushMicrotasksQueue, render } from 'react-native-testing-library'; | |
import React from 'react'; | |
import useAutoScroll from './useAutoScroll'; | |
import { renderHook } from '@testing-library/react-hooks'; | |
describe('tests for useAutoScroll', () => { | |
const mockFocus = jest.spyOn(RNTextInput.prototype, 'focus'); | |
const mockMeasure = jest.spyOn(RNView.prototype, 'measure'); | |
it('should render wrapperView', async () => { | |
const { result } = renderHook(() => useAutoScroll()); | |
const { getByTestId } = render(result.current.scrollTracker(<Text>Noice</Text>, 'aNoiceKey')); | |
await flushMicrotasksQueue(); | |
expect(getByTestId('aNoiceKeyWrapper')).toBeDefined(); | |
}); | |
it('should call scrollTo on click of button when yCoorodinate is absent while setting coordinate', () => { | |
const mockScrollTo = jest.fn(); | |
const mockScrollRef = { | |
scrollToPosition: mockScrollTo, | |
}; | |
const { result } = renderHook(() => useAutoScroll()); | |
const { getByTestId } = render( | |
result.current.scrollTracker(<TextInput ref={result.current.captureRef('aNoiceKey')} />, 'aNoiceKey') | |
); | |
fireEvent(getByTestId('aNoiceKeyWrapper'), 'onLayout'); | |
expect(mockMeasure).toHaveBeenCalledTimes(1); | |
result.current.captureRef('aNoiceKey'); | |
fireEvent(getByTestId('aNoiceKeyWrapper'), 'onLayout'); | |
mockMeasure.mock.calls[0][0](23, 29, 31, 37, 89, 97); | |
result.current.setScrollRef(mockScrollRef); | |
result.current.scrollTo(['aNoiceKey']); | |
expect(mockScrollTo).toHaveBeenCalledTimes(1); | |
expect(mockScrollTo).toHaveBeenNthCalledWith(1, 0, 97 - Dimensions.get('window').height / 5.5); | |
expect(mockFocus).toHaveBeenCalledTimes(1); | |
}); | |
it('should call scrollTo on click of button when yCoordinate is absent during setting captureRef', () => { | |
mockMeasure.mockClear(); | |
mockFocus.mockClear(); | |
let localRef = {}; | |
const mockScrollTo = jest.fn(); | |
const mockScrollRef = { | |
scrollToPosition: mockScrollTo, | |
}; | |
const { result } = renderHook(() => useAutoScroll()); | |
const { getByTestId } = render( | |
// eslint-disable-next-line no-return-assign | |
result.current.scrollTracker(<TextInput ref={ref => (localRef = ref)} />, 'aNoiceKey') | |
); | |
fireEvent(getByTestId('aNoiceKeyWrapper'), 'onLayout'); | |
expect(mockMeasure).toHaveBeenCalledTimes(1); | |
mockMeasure.mock.calls[0][0](23, 29, 31, 37, 89, 97); | |
result.current.captureRef('aNoiceKey')(localRef); | |
result.current.setScrollRef(mockScrollRef); | |
result.current.scrollTo(['aNoiceKey']); | |
expect(mockScrollTo).toHaveBeenCalledTimes(1); | |
expect(mockScrollTo).toHaveBeenNthCalledWith(1, 0, 97 - Dimensions.get('window').height / 5.5); | |
expect(mockFocus).toHaveBeenCalledTimes(1); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
where i found util folder?