1- import { Fragment , useEffect , useRef , useState } from 'react' ;
1+ import { Fragment , useEffect , useRef , useState , forwardRef } from 'react' ;
22import styles from '@patternfly/react-styles/css/components/Truncate/truncate' ;
33import { css } from '@patternfly/react-styles' ;
4- import { Tooltip , TooltipPosition } from '../Tooltip' ;
4+ import { Tooltip , TooltipPosition , TooltipProps } from '../Tooltip' ;
5+ import { getReferenceElement } from '../../helpers' ;
56import { getResizeObserver } from '../../helpers/resizeObserver' ;
67
78export enum TruncatePosition {
@@ -17,11 +18,15 @@ const truncateStyles = {
1718
1819const minWidthCharacters : number = 12 ;
1920
20- export interface TruncateProps extends React . HTMLProps < HTMLSpanElement > {
21+ export interface TruncateProps extends Omit < React . HTMLProps < HTMLSpanElement | HTMLAnchorElement > , 'ref' > {
2122 /** Class to add to outer span */
2223 className ?: string ;
2324 /** Text to truncate */
2425 content : string ;
26+ /** An HREF to turn the truncate wrapper into an anchor element. For more custom control, use the
27+ * tooltipProps with a triggerRef property passed in.
28+ */
29+ href ?: string ;
2530 /** The number of characters displayed in the second half of a middle truncation. This will be overridden by
2631 * the maxCharsDisplayed prop.
2732 */
@@ -52,24 +57,24 @@ export interface TruncateProps extends React.HTMLProps<HTMLSpanElement> {
5257 | 'left-end'
5358 | 'right-start'
5459 | 'right-end' ;
55- /** @hide The element whose parent to reference when calculating whether truncation should occur. This must be an ancestor
56- * of the ClipboardCopy, and must have a valid width value. For internal use only, do not use as it is not part of the public API
57- * and is subject to change.
58- */
59- refToGetParent ?: React . RefObject < any > ;
60+ /** Additional props to pass to the tooltip. */
61+ tooltipProps ?: Omit < TooltipProps , 'content' > ;
62+ /** @hide Forwarded ref */
63+ innerRef ?: React . Ref < any > ;
6064}
6165
6266const sliceTrailingContent = ( str : string , slice : number ) => [ str . slice ( 0 , str . length - slice ) , str . slice ( - slice ) ] ;
6367
64- export const Truncate : React . FunctionComponent < TruncateProps > = ( {
68+ const TruncateBase : React . FunctionComponent < TruncateProps > = ( {
6569 className,
70+ href,
6671 position = 'end' ,
6772 tooltipPosition = 'top' ,
73+ tooltipProps,
6874 trailingNumChars = 7 ,
6975 maxCharsDisplayed,
7076 omissionContent = '\u2026' ,
7177 content,
72- refToGetParent,
7378 ...props
7479} : TruncateProps ) => {
7580 const [ isTruncated , setIsTruncated ] = useState ( true ) ;
@@ -78,7 +83,8 @@ export const Truncate: React.FunctionComponent<TruncateProps> = ({
7883 const [ shouldRenderByMaxChars , setShouldRenderByMaxChars ] = useState ( maxCharsDisplayed > 0 ) ;
7984
8085 const textRef = useRef < HTMLElement > ( null ) ;
81- const subParentRef = useRef < HTMLDivElement > ( null ) ;
86+ const defaultSubParentRef = useRef < any > ( null ) ;
87+ const subParentRef = tooltipProps ?. triggerRef || defaultSubParentRef ;
8288 const observer = useRef ( null ) ;
8389
8490 if ( maxCharsDisplayed <= 0 ) {
@@ -108,11 +114,14 @@ export const Truncate: React.FunctionComponent<TruncateProps> = ({
108114 if ( textRef && textRef . current && ! textElement ) {
109115 setTextElement ( textRef . current ) ;
110116 }
117+ } , [ textRef , textElement ] ) ;
111118
112- if ( ( refToGetParent ?. current || ( subParentRef ?. current && subParentRef . current . parentElement ) ) && ! parentElement ) {
113- setParentElement ( refToGetParent ?. current . parentElement || subParentRef ?. current . parentElement ) ;
119+ useEffect ( ( ) => {
120+ const refElement = getReferenceElement ( subParentRef ) ;
121+ if ( refElement ?. parentElement && ! parentElement ) {
122+ setParentElement ( refElement . parentElement ) ;
114123 }
115- } , [ textRef , subParentRef , textElement , parentElement ] ) ;
124+ } , [ subParentRef , parentElement ] ) ;
116125
117126 useEffect ( ( ) => {
118127 if ( textElement && parentElement && ! observer . current && ! shouldRenderByMaxChars ) {
@@ -222,25 +231,37 @@ export const Truncate: React.FunctionComponent<TruncateProps> = ({
222231 ) ;
223232 } ;
224233
234+ const TruncateWrapper = href ? 'a' : 'span' ;
225235 const truncateBody = (
226- < span
227- ref = { subParentRef }
236+ < TruncateWrapper
237+ ref = { ! tooltipProps ?. triggerRef ? ( subParentRef as React . MutableRefObject < any > ) : null }
238+ href = { href }
228239 className = { css ( styles . truncate , shouldRenderByMaxChars && styles . modifiers . fixed , className ) }
229- { ...( isTruncated && { tabIndex : 0 } ) }
240+ { ...( isTruncated && ! href && ! tooltipProps ?. triggerRef && { tabIndex : 0 } ) }
230241 { ...props }
231242 >
232243 { ! shouldRenderByMaxChars ? renderResizeObserverContent ( ) : renderMaxDisplayContent ( ) }
233- </ span >
244+ </ TruncateWrapper >
234245 ) ;
235246
236247 return (
237248 < >
238249 { isTruncated && (
239- < Tooltip hidden = { ! isTruncated } position = { tooltipPosition } content = { content } triggerRef = { subParentRef } />
250+ < Tooltip
251+ hidden = { ! isTruncated }
252+ position = { tooltipPosition }
253+ content = { content }
254+ triggerRef = { subParentRef }
255+ { ...tooltipProps }
256+ />
240257 ) }
241258 { truncateBody }
242259 </ >
243260 ) ;
244261} ;
245262
263+ export const Truncate = forwardRef ( ( props : TruncateProps , ref : React . Ref < HTMLAnchorElement | HTMLSpanElement > ) => (
264+ < TruncateBase innerRef = { ref } { ...props } />
265+ ) ) ;
266+
246267Truncate . displayName = 'Truncate' ;
0 commit comments