All files / src/utilities FocusableTooltipText.tsx

82.85% Statements 29/35
60% Branches 9/15
85.71% Functions 6/7
80% Lines 24/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 8128x 28x 28x   28x                               28x 216x         216x   216x       216x     28x 269x   269x                         28x 216x 216x           28x 178x 178x     216x               216x 216x 25x     191x   28x  
import * as React from 'react';
import { hasOverflow, ITooltipHostProps, TooltipHost, TooltipOverflowMode } from '@fluentui/react';
import { getAccessibleDataObject } from './index';
import { IAccessibilityProps } from '../types/index';
import { Async } from '../Utilities';
 
interface IFocusableTooltipTextProps {
  className?: ITooltipHostProps['hostClassName'];
  content?: ITooltipHostProps['content'];
  accessibilityData?: IAccessibilityProps;
}
 
interface IFocusableTooltipTextState {
  textOverflow: boolean;
}
 
/**
 * Component to make the text focusable when the overflowed content is clipped
 * because of the CSS text-overflow property.
 */
export class FocusableTooltipText extends React.Component<IFocusableTooltipTextProps, IFocusableTooltipTextState> {
  private _tooltipChild = React.createRef<HTMLSpanElement>();
  private _resizeObserver?: ResizeObserver;
  private _async: Async;
 
  constructor(props: IFocusableTooltipTextProps) {
    super(props);
 
    this.state = {
      textOverflow: false,
    };
 
    this._async = new Async(this);
  }
 
  public render(): React.ReactNode {
    const { className, content, accessibilityData } = this.props;
 
    return (
      <TooltipHost overflowMode={TooltipOverflowMode.Self} hostClassName={className} content={content}>
        <span
          {...getAccessibleDataObject(accessibilityData)}
          ref={this._tooltipChild}
          data-is-focusable={this.state.textOverflow}
        >
          {content}
        </span>
      </TooltipHost>
    );
  }
 
  public componentDidMount(): void {
    const overflowElement = this._getTargetElement();
    Iif (window.ResizeObserver && overflowElement) {
      this._resizeObserver = new ResizeObserver(this._async.debounce(this._checkTextOverflow, 500));
      this._resizeObserver.observe(overflowElement);
    }
  }
 
  public componentWillUnmount(): void {
    this._resizeObserver?.disconnect();
    this._async.dispose();
  }
 
  private _checkTextOverflow = (): void => {
    const overflowElement = this._getTargetElement();
    const textOverflow = !!overflowElement && hasOverflow(overflowElement);
    Iif (textOverflow !== this.state.textOverflow) {
      this.setState({ textOverflow });
    }
  };
 
  private _getTargetElement = (): HTMLElement | undefined => {
    if (!this._tooltipChild.current || !this._tooltipChild.current.parentElement) {
      return undefined;
    }
 
    return this._tooltipChild.current.parentElement;
  };
}