1

I try to use a good pattern to use the Callout, for instance when hovering over a Persona element. Currently I do the following:

  • _clientPersonaElement is a reference to the Element the Callout uses as location: Callout.target
  • state.clientCalloutVisible is set by _onShowClientCallout to show the Callout. It is reset by _onClientCalloutDismiss

Here the code:

export default class ILoggerListItem extends React.Component<IILoggerListItemProps,IILoggerListItemState> {
    private _clientPersonaElement: HTMLElement;

    constructor(props: IILoggerListItemProps) {
        super(props);

        this._onShowClientCallout = this._onShowClientCallout.bind(this);
        this._onClientCalloutDismiss = this._onClientCalloutDismiss.bind(this);

        this.state = {
          clientCalloutVisible: false
        };
    }

    private _onShowClientCallout(person: IInteractionLogPerson, event: React.MouseEvent<Persona>) {
        if(! this.state.clientCalloutVisible)
        {
            this.setState(
            { 
                clientCalloutVisible: true,
            });
        }
    }

    private _onClientCalloutDismiss() {
        if( this.state.clientCalloutVisible)
        {
            this.setState({
                clientCalloutVisible: false
            });
        }
    }

My render looks like:

public render(): JSX.Element {

return (
  <div
    role='row'
    className={ classILoggerItem }
    data-is-focusable={ true } >
    <FocusZone direction={ FocusZoneDirection.horizontal }>
      <div className={ css(styles.itemTaskRow, 'ms-Grid-row') }>

        <div className= {css( 'ms-Grid-col', 'ms-u-sm4')}
          ref={ (targetElement) => this._clientPersonaElement = targetElement } >
          <Persona 
            onMouseOver= {(event) => this._onShowClientCallout(person, event)}
            size= { PersonaSize.regular }
            imageUrl= { person.Picture }
            primaryText= { person.Title }
            secondaryText= { person.MobilePhone }
          />
        </div>

      </div>
    </FocusZone>
    { this.state.clientCalloutVisible && (
      <Callout
        className={ css(styles.iLoggerClientCallout ) }
        gapSpace={ 0 }
        onDismiss={ this._onClientCalloutDismiss }
        target={this._clientPersonaElement}
        setInitialFocus={ true }
      >
        <div className={ css(styles.iLoggerClientCalloutHeader) }>
          <p className={ css(styles.iLoggerClientCalloutTitle, 'ms-font-l') }>
            { this.props.item.Client.Title }
          </p>
        </div>
        <div className={ css(styles.iLoggerClientCalloutInner) }>
          <div className={ css(styles.iLoggerClientCalloutContent) }>
            <p className={ css(styles.iLoggerClientCalloutSubText, 'ms-font-m') }>
              { this.props.item.Client.MobilePhone } <br />
              { this.props.item.Client.EMail }
            </p>
          </div>
          <div className={ css(styles.iLoggerClientCalloutActions) }>
            <Link className={ css(styles.iLoggerClientCalloutLink) } href='http://microsoft.com'>Go to profile</Link>
          </div>
        </div>
      </Callout>
    ) }
  </div>
);
}

I wrap the Persona element in a Div element, so I can get a reference used for the target property of the Callout:

I couldn't get ref=.. on the PersonA to work, because it's not an HtmlElement.

  • target of the Callout can be a MouseEvent, but I couldn't figure out, how to get the Persona.onMouseOver as a MouseEvent.
  • target can be a querySelector string of a valid HTMLElement . Is it possible to reference the Persona element via a querySelector?

Is this a good pattern with the additional div element and ref=.. ? Or would using MouseEvent or querySelector better?

Every feedback is valued and appreciated :-)

3
  • 1
    Is there any error logs on your browser console? Start by putting a debugger on _onShowClientCallout function and check if it is invoking or not. Commented Jul 6, 2017 at 5:36
  • The above code is working. I don't know how to use the MouseEvent for the Callout.target. I tried using the React.MouseEvent<Persona> passed to the onMouseEnter handler, but it failed, because it is not of type MouseEvent. Is the <div> wrapper the best approach for this? Commented Jul 6, 2017 at 6:18
  • Yes, as a workaround and for testing purpose attaching it to wrapper div mouse event is good approach. Commented Jul 6, 2017 at 6:40

1 Answer 1

1

It seems that how to specify the target for a Callout control is not very well documented. In my case, I was trying to display a callout when a command button was clicked in a Command Bar control. I wanted the callout to popup at the location of the clicked command button and so couldn't just wrap the control in a container div and create a react ref for that.

However, I was able to trap the onClick event for the command button and throw it into an IPoint object.

private mouseClickPoint : IPoint;

private onClickOpenCallout = (ev?: any, item?: IContextualMenuItem) : boolean | void  => {
    this.mouseClickPoint = {x: ev.clientX, y: ev.clientY};
}

Note that I needed to cast the event to 'any', in order to access the clientX and clientY properties. You can then use this point as the target in the Callout control.

<Callout target={this.mouseClickPoint}>
    {/* payload goes here */}
</Callout>;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.