Skip to content

Instantly share code, notes, and snippets.

@ShaswatPrabhat
Created June 8, 2020 02:12
Show Gist options
  • Save ShaswatPrabhat/75cb0e89a44618a5a1d820d521d52265 to your computer and use it in GitHub Desktop.
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
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;
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);
});
});
@jhonatanhrg
Copy link

where i found util folder?

@Jayb2022
Copy link

where I find the util folder?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment