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 'MentionSuggestions.js' component to TypeScript #30514

Merged
Original file line number Diff line number Diff line change
@@ -1,73 +1,61 @@
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import _ from 'underscore';
import getStyledTextArray from '@libs/GetStyledTextArray';
import styles from '@styles/styles';
import * as StyleUtils from '@styles/StyleUtils';
import themeColors from '@styles/themes/default';
import CONST from '@src/CONST';
import {Icon} from '@src/types/onyx/OnyxCommon';
import AutoCompleteSuggestions from './AutoCompleteSuggestions';
import Avatar from './Avatar';
import avatarPropTypes from './avatarPropTypes';
import Text from './Text';

const propTypes = {
/** The index of the highlighted mention */
highlightedMentionIndex: PropTypes.number,
type Mention = {
/** Display name of the user */
text: string;

/** Array of suggested mentions */
mentions: PropTypes.arrayOf(
PropTypes.shape({
/** Display name of the user */
text: PropTypes.string,
/** Email/phone number of the user */
alternateText: string;

/** Array of icons of the user. We use the first element of this array */
icons: Icon[];
};

/** Email/phone number of the user */
alternateText: PropTypes.string,
type MentionSuggestionsProps = {
/** The index of the highlighted mention */
highlightedMentionIndex?: number;

/** Array of icons of the user. We use the first element of this array */
icons: PropTypes.arrayOf(avatarPropTypes),
}),
).isRequired,
/** Array of suggested mentions */
mentions: Mention[];

/** Fired when the user selects an mention */
onSelect: PropTypes.func.isRequired,
onSelect: () => void;

/** Mention prefix that follows the @ sign */
prefix: PropTypes.string.isRequired,
prefix: string;

/** Show that we can use large mention picker.
* Depending on available space and whether the input is expanded, we can have a small or large mention suggester.
* When this value is false, the suggester will have a height of 2.5 items. When this value is true, the height can be up to 5 items. */
isMentionPickerLarge: PropTypes.bool.isRequired,
isMentionPickerLarge: boolean;

/** Meaures the parent container's position and dimensions. */
measureParentContainer: PropTypes.func,
};

const defaultProps = {
highlightedMentionIndex: 0,
measureParentContainer: () => {},
measureParentContainer: () => void;
};

/**
* Create unique keys for each mention item
* @param {Object} item
* @param {Number} index
* @returns {String}
*/
const keyExtractor = (item) => item.alternateText;
const keyExtractor = (item: Mention) => item.alternateText;

function MentionSuggestions(props) {
function MentionSuggestions({prefix, mentions, highlightedMentionIndex = 0, onSelect, isMentionPickerLarge, measureParentContainer = () => {}}: MentionSuggestionsProps) {
/**
* Render a suggestion menu item component.
* @param {Object} item
* @returns {JSX.Element}
*/
const renderSuggestionMenuItem = (item) => {
const renderSuggestionMenuItem = (item: Mention) => {
const isIcon = item.text === CONST.AUTO_COMPLETE_SUGGESTER.HERE_TEXT;
const styledDisplayName = getStyledTextArray(item.text, props.prefix);
const styledHandle = item.text === item.alternateText ? '' : getStyledTextArray(item.alternateText, props.prefix);
const styledDisplayName = getStyledTextArray(item.text, prefix);
const styledHandle = item.text === item.alternateText ? undefined : getStyledTextArray(item.alternateText, prefix);

return (
<View style={[styles.autoCompleteSuggestionContainer, styles.ph2]}>
Expand All @@ -85,8 +73,9 @@ function MentionSuggestions(props) {
style={[styles.mentionSuggestionsText, styles.flexShrink1]}
numberOfLines={1}
>
{_.map(styledDisplayName, ({text, isColored}, i) => (
{styledDisplayName?.map(({text, isColored}, i) => (
<Text
// eslint-disable-next-line react/no-array-index-key
key={`${text}${i}`}
style={[StyleUtils.getColoredBackgroundStyle(isColored), styles.mentionSuggestionsDisplayName]}
>
Expand All @@ -98,13 +87,13 @@ function MentionSuggestions(props) {
style={[styles.mentionSuggestionsText, styles.flex1]}
numberOfLines={1}
>
{_.map(
styledHandle,
{styledHandle?.map(
({text, isColored}, i) =>
text !== '' && (
Boolean(text) && (
<Text
// eslint-disable-next-line react/no-array-index-key
key={`${text}${i}`}
style={[StyleUtils.getColoredBackgroundStyle(isColored), styles.mentionSuggestionsHandle, {...(isColored && {color: styles.text})}]}
style={[StyleUtils.getColoredBackgroundStyle(isColored), styles.mentionSuggestionsHandle]}
MonilBhavsar marked this conversation as resolved.
Show resolved Hide resolved
>
{text}
</Text>
Expand All @@ -117,20 +106,18 @@ function MentionSuggestions(props) {

return (
<AutoCompleteSuggestions
suggestions={props.mentions}
suggestions={mentions}
renderSuggestionMenuItem={renderSuggestionMenuItem}
keyExtractor={keyExtractor}
highlightedSuggestionIndex={props.highlightedMentionIndex}
onSelect={props.onSelect}
isSuggestionPickerLarge={props.isMentionPickerLarge}
highlightedSuggestionIndex={highlightedMentionIndex}
onSelect={onSelect}
isSuggestionPickerLarge={isMentionPickerLarge}
accessibilityLabelExtractor={keyExtractor}
measureParentContainer={props.measureParentContainer}
measureParentContainer={measureParentContainer}
/>
);
}

MentionSuggestions.propTypes = propTypes;
MentionSuggestions.defaultProps = defaultProps;
MentionSuggestions.displayName = 'MentionSuggestions';

export default MentionSuggestions;
2 changes: 1 addition & 1 deletion src/styles/StyleUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1018,7 +1018,7 @@ function getAutoCompleteSuggestionContainerStyle(itemsHeight: number): ViewStyle
/**
* Select the correct color for text.
*/
function getColoredBackgroundStyle(isColored: boolean): TextStyle {
function getColoredBackgroundStyle(isColored: boolean): StyleProp<TextStyle> {
return {backgroundColor: isColored ? themeColors.link : undefined};
}

Expand Down
21 changes: 0 additions & 21 deletions src/styles/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,10 +328,6 @@ const styles = (theme: ThemeColors) =>
textAlign: 'left',
},

textUnderline: {
textDecorationLine: 'underline',
},

verticalAlignMiddle: {
verticalAlign: 'middle',
},
Expand Down Expand Up @@ -392,10 +388,6 @@ const styles = (theme: ThemeColors) =>
fontSize: variables.fontSizeLarge,
},

textXLarge: {
fontSize: variables.fontSizeXLarge,
},

textXXLarge: {
fontSize: variables.fontSizeXXLarge,
},
Expand All @@ -415,11 +407,6 @@ const styles = (theme: ThemeColors) =>
fontWeight: fontWeightBold,
},

textItalic: {
fontFamily: fontFamily.EXP_NEUE_ITALIC,
fontStyle: 'italic',
},

textHeadline: {
...headlineFont,
...whiteSpace.preWrap,
Expand All @@ -436,10 +423,6 @@ const styles = (theme: ThemeColors) =>
lineHeight: variables.lineHeightSizeh1,
},

textDecorationNoLine: {
textDecorationLine: 'none',
},

textWhite: {
color: theme.textLight,
},
Expand All @@ -448,10 +431,6 @@ const styles = (theme: ThemeColors) =>
color: theme.link,
},

textUppercase: {
textTransform: 'uppercase',
},

Copy link
Contributor

Choose a reason for hiding this comment

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

why did You remove this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After the removal of nonexistent styles.text, the script findUnusedKeys starts failing with these styles

we do not use these styles in the project

textNoWrap: {
...whiteSpace.noWrap,
},
Expand Down
7 changes: 5 additions & 2 deletions src/types/onyx/OnyxCommon.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import {SvgProps} from 'react-native-svg';
import {ValueOf} from 'type-fest';
import CONST from '@src/CONST';

Expand All @@ -11,9 +12,11 @@ type ErrorFields<TKey extends string = string> = Record<TKey, Errors | null | un
type Errors = Record<string, string>;

type Icon = {
source: React.ReactNode | string;
type: 'avatar' | 'workspace';
source: string | React.FC<SvgProps>;
type: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE;
name: string;
id: number | string;
fallbackIcon?: string | React.FC<SvgProps>;
};

export type {Icon, PendingAction, PendingFields, ErrorFields, Errors};
Loading