none
Multiple CallOut in React SPFX webpart RRS feed

  • Question

  • I want create the 4 CallOuts in SPFX react webpart. The CallOut will open up whenever the user click on the icon marked in red circle in the below image. The CallOut should comeout always beside the icon if the user moves page up and click on icon or page down and click on icon . The CallOut always shows beside the click icon.

     
    Friday, January 10, 2020 9:20 AM

Answers

  • Hi Rajesh,

    The following example code for your reference:

    import * as React from 'react';
    import { DefaultButton, Callout,Icon, DirectionalHint, Link, getTheme, FontWeights, mergeStyleSets, getId } from 'office-ui-fabric-react';
    
    export interface IOfficeUiReactState{
      isCalloutVisible?: boolean; 
      calloutTitle:string;
      calloutContent:string;
      calloutLink:string;
    }
    const theme = getTheme();
    const styles = mergeStyleSets({
      IconArea: {
        verticalAlign: 'top',
        display: 'inline-block',
        textAlign: 'center',
        margin: '0 0px'
      },
      callout: {
        maxWidth: 300
      },
      header: {
        padding: '18px 24px 12px'
      },
      title: [
        theme.fonts.xLarge,
        {
          margin: 0,
          color: theme.palette.neutralPrimary,
          fontWeight: FontWeights.semilight
        }
      ],
      inner: {
        height: '100%',
        padding: '0 24px 20px'
      },
      actions: {
        position: 'relative',
        marginTop: 20,
        width: '100%',
        whiteSpace: 'nowrap'
      },
      subtext: [
        theme.fonts.small,
        {
          margin: 0,
          color: theme.palette.neutralPrimary,
          fontWeight: FontWeights.semilight
        }
      ],
      link: [
        theme.fonts.medium,
        {
          color: theme.palette.neutralPrimary
        }
      ]
    });
    export default class OfficeUiReact extends React.Component<{},IOfficeUiReactState> {
      public state: IOfficeUiReactState = {
        isCalloutVisible: false,
        calloutTitle:"",
        calloutContent:"",
        calloutLink:""
      };
      private _menuButtonElement: HTMLElement | null;
      private _labelId: string = getId('callout-label');
      private _descriptionId: string = getId('callout-description');
    
      public render(): JSX.Element{
        return (
          <div>
             <div id="icon1"  className={styles.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
              <Icon  iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon1"/>
            </div> 
           
            <div id="icon2" style={{marginLeft:"400px"}} className={styles.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
              <Icon  iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon2"/>
            </div>
            <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/>
            <div  id="icon3" className={styles.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
              <Icon  iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon3"/>
            </div>
            <div id="icon4" style={{marginLeft:"400px"}} className={styles.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
              <Icon  iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon4"/>
            </div>
            {this.state.isCalloutVisible && (
              <Callout
                className={styles.callout}
                ariaLabelledBy={this._labelId}
                ariaDescribedBy={this._descriptionId}
                role="alertdialog"
                gapSpace={0}
                directionalHint={DirectionalHint.rightCenter}
                target={this._menuButtonElement}
                onDismiss={this._onCalloutDismiss}
                setInitialFocus={true}
              >
                <div className={styles.header}>
                  <p className={styles.title} id={this._labelId}>
                    {this.state.calloutTitle}
                  </p>
                </div>
                <div className={styles.inner}>
                  <p className={styles.subtext} id={this._descriptionId}>
                    {this.state.calloutContent}
                  </p>
                  <div className={styles.actions}>         
                    <div dangerouslySetInnerHTML={{__html: this.state.calloutLink}} />
                  </div>
                </div>
              </Callout>
            )}          
           </div>
          
        ); 
      }
      private _onShowMenuClicked= (title:any): void =>{
        this._menuButtonElement=document.getElementById(title);
        if(title=="icon1"){
          this.setState({
            isCalloutVisible: !this.state.isCalloutVisible,
            calloutTitle:"My CallOut 1",
            calloutContent:"My CallOut Content 1",
            calloutLink:"<a href='http://bing.com'>Go to Bing1</a>",
          });
        }     
        if(title=="icon2"){
          this.setState({
            isCalloutVisible: !this.state.isCalloutVisible,
            calloutTitle:"My CallOut 2",
            calloutContent:"My CallOut Content 2",
            calloutLink:"<a href='http://microsoft.com'>Go to microsoft</a>"
          });
        }
        if(title=="icon3"){
          this.setState({
            isCalloutVisible: !this.state.isCalloutVisible,
            calloutTitle:"My CallOut 3",
            calloutContent:"My CallOut Content 3",
            calloutLink:"<a href='http://microsoft.com'>Go to microsoft</a>"
          });
        }
        if(title=="icon4"){
          this.setState({
            isCalloutVisible: !this.state.isCalloutVisible,
            calloutTitle:"My CallOut 4",
            calloutContent:"My CallOut Content 4",
            calloutLink:"<a href='http://microsoft.com'>Go to microsoft</a>"
          });
        }
      };
    
      private _onCalloutDismiss = (): void => {
        this.setState({
          isCalloutVisible: false
        });
      };
    }
    

    Best Regards,

    Dennis


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    SharePoint Server 2019 has been released, you can click here to download it.
    Click here to learn new features. Visit the dedicated forum to share, explore and talk to experts about SharePoint Server 2019.

    Monday, January 13, 2020 2:01 AM
    Moderator
  • Hi Rajesh,

    We just modify the "maxWidth" in the code above. Like this.

    callout: {
        maxWidth: 500
    }

    Best Regards,

    Dennis


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    SharePoint Server 2019 has been released, you can click here to download it.
    Click here to learn new features. Visit the dedicated forum to share, explore and talk to experts about SharePoint Server 2019.

    Wednesday, January 22, 2020 7:28 AM
    Moderator

All replies

  • Hi Rajesh,

    The following example code for your reference:

    import * as React from 'react';
    import { DefaultButton, Callout,Icon, DirectionalHint, Link, getTheme, FontWeights, mergeStyleSets, getId } from 'office-ui-fabric-react';
    
    export interface IOfficeUiReactState{
      isCalloutVisible?: boolean; 
      calloutTitle:string;
      calloutContent:string;
      calloutLink:string;
    }
    const theme = getTheme();
    const styles = mergeStyleSets({
      IconArea: {
        verticalAlign: 'top',
        display: 'inline-block',
        textAlign: 'center',
        margin: '0 0px'
      },
      callout: {
        maxWidth: 300
      },
      header: {
        padding: '18px 24px 12px'
      },
      title: [
        theme.fonts.xLarge,
        {
          margin: 0,
          color: theme.palette.neutralPrimary,
          fontWeight: FontWeights.semilight
        }
      ],
      inner: {
        height: '100%',
        padding: '0 24px 20px'
      },
      actions: {
        position: 'relative',
        marginTop: 20,
        width: '100%',
        whiteSpace: 'nowrap'
      },
      subtext: [
        theme.fonts.small,
        {
          margin: 0,
          color: theme.palette.neutralPrimary,
          fontWeight: FontWeights.semilight
        }
      ],
      link: [
        theme.fonts.medium,
        {
          color: theme.palette.neutralPrimary
        }
      ]
    });
    export default class OfficeUiReact extends React.Component<{},IOfficeUiReactState> {
      public state: IOfficeUiReactState = {
        isCalloutVisible: false,
        calloutTitle:"",
        calloutContent:"",
        calloutLink:""
      };
      private _menuButtonElement: HTMLElement | null;
      private _labelId: string = getId('callout-label');
      private _descriptionId: string = getId('callout-description');
    
      public render(): JSX.Element{
        return (
          <div>
             <div id="icon1"  className={styles.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
              <Icon  iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon1"/>
            </div> 
           
            <div id="icon2" style={{marginLeft:"400px"}} className={styles.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
              <Icon  iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon2"/>
            </div>
            <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/>
            <div  id="icon3" className={styles.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
              <Icon  iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon3"/>
            </div>
            <div id="icon4" style={{marginLeft:"400px"}} className={styles.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
              <Icon  iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon4"/>
            </div>
            {this.state.isCalloutVisible && (
              <Callout
                className={styles.callout}
                ariaLabelledBy={this._labelId}
                ariaDescribedBy={this._descriptionId}
                role="alertdialog"
                gapSpace={0}
                directionalHint={DirectionalHint.rightCenter}
                target={this._menuButtonElement}
                onDismiss={this._onCalloutDismiss}
                setInitialFocus={true}
              >
                <div className={styles.header}>
                  <p className={styles.title} id={this._labelId}>
                    {this.state.calloutTitle}
                  </p>
                </div>
                <div className={styles.inner}>
                  <p className={styles.subtext} id={this._descriptionId}>
                    {this.state.calloutContent}
                  </p>
                  <div className={styles.actions}>         
                    <div dangerouslySetInnerHTML={{__html: this.state.calloutLink}} />
                  </div>
                </div>
              </Callout>
            )}          
           </div>
          
        ); 
      }
      private _onShowMenuClicked= (title:any): void =>{
        this._menuButtonElement=document.getElementById(title);
        if(title=="icon1"){
          this.setState({
            isCalloutVisible: !this.state.isCalloutVisible,
            calloutTitle:"My CallOut 1",
            calloutContent:"My CallOut Content 1",
            calloutLink:"<a href='http://bing.com'>Go to Bing1</a>",
          });
        }     
        if(title=="icon2"){
          this.setState({
            isCalloutVisible: !this.state.isCalloutVisible,
            calloutTitle:"My CallOut 2",
            calloutContent:"My CallOut Content 2",
            calloutLink:"<a href='http://microsoft.com'>Go to microsoft</a>"
          });
        }
        if(title=="icon3"){
          this.setState({
            isCalloutVisible: !this.state.isCalloutVisible,
            calloutTitle:"My CallOut 3",
            calloutContent:"My CallOut Content 3",
            calloutLink:"<a href='http://microsoft.com'>Go to microsoft</a>"
          });
        }
        if(title=="icon4"){
          this.setState({
            isCalloutVisible: !this.state.isCalloutVisible,
            calloutTitle:"My CallOut 4",
            calloutContent:"My CallOut Content 4",
            calloutLink:"<a href='http://microsoft.com'>Go to microsoft</a>"
          });
        }
      };
    
      private _onCalloutDismiss = (): void => {
        this.setState({
          isCalloutVisible: false
        });
      };
    }
    

    Best Regards,

    Dennis


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    SharePoint Server 2019 has been released, you can click here to download it.
    Click here to learn new features. Visit the dedicated forum to share, explore and talk to experts about SharePoint Server 2019.

    Monday, January 13, 2020 2:01 AM
    Moderator
  • I implemented the above code but if i click 'Icon1' & 'Icon2' & 'Icon3' the popup initially coming but finally the popup displaying near Icon4 as shown in the below screen.

    Monday, January 20, 2020 6:17 AM
  • Hi Rajesh,

    I suggest you create a new SPFx web part and use my code above to check if it works.

    Note: please use the different "title" and "id" for HTML elements.

    <div id="icon1"  className={styles.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
      <Icon  iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon1"/>
    </div> 
    <div id="icon2" style={{marginLeft:"400px"}} className={styles.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
      <Icon  iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon2"/>
    </div>
    <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/>
    <div  id="icon3" className={styles.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
      <Icon  iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon3"/>
    </div>
    <div id="icon4" style={{marginLeft:"400px"}} className={styles.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
      <Icon  iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon4"/>
    </div>

    Best Regards,

    Dennis


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    SharePoint Server 2019 has been released, you can click here to download it.
    Click here to learn new features. Visit the dedicated forum to share, explore and talk to experts about SharePoint Server 2019.

    Monday, January 20, 2020 9:02 AM
    Moderator
  • I used the different ID & Title as shown in below and this is my PSFX react webpart. If i clcik icon1 the pope up initially coming near icon1 then moving to icon4 all icons icon1 icon2 icon3 moving to icon4

    <div id="icon1" className={styles2.IconArea} ref={menuButton1 => (this._menuButtonElement = menuButton1)}>
    <Icon iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon1"/>
    </div>
    <div id="icon2" style={{margin"color:#ce9178;">"400px"}} className={styles2.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
    <Icon iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon2"/>
    </div>
    <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/>
    <div id="icon3" className={styles2.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
    <Icon iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon3"/>
    </div>
    <div id="icon4" style={{margin"color:#ce9178;">"400px"}} className={styles2.IconArea} ref={menuButton => (this._menuButtonElement = menuButton)}>
    <Icon iconName='Info' onClick={e => this._onShowMenuClicked(e.currentTarget.title)} title="icon4"/>
    </div>


    Monday, January 20, 2020 10:02 AM
  • Hi Dennis,

    Its working as per your above code.


    Wednesday, January 22, 2020 5:31 AM
  • How to increase the call out width?
    Wednesday, January 22, 2020 7:06 AM
  • Hi Rajesh,

    We just modify the "maxWidth" in the code above. Like this.

    callout: {
        maxWidth: 500
    }

    Best Regards,

    Dennis


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    SharePoint Server 2019 has been released, you can click here to download it.
    Click here to learn new features. Visit the dedicated forum to share, explore and talk to experts about SharePoint Server 2019.

    Wednesday, January 22, 2020 7:28 AM
    Moderator
  • I want CallOut Content as similar to below image 

    Then how to change the below code 

    if(title=="icon1"){
    this.setState({
    isCalloutVisible: !this.state.isCalloutVisible,
    calloutTitle:"My CallOut 1",
    calloutContent:"My CallOut Content 1 ",
    calloutLink:"<a href='http://bing.com'>Go to Bing1</a>",
    });
    }

    Wednesday, January 22, 2020 8:47 AM