Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TS migration] Migrate 'EmojiPicker' component to TypeScript #37641

Merged
merged 53 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
b0678c6
migrate EmojiPicker to ts
HezekielT Feb 22, 2024
19b1bed
rename emojiPickerMenuPropTypes to types.ts
HezekielT Feb 22, 2024
df5af68
remove unnecessary type definition
HezekielT Feb 22, 2024
9ed4b4f
export types
HezekielT Feb 22, 2024
0b8f228
migrate useEmojiPickerMenu to ts
HezekielT Feb 22, 2024
5f7e85b
wip
HezekielT Feb 23, 2024
b249860
migrate CategoryShortcutBar to ts
HezekielT Feb 25, 2024
38d4c3a
migrate CategoryShortcutButton to ts
HezekielT Feb 25, 2024
918b173
wip migrate BaseEmojiPickerMenu to ts
HezekielT Feb 25, 2024
5bf07bf
remove CategoryShortcutButton.js
HezekielT Feb 25, 2024
e12a1f4
migrate getSkinToneEmojiFromIndex to ts
HezekielT Feb 25, 2024
4045713
delete BaseEmojiPickerMenu.js
HezekielT Feb 25, 2024
d93dba4
wip migrate EmojiPickerButton to ts
HezekielT Feb 28, 2024
7218e0a
wip migrate EmojiPickerButtonDropdown to ts
HezekielT Feb 28, 2024
43d4aa9
wip
HezekielT Feb 28, 2024
0abcb72
migrate index.js to functional component
HezekielT Feb 29, 2024
0c6d6e7
migrate index.native to functional component
HezekielT Feb 29, 2024
9dcdfd6
create types file for EmojiPickerMenuItem
HezekielT Feb 29, 2024
7c8bceb
migrate EmojiPickerMenuItem(index) to ts
HezekielT Feb 29, 2024
22a872f
migrate EmojiPickerMenuItem(index.native) to ts
HezekielT Feb 29, 2024
4395ce7
wip
HezekielT Mar 1, 2024
aa7b6fd
EmojiPicker migration done
HezekielT Mar 1, 2024
ea27a47
remove unnecessary type definition
HezekielT Mar 1, 2024
616e4ac
minor changes
HezekielT Mar 1, 2024
13357f0
migration of EmojiPickerButton done
HezekielT Mar 1, 2024
8a85861
migrate EmojiSkinTone
HezekielT Mar 1, 2024
5026a28
run prettier
HezekielT Mar 1, 2024
7db7ee9
fix ts issues
HezekielT Mar 5, 2024
f4f7229
fix lint and ts issues
HezekielT Mar 5, 2024
8559b7b
resolve confilict
HezekielT Mar 5, 2024
9562843
code cleanup
HezekielT Mar 5, 2024
b2932a2
code changes based on review
HezekielT Mar 7, 2024
2215fe3
resolve conflict
HezekielT Mar 7, 2024
795e3e6
remove params without description
HezekielT Mar 7, 2024
ae9f213
Update style conditions
HezekielT Mar 7, 2024
4305596
Update src/components/EmojiPicker/EmojiPickerMenu/types.ts
HezekielT Mar 7, 2024
bfcc907
Update src/components/EmojiPicker/EmojiPickerMenu/index.tsx
HezekielT Mar 7, 2024
cd2f620
Update src/components/EmojiPicker/EmojiPickerMenu/BaseEmojiPickerMenu…
HezekielT Mar 7, 2024
280a849
run prettier
HezekielT Mar 7, 2024
bb66611
remove params without description
HezekielT Mar 7, 2024
abc8a60
improve code
HezekielT Mar 8, 2024
e192774
run prettier
HezekielT Mar 8, 2024
f8da5fa
add default props
HezekielT Mar 8, 2024
22fbbe8
Update src/components/EmojiPicker/EmojiPickerMenu/BaseEmojiPickerMenu…
HezekielT Mar 8, 2024
4423c09
code cleanup
HezekielT Mar 10, 2024
9085bc8
code changes based on review
HezekielT Mar 12, 2024
1085811
code changes based on review
HezekielT Mar 13, 2024
3932433
code changes based on review
HezekielT Mar 19, 2024
da9e52f
remove type assertion
HezekielT Mar 20, 2024
ee852df
fix ts errors
HezekielT Mar 24, 2024
4192624
code changes based on review
HezekielT Mar 25, 2024
27a2dd5
replaced array index usage by headerEmoji.index
HezekielT Mar 29, 2024
dd6327f
fix cursor issue
HezekielT Apr 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,42 +1,33 @@
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import _ from 'underscore';
import sourcePropTypes from '@components/Image/sourcePropTypes';
import useThemeStyles from '@hooks/useThemeStyles';
import type {HeaderIndice} from '@libs/EmojiUtils';
import CategoryShortcutButton from './CategoryShortcutButton';

const propTypes = {
type CategoryShortcutBarProps = {
/** The function to call when an emoji is selected */
onPress: PropTypes.func.isRequired,
onPress: (index: number) => void;

/** The emojis consisting emoji code and indices that the icons should link to */
headerEmojis: PropTypes.arrayOf(
PropTypes.shape({
code: PropTypes.string.isRequired,
index: PropTypes.number.isRequired,
icon: sourcePropTypes.isRequired,
}),
).isRequired,
headerEmojis: HeaderIndice[];
};

function CategoryShortcutBar(props) {
function CategoryShortcutBar({onPress, headerEmojis}: CategoryShortcutBarProps) {
const styles = useThemeStyles();
return (
<View style={[styles.ph4, styles.flexRow]}>
{_.map(props.headerEmojis, (headerEmoji, i) => (
{headerEmojis.map((headerEmoji) => (
<CategoryShortcutButton
icon={headerEmoji.icon}
onPress={() => props.onPress(headerEmoji.index)}
key={`categoryShortcut${i}`}
onPress={() => onPress(headerEmoji.index)}
key={`categoryShortcut${headerEmoji.index}`}
code={headerEmoji.code}
/>
))}
</View>
);
}

CategoryShortcutBar.propTypes = propTypes;
CategoryShortcutBar.displayName = 'CategoryShortcutBar';

export default CategoryShortcutBar;
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import PropTypes from 'prop-types';
import React, {useState} from 'react';
import Icon from '@components/Icon';
import sourcePropTypes from '@components/Image/sourcePropTypes';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import Tooltip from '@components/Tooltip';
import useLocalize from '@hooks/useLocalize';
Expand All @@ -11,19 +9,21 @@ import useThemeStyles from '@hooks/useThemeStyles';
import getButtonState from '@libs/getButtonState';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import type IconAsset from '@src/types/utils/IconAsset';

const propTypes = {
type CategoryShortcutButtonProps = {
/** The emoji code of the category header */
code: PropTypes.string.isRequired,
code: string;

HezekielT marked this conversation as resolved.
Show resolved Hide resolved
/** The icon representation of the category that this button links to */
icon: sourcePropTypes.isRequired,
icon: IconAsset;

/** The function to call when an emoji is selected */
onPress: PropTypes.func.isRequired,
onPress: () => void;
};

function CategoryShortcutButton(props) {
function CategoryShortcutButton({code, icon, onPress}: CategoryShortcutButtonProps) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
Expand All @@ -32,28 +32,28 @@ function CategoryShortcutButton(props) {

return (
<Tooltip
text={translate(`emojiPicker.headers.${props.code}`)}
text={translate(`emojiPicker.headers.${code}` as TranslationPaths)}
shiftVertical={-4}
HezekielT marked this conversation as resolved.
Show resolved Hide resolved
>
<PressableWithoutFeedback
shouldUseAutoHitSlop={false}
onPress={props.onPress}
onPress={onPress}
onHoverIn={() => setIsHighlighted(true)}
onHoverOut={() => setIsHighlighted(false)}
style={({pressed}) => [StyleUtils.getButtonBackgroundColorStyle(getButtonState(false, pressed)), styles.categoryShortcutButton, isHighlighted && styles.emojiItemHighlighted]}
accessibilityLabel={`emojiPicker.headers.${props.code}`}
accessibilityLabel={`emojiPicker.headers.${code}`}
role={CONST.ROLE.BUTTON}
>
<Icon
fill={theme.icon}
src={props.icon}
src={icon}
height={variables.iconSizeNormal}
width={variables.iconSizeNormal}
/>
</PressableWithoutFeedback>
</Tooltip>
);
}
CategoryShortcutButton.propTypes = propTypes;

CategoryShortcutButton.displayName = 'CategoryShortcutButton';
export default React.memo(CategoryShortcutButton);
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import PropTypes from 'prop-types';
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
import type {ForwardedRef, RefObject} from 'react';
import {Dimensions} from 'react-native';
import _ from 'underscore';
import type {View} from 'react-native';
import type {Emoji} from '@assets/emojis/types';
import PopoverWithMeasuredContent from '@components/PopoverWithMeasuredContent';
import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types';
import withViewportOffsetTop from '@components/withViewportOffsetTop';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeStyles from '@hooks/useThemeStyles';
import useWindowDimensions from '@hooks/useWindowDimensions';
import type {AnchorOrigin, EmojiPickerRef, EmojiPopoverAnchor, OnEmojiSelected, OnModalHideValue, OnWillShowPicker} from '@libs/actions/EmojiPickerAction';
import * as Browser from '@libs/Browser';
import calculateAnchorPosition from '@libs/calculateAnchorPosition';
import CONST from '@src/CONST';
Expand All @@ -17,29 +20,29 @@ const DEFAULT_ANCHOR_ORIGIN = {
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM,
};

const propTypes = {
viewportOffsetTop: PropTypes.number.isRequired,
type EmojiPickerProps = {
viewportOffsetTop: number;
};

const EmojiPicker = forwardRef((props, ref) => {
function EmojiPicker({viewportOffsetTop}: EmojiPickerProps, ref: ForwardedRef<EmojiPickerRef>) {
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const [isEmojiPickerVisible, setIsEmojiPickerVisible] = useState(false);
const [emojiPopoverAnchorPosition, setEmojiPopoverAnchorPosition] = useState({
horizontal: 0,
vertical: 0,
});
const [emojiPopoverAnchorOrigin, setEmojiPopoverAnchorOrigin] = useState(DEFAULT_ANCHOR_ORIGIN);
const [activeID, setActiveID] = useState();
const emojiPopoverAnchorRef = useRef(null);
const [emojiPopoverAnchorOrigin, setEmojiPopoverAnchorOrigin] = useState<AnchorOrigin>(DEFAULT_ANCHOR_ORIGIN);
const [activeID, setActiveID] = useState<string | null>();
const emojiPopoverAnchorRef = useRef<EmojiPopoverAnchor | null>(null);
const emojiAnchorDimension = useRef({
width: 0,
height: 0,
});
const onModalHide = useRef(() => {});
const onEmojiSelected = useRef(() => {});
const activeEmoji = useRef();
const emojiSearchInput = useRef();
const onEmojiSelected = useRef<OnEmojiSelected>(() => {});
const activeEmoji = useRef<string | undefined>();
const emojiSearchInput = useRef<BaseTextInputRef | null>();
const {isSmallScreenWidth, windowHeight} = useWindowDimensions();

/**
Expand All @@ -50,34 +53,39 @@ const EmojiPicker = forwardRef((props, ref) => {
*
* Don't directly get the ref from emojiPopoverAnchorRef, instead use getEmojiPopoverAnchor()
*/
const getEmojiPopoverAnchor = useCallback(() => emojiPopoverAnchorRef.current || emojiPopoverAnchorRef, []);
const getEmojiPopoverAnchor = useCallback(() => emojiPopoverAnchorRef.current ?? emojiPopoverAnchorRef?.current, []);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be emojiPopoverAnchorRef same as the original code

/**
* Show the emoji picker menu.
*
* @param {Function} [onModalHideValue=() => {}] - Run a callback when Modal hides.
* @param {Function} [onEmojiSelectedValue=() => {}] - Run a callback when Emoji selected.
* @param {React.MutableRefObject} emojiPopoverAnchorValue - Element to which Popover is anchored
* @param {Object} [anchorOrigin=DEFAULT_ANCHOR_ORIGIN] - Anchor origin for Popover
* @param {Function} [onWillShow] - Run a callback when Popover will show
* @param {String} id - Unique id for EmojiPicker
* @param {String} activeEmojiValue - Selected emoji to be highlighted
* @param [onModalHideValue=() => {}] - Run a callback when Modal hides.
* @param [onEmojiSelectedValue=() => {}] - Run a callback when Emoji selected.
* @param emojiPopoverAnchorValue - Element to which Popover is anchored
* @param [anchorOrigin=DEFAULT_ANCHOR_ORIGIN] - Anchor origin for Popover
* @param [onWillShow] - Run a callback when Popover will show
* @param id - Unique id for EmojiPicker
* @param activeEmojiValue - Selected emoji to be highlighted
*/
const showEmojiPicker = (onModalHideValue, onEmojiSelectedValue, emojiPopoverAnchorValue, anchorOrigin, onWillShow, id, activeEmojiValue) => {
const showEmojiPicker = (
onModalHideValue: OnModalHideValue,
onEmojiSelectedValue: OnEmojiSelected,
emojiPopoverAnchorValue: EmojiPopoverAnchor,
anchorOrigin?: AnchorOrigin,
onWillShow?: OnWillShowPicker,
id?: string,
activeEmojiValue?: string,
) => {
HezekielT marked this conversation as resolved.
Show resolved Hide resolved
onModalHide.current = onModalHideValue;
onEmojiSelected.current = onEmojiSelectedValue;
activeEmoji.current = activeEmojiValue;
emojiPopoverAnchorRef.current = emojiPopoverAnchorValue;
const emojiPopoverAnchor = getEmojiPopoverAnchor();
if (emojiPopoverAnchor.current && emojiPopoverAnchor.current.blur) {
// Drop focus to avoid blue focus ring.
emojiPopoverAnchor.current.blur();
}
// Drop focus to avoid blue focus ring.
emojiPopoverAnchor?.current?.blur();

const anchorOriginValue = anchorOrigin || DEFAULT_ANCHOR_ORIGIN;
const anchorOriginValue = anchorOrigin ?? DEFAULT_ANCHOR_ORIGIN;

calculateAnchorPosition(emojiPopoverAnchor.current, anchorOriginValue).then((value) => {
// eslint-disable-next-line es/no-optional-chaining
calculateAnchorPosition(emojiPopoverAnchor?.current, anchorOriginValue).then((value) => {
onWillShow?.();
setIsEmojiPickerVisible(true);
setEmojiPopoverAnchorPosition({
Expand All @@ -95,10 +103,8 @@ const EmojiPicker = forwardRef((props, ref) => {

/**
* Hide the emoji picker menu.
*
* @param {Boolean} isNavigating
*/
const hideEmojiPicker = (isNavigating) => {
const hideEmojiPicker = (isNavigating?: boolean) => {
if (isNavigating) {
onModalHide.current = () => {};
}
Expand All @@ -124,30 +130,24 @@ const EmojiPicker = forwardRef((props, ref) => {

/**
* Callback for the emoji picker to add whatever emoji is chosen into the main input
*
* @param {String} emoji
* @param {Object} emojiObject
*/
const selectEmoji = (emoji, emojiObject) => {
const selectEmoji = (emoji: string, emojiObject: Emoji) => {
// Prevent fast click / multiple emoji selection;
// The first click will hide the emoji picker by calling the hideEmojiPicker() function
if (!isEmojiPickerVisible) {
return;
}

hideEmojiPicker(false);
if (_.isFunction(onEmojiSelected.current)) {
if (typeof onEmojiSelected.current === 'function') {
onEmojiSelected.current(emoji, emojiObject);
}
};

/**
* Whether emoji picker is active for the given id.
*
* @param {String} id
* @return {Boolean}
*/
const isActive = (id) => Boolean(id) && id === activeID;
const isActive = (id: string) => !!id && id === activeID;

const clearActive = () => setActiveID(null);

Expand All @@ -158,14 +158,14 @@ const EmojiPicker = forwardRef((props, ref) => {
useEffect(() => {
const emojiPopoverDimensionListener = Dimensions.addEventListener('change', () => {
const emojiPopoverAnchor = getEmojiPopoverAnchor();
if (!emojiPopoverAnchor.current) {
if (!emojiPopoverAnchor?.current) {
// In small screen width, the window size change might be due to keyboard open/hide, we should avoid hide EmojiPicker in those cases
if (isEmojiPickerVisible && !isSmallScreenWidth) {
hideEmojiPicker();
}
return;
}
calculateAnchorPosition(emojiPopoverAnchor.current, emojiPopoverAnchorOrigin).then((value) => {
calculateAnchorPosition(emojiPopoverAnchor?.current, emojiPopoverAnchorOrigin).then((value) => {
setEmojiPopoverAnchorPosition({
horizontal: value.horizontal,
vertical: value.vertical,
Expand Down Expand Up @@ -201,14 +201,14 @@ const EmojiPicker = forwardRef((props, ref) => {
vertical: emojiPopoverAnchorPosition.vertical,
horizontal: emojiPopoverAnchorPosition.horizontal,
}}
anchorRef={getEmojiPopoverAnchor()}
anchorRef={getEmojiPopoverAnchor() as RefObject<View | HTMLDivElement>}
withoutOverlay
HezekielT marked this conversation as resolved.
Show resolved Hide resolved
popoverDimensions={{
width: CONST.EMOJI_PICKER_SIZE.WIDTH,
height: CONST.EMOJI_PICKER_SIZE.HEIGHT,
}}
anchorAlignment={emojiPopoverAnchorOrigin}
outerStyle={StyleUtils.getOuterModalStyle(windowHeight, props.viewportOffsetTop)}
outerStyle={StyleUtils.getOuterModalStyle(windowHeight, viewportOffsetTop)}
innerContainerStyle={styles.popoverInnerContainer}
anchorDimensions={emojiAnchorDimension.current}
avoidKeyboard
Expand All @@ -223,8 +223,7 @@ const EmojiPicker = forwardRef((props, ref) => {
/>
</PopoverWithMeasuredContent>
);
});
}

EmojiPicker.propTypes = propTypes;
EmojiPicker.displayName = 'EmojiPicker';
export default withViewportOffsetTop(EmojiPicker);
export default withViewportOffsetTop(forwardRef(EmojiPicker));
Loading
Loading