diff --git a/app/components/emoji_picker/__snapshots__/emoji_picker.test.js.snap b/app/components/emoji_picker/__snapshots__/emoji_picker.test.js.snap
new file mode 100644
index 000000000..a4a9bb84c
--- /dev/null
+++ b/app/components/emoji_picker/__snapshots__/emoji_picker.test.js.snap
@@ -0,0 +1,133 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`components/emoji_picker/EmojiPicker should match snapshot 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/app/components/emoji_picker/emoji_picker.js b/app/components/emoji_picker/emoji_picker.js
index 2d6ba17a8..0b54b7d49 100644
--- a/app/components/emoji_picker/emoji_picker.js
+++ b/app/components/emoji_picker/emoji_picker.js
@@ -95,9 +95,9 @@ export default class EmojiPicker extends PureComponent {
}
componentWillReceiveProps(nextProps) {
- let rebuildEmojis = false;
+ this.rebuildEmojis = false;
if (this.props.deviceWidth !== nextProps.deviceWidth) {
- rebuildEmojis = true;
+ this.rebuildEmojis = true;
if (this.refs.search_bar) {
this.refs.search_bar.blur();
@@ -105,14 +105,16 @@ export default class EmojiPicker extends PureComponent {
}
if (this.props.emojis !== nextProps.emojis) {
- rebuildEmojis = true;
+ this.rebuildEmojis = true;
+ this.deviceWidth = nextProps.deviceWidth;
}
+ }
- if (rebuildEmojis) {
- const emojis = this.renderableEmojis(this.props.emojisBySection, nextProps.deviceWidth);
- this.setState({
- emojis,
- });
+ setRebuiltEmojis = (searchBarAnimationComplete) => {
+ if (this.rebuildEmojis && searchBarAnimationComplete) {
+ this.rebuildEmojis = false;
+ const emojis = this.renderableEmojis(this.props.emojisBySection, this.deviceWidth);
+ this.setState({emojis});
}
}
@@ -489,6 +491,7 @@ export default class EmojiPicker extends PureComponent {
autoCapitalize='none'
value={searchTerm}
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
+ onAnimationComplete={this.setRebuiltEmojis}
/>
diff --git a/app/components/emoji_picker/emoji_picker.test.js b/app/components/emoji_picker/emoji_picker.test.js
index 9d8f995f9..3f8511a13 100644
--- a/app/components/emoji_picker/emoji_picker.test.js
+++ b/app/components/emoji_picker/emoji_picker.test.js
@@ -1,9 +1,30 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {filterEmojiSearchInput} from './emoji_picker.js';
+import React from 'react';
+
+import Preferences from 'mattermost-redux/constants/preferences';
+
+import {shallowWithIntl} from 'test/intl-test-helper';
+import EmojiPicker, {filterEmojiSearchInput} from './emoji_picker.js';
describe('components/emoji_picker/EmojiPicker', () => {
+ const baseProps = {
+ actions: {
+ getCustomEmojis: jest.fn(),
+ incrementEmojiPickerPage: jest.fn(),
+ searchCustomEmojis: jest.fn(),
+ },
+ customEmojisEnabled: false,
+ customEmojiPage: 200,
+ deviceWidth: 400,
+ emojis: [],
+ emojisBySection: [],
+ fuse: {},
+ isLandscape: false,
+ theme: Preferences.THEMES.default,
+ };
+
const testCases = [
{input: 'smile', output: 'smile'},
{input: 'SMILE', output: 'smile'},
@@ -20,4 +41,77 @@ describe('components/emoji_picker/EmojiPicker', () => {
expect(filterEmojiSearchInput(testCase.input)).toEqual(testCase.output);
});
});
+
+ test('should match snapshot', () => {
+ const wrapper = shallowWithIntl();
+ expect(wrapper.getElement()).toMatchSnapshot();
+ });
+
+ test('should set rebuildEmojis to true when deviceWidth changes', () => {
+ const wrapper = shallowWithIntl();
+ const instance = wrapper.instance();
+
+ expect(instance.rebuildEmojis).toBe(undefined);
+
+ const newDeviceWidth = baseProps.deviceWidth * 2;
+ wrapper.setProps({deviceWidth: newDeviceWidth});
+
+ expect(instance.rebuildEmojis).toBe(true);
+ });
+
+ test('should set rebuildEmojis to true and new deviceWidth when emojis change', () => {
+ const wrapper = shallowWithIntl();
+ const instance = wrapper.instance();
+
+ expect(instance.rebuildEmojis).toBe(undefined);
+ expect(instance.deviceWidth).toBe(undefined);
+
+ const newDeviceWidth = baseProps.deviceWidth * 2;
+ const newEmojis = [{}];
+ wrapper.setProps({deviceWidth: newDeviceWidth, emojis: newEmojis});
+
+ expect(instance.rebuildEmojis).toBe(true);
+ expect(instance.deviceWidth).toBe(newDeviceWidth);
+ });
+
+ test('should set rebuilt emojis when rebuildEmojis is true and searchBarAnimationComplete is true', () => {
+ const wrapper = shallowWithIntl();
+ const instance = wrapper.instance();
+ instance.setState = jest.fn();
+ instance.renderableEmojis = jest.spyOn(instance, 'renderableEmojis');
+
+ instance.rebuildEmojis = true;
+ const searchBarAnimationComplete = true;
+ const setRebuiltEmojis = jest.spyOn(instance, 'setRebuiltEmojis');
+ setRebuiltEmojis(searchBarAnimationComplete);
+
+ expect(instance.setState).toHaveBeenCalledWith({emojis: []});
+ expect(instance.rebuildEmojis).toBe(false);
+ });
+
+ test('should not set rebuilt emojis when rebuildEmojis is false and searchBarAnimationComplete is true', () => {
+ const wrapper = shallowWithIntl();
+ const instance = wrapper.instance();
+ instance.setState = jest.fn();
+
+ instance.rebuildEmojis = false;
+ const searchBarAnimationComplete = true;
+ const setRebuiltEmojis = jest.spyOn(instance, 'setRebuiltEmojis');
+ setRebuiltEmojis(searchBarAnimationComplete);
+
+ expect(instance.setState).not.toHaveBeenCalled();
+ });
+
+ test('should not set rebuilt emojis when rebuildEmojis is true and searchBarAnimationComplete is false', () => {
+ const wrapper = shallowWithIntl();
+ const instance = wrapper.instance();
+ instance.setState = jest.fn();
+
+ instance.rebuildEmojis = true;
+ const searchBarAnimationComplete = false;
+ const setRebuiltEmojis = jest.spyOn(instance, 'setRebuiltEmojis');
+ setRebuiltEmojis(searchBarAnimationComplete);
+
+ expect(instance.setState).not.toHaveBeenCalled();
+ });
});
diff --git a/app/components/search_bar/search_bar.ios.js b/app/components/search_bar/search_bar.ios.js
index 6c9de2c16..ad2e6bd8a 100644
--- a/app/components/search_bar/search_bar.ios.js
+++ b/app/components/search_bar/search_bar.ios.js
@@ -40,6 +40,7 @@ export default class SearchBarIos extends PureComponent {
searchIconCollapsedMargin: PropTypes.number,
searchIconExpandedMargin: PropTypes.number,
keyboardAppearance: PropTypes.string,
+ onAnimationComplete: PropTypes.func,
};
static defaultProps = {
diff --git a/app/components/search_bar/search_box.js b/app/components/search_bar/search_box.js
index a20cae328..947ae56de 100644
--- a/app/components/search_bar/search_box.js
+++ b/app/components/search_bar/search_box.js
@@ -18,6 +18,7 @@ import EvilIcon from 'react-native-vector-icons/EvilIcons';
import IonIcon from 'react-native-vector-icons/Ionicons';
import CustomPropTypes from 'app/constants/custom_prop_types';
+import {emptyFunction} from 'app/utils/general';
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
const AnimatedIonIcon = Animated.createAnimatedComponent(IonIcon);
@@ -78,6 +79,7 @@ export default class Search extends Component {
leftComponent: PropTypes.element,
inputCollapsedMargin: PropTypes.number,
keyboardAppearance: PropTypes.string,
+ onAnimationComplete: PropTypes.func,
};
static defaultProps = {
@@ -102,6 +104,7 @@ export default class Search extends Component {
value: '',
leftComponent: null,
inputCollapsedMargin: 10,
+ onAnimationComplete: emptyFunction,
};
constructor(props) {
@@ -370,7 +373,7 @@ export default class Search extends Component {
useNativeDriver: true,
}
),
- ]).start();
+ ]).start(({finished}) => this.props.onAnimationComplete(finished));
this.shadowHeight = this.props.shadowOffsetHeightCollapsed;
resolve();
});