diff --git a/src/components/DisplayNames/displayNamesPropTypes.js b/src/components/DisplayNames/displayNamesPropTypes.js new file mode 100644 index 000000000000..5a94673a9bfa --- /dev/null +++ b/src/components/DisplayNames/displayNamesPropTypes.js @@ -0,0 +1,33 @@ +import PropTypes from 'prop-types'; + +const propTypes = { + // The full title of the DisplayNames component (not split up) + fullTitle: PropTypes.string, + + // Array of objects that map display names to their corresponding tooltip + displayNamesWithTooltips: PropTypes.arrayOf(PropTypes.shape({ + // The name to display in bold + displayName: PropTypes.string, + + // The tooltip to show when the associated name is hovered + tooltip: PropTypes.string, + })), + + // Number of lines before wrapping + numberOfLines: PropTypes.number, + + // Is tooltip needed? + // When true, triggers complex title rendering + tooltipEnabled: PropTypes.bool, + + // Arbitrary styles of the displayName text + textStyles: PropTypes.arrayOf(PropTypes.any), +}; + +const defaultProps = { + numberOfLines: 1, + tooltipEnabled: false, + titleStyles: [], +}; + +export {propTypes, defaultProps}; diff --git a/src/pages/home/sidebar/OptionRowTitle/index.js b/src/components/DisplayNames/index.js similarity index 67% rename from src/pages/home/sidebar/OptionRowTitle/index.js rename to src/components/DisplayNames/index.js index 9f887f2a65d2..72f89e6f1e22 100644 --- a/src/pages/home/sidebar/OptionRowTitle/index.js +++ b/src/components/DisplayNames/index.js @@ -1,15 +1,11 @@ import _ from 'underscore'; import React, {Fragment, PureComponent} from 'react'; -import { - Text, - View, -} from 'react-native'; -import {propTypes, defaultProps} from './OptionRowTitleProps'; -import styles from '../../../../styles/styles'; -import Tooltip from '../../../../components/Tooltip'; -import hasEllipsis from '../../../../libs/hasEllipsis'; +import {Text, View} from 'react-native'; +import {propTypes, defaultProps} from './displayNamesPropTypes'; +import styles from '../../styles/styles'; +import Tooltip from '../Tooltip'; -class OptionRowTitle extends PureComponent { +class DisplayNames extends PureComponent { constructor(props) { super(props); this.containerRef = null; @@ -23,7 +19,10 @@ class OptionRowTitle extends PureComponent { componentDidMount() { this.setState({ - isEllipsisActive: this.containerRef && hasEllipsis(this.containerRef), + isEllipsisActive: this.containerRef + && this.containerRef.offsetWidth + && this.containerRef.scrollWidth + && this.containerRef.offsetWidth < this.containerRef.scrollWidth, }); } @@ -31,7 +30,6 @@ class OptionRowTitle extends PureComponent { * Set the container layout for post calculations * * @param {*} {nativeEvent} - * @memberof OptionRowTitle */ setContainerLayout({nativeEvent}) { this.containerLayout = nativeEvent.layout; @@ -44,11 +42,10 @@ class OptionRowTitle extends PureComponent { * So we shift it by calculating it as follows: * 1. We get the container layout and take the Child inline text node. * 2. Now we get the tooltip original position. - * 3. If inline node's right edge is overflowing the containe's right edge, we set the tooltip to the center + * 3. If inline node's right edge is overflowing the container's right edge, we set the tooltip to the center * of the distance between the left edge of the inline node and right edge of the container. * @param {Number} index Used to get the Ref to the node at the current index. * @returns {Number} Distance to shift the tooltip horizontally - * @memberof OptionRowTitle */ getTooltipShiftX(index) { // Only shift when containerLayout or Refs to text node is available . @@ -70,43 +67,48 @@ class OptionRowTitle extends PureComponent { render() { - const { - option, style, tooltipEnabled, numberOfLines, - } = this.props; - - if (!tooltipEnabled) { - return {option.text}; + if (!this.props.tooltipEnabled) { + // No need for any complex text-splitting, just return a simple text component + return ( + + {this.props.fullTitle} + + ); } + return ( - // Tokenization of string only support 1 numberofLines on Web + // Tokenization of string only support 1 numberOfLines on Web this.containerRef = el} > - {_.map(option.participantsList, (participant, index) => ( + {_.map(this.props.displayNamesToTooltips, ({displayName, tooltip}, index) => ( this.getTooltipShiftX(index)} > {/* // We need to get the refs to all the names which will be used to correct the horizontal position of the tooltip */} this.childRefs[index] = el}> - {participant.displayName} + {displayName} - {index < option.participantsList.length - 1 && } + {index < this.props.displayNamesToTooltips.length - 1 && } ))} - {option.participantsList.length > 1 && this.state.isEllipsisActive + {this.props.displayNamesToTooltips.length > 1 && this.state.isEllipsisActive && ( - - + + {/* There is some Gap for real ellipsis so we are adding 4 `.` to cover */} .... @@ -116,8 +118,8 @@ class OptionRowTitle extends PureComponent { ); } } -OptionRowTitle.propTypes = propTypes; -OptionRowTitle.defaultProps = defaultProps; -OptionRowTitle.displayName = 'OptionRowTitle'; +DisplayNames.propTypes = propTypes; +DisplayNames.defaultProps = defaultProps; +DisplayNames.displayName = 'DisplayNames'; -export default OptionRowTitle; +export default DisplayNames; diff --git a/src/components/DisplayNames/index.native.js b/src/components/DisplayNames/index.native.js new file mode 100644 index 000000000000..8a882f8eb576 --- /dev/null +++ b/src/components/DisplayNames/index.native.js @@ -0,0 +1,20 @@ +import React from 'react'; +import {Text} from 'react-native'; +import {propTypes, defaultProps} from './displayNamesPropTypes'; + +// As we don't have to show tooltips of the Native platform so we simply render the full display names list. +const DisplayNames = ({ + fullTitle, + numberOfLines, + textStyles, +}) => ( + + {fullTitle} + +); + +DisplayNames.propTypes = propTypes; +DisplayNames.defaultProps = defaultProps; +DisplayNames.displayName = 'DisplayNames'; + +export default DisplayNames; diff --git a/src/libs/hasEllipsis/index.js b/src/libs/hasEllipsis/index.js deleted file mode 100644 index d18af237be35..000000000000 --- a/src/libs/hasEllipsis/index.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Does an elment have ellipsis - * - * @param {HTMLElement} el Element to check - * @returns {Boolean} - */ -function hasEllipsis(el) { - return el.offsetWidth < el.scrollWidth; -} - -export default hasEllipsis; diff --git a/src/libs/hasEllipsis/index.native.js b/src/libs/hasEllipsis/index.native.js deleted file mode 100644 index 2d1ec238274a..000000000000 --- a/src/libs/hasEllipsis/index.native.js +++ /dev/null @@ -1 +0,0 @@ -export default () => {}; diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 40e07a82daef..d94eab3d18b6 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import React from 'react'; import {View, Pressable} from 'react-native'; import PropTypes from 'prop-types'; @@ -15,7 +16,7 @@ import MultipleAvatars from '../../components/MultipleAvatars'; import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; import {getReportParticipantsTitle} from '../../libs/reportUtils'; -import OptionRowTitle from './sidebar/OptionRowTitle'; +import DisplayNames from '../../components/DisplayNames'; import {getPersonalDetailsForLogins} from '../../libs/OptionsListUtils'; import {participantPropTypes} from './sidebar/optionPropTypes'; import VideoChatButtonAndMenu from '../../components/VideoChatButtonAndMenu'; @@ -53,11 +54,10 @@ const defaultProps = { const HeaderView = (props) => { const participants = lodashGet(props.report, 'participants', []); - const reportOption = { - text: lodashGet(props.report, 'reportName', ''), - tooltipText: getReportParticipantsTitle(participants), - participantsList: getPersonalDetailsForLogins(participants, props.personalDetails), - }; + const displayNamesWithTooltips = _.map( + getPersonalDetailsForLogins(participants, props.personalDetails), + ({displayName, login}) => ({displayName, tooltip: login}), + ); return ( @@ -92,11 +92,12 @@ const HeaderView = (props) => { secondAvatarStyle={[styles.secondAvatarHovered]} /> - diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 3cc279702809..060f99c8770a 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -110,7 +110,7 @@ class ReportActionItem extends Component { )} > diff --git a/src/pages/home/sidebar/OptionRow.js b/src/pages/home/sidebar/OptionRow.js index 860b23a45090..5212a2584476 100644 --- a/src/pages/home/sidebar/OptionRow.js +++ b/src/pages/home/sidebar/OptionRow.js @@ -14,7 +14,7 @@ import {Pencil, PinCircle, Checkmark} from '../../../components/Icon/Expensicons import MultipleAvatars from '../../../components/MultipleAvatars'; import themeColors from '../../../styles/themes/default'; import Hoverable from '../../../components/Hoverable'; -import OptionRowTitle from './OptionRowTitle'; +import DisplayNames from '../../../components/DisplayNames'; import IOUBadge from '../../../components/IOUBadge'; import colors from '../../../styles/colors'; @@ -109,6 +109,11 @@ const OptionRow = ({ ? hoverStyle.backgroundColor : backgroundColor; const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor; + const displayNamesWithTooltips = _.map( + option.participantsList, + ({displayName, login}) => ({displayName, tooltip: login}), + ); + return ( {hovered => ( @@ -152,14 +157,13 @@ const OptionRow = ({ ) } - - {option.alternateText ? ( ( - - {option.text} - -); - -OptionRowTitle.propTypes = propTypes; -OptionRowTitle.defaultProps = defaultProps; -OptionRowTitle.displayName = 'OptionRowTitle'; - -export default OptionRowTitle; diff --git a/src/styles/styles.js b/src/styles/styles.js index fae27fd6d72c..dd3ca22b7749 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -14,6 +14,7 @@ import whiteSpace from './utilities/whiteSpace'; import wordBreak from './utilities/wordBreak'; import textInputAlignSelf from './utilities/textInputAlignSelf'; import CONST from '../CONST'; +import positioning from './utilities/positioning'; const styles = { // Add all of our utility and helper styles @@ -22,6 +23,7 @@ const styles = { ...flex, ...display, ...overflow, + ...positioning, ...wordBreak, ...whiteSpace, @@ -601,11 +603,7 @@ const styles = { flexShrink: 0, }, - optionDisplayNameTooltipWrapper: { - position: 'relative', - }, - - optionDisplayNameTooltipEllipsis: { + displayNameTooltipEllipsis: { position: 'absolute', opacity: 0, right: 0, diff --git a/src/styles/utilities/positioning.js b/src/styles/utilities/positioning.js index 9514b8f55091..f73954241960 100644 --- a/src/styles/utilities/positioning.js +++ b/src/styles/utilities/positioning.js @@ -3,6 +3,9 @@ * Everything is a multiple of 4 to coincide with the spacing utilities. */ export default { + pRelative: { + position: 'relative', + }, tn4: { top: -16, },