setCustomPanel in react with typescript

Is there a way to render a react component in setCustomPanels render function? I can’t find any examples or documentation on how to put anything there other than simple html node elements.

example:

var myCustomPanel = {
      tab:{
        dataElement: 'customPanelTab',
        title: 'customPanelTab',
        img: 'https://www.pdftron.com/favicon-32x32.png',
      },
      panel: {
        dataElement: 'customPanel',
        render: function() {
          return <SomeReactComponent />;
        }
      }
    };

    instance.UI.setCustomPanel(myCustomPanel);

Hello @mark.h,

The render function is expected to return one of two:

  • valid HTML Element
  • React Element

You can find our documentation here.

Please let me know how this works for you, and if you have any further questions.

Best regards,
Maggie V.
Software Developer
PDFTron

@maggiev would you be able to give an example of rendering a react element? The documentation only has the html element example. The following is the TS error I get that I cant seem to find a way around:

TS2345: Argument of type '{ tab: { dataElement: string; title: string; img: string; }; panel: { dataElement: string; render: () => () => Element; }; }' is not assignable to parameter of type '{ tab: { dataElement: string; title: string; img: string; }; panel: { dataElement: string; render: renderCustomPanel; }; }'.
  The types returned by 'panel.render()' are incompatible between these types.
    Type '() => Element' is missing the following properties from type 'HTMLElement': accessKey, accessKeyLabel, autocapitalize, dir, and 277 more.

This seemed to fix it

const leftPanelLayout: any = () => {
        const items: any[] = [];
        redactSearchPatterns.forEach(pattern => {
          items.push(<><div className='left-column' style={{marginTop: 12}}><input type="checkbox" value={pattern.type} /> <span className='patternLabel'>{pattern?.label}</span></div></>);
        });
        return (
          <div className='patternContainer settings-row'>
            <h3>Patterns</h3>
            {items.length > 0 ? (items.map(item => (item))) : ("<p>No patterns</p>")}
          </div>
        );
      }
      
      const patternPanel = {
        tab:{
          dataElement: 'patternPanelTab',
          title: 'patternPanelTab',
          img: 'https://www.pngkit.com/png/detail/113-1138611_line-swirl-vector-free-swirl-svg.png',
        },
        panel: {
          dataElement: 'patternPanel',
          render: leftPanelLayout
        }
      };

Hi @mark.h,

I’m glad you found a solution. Not sure what didn’t work previously, could be a TS typing issue but here is a basic example of how to render with a react element.

import React, { useRef, useEffect } from 'react';
import WebViewer from '@pdftron/webviewer';
import './App.css';

const BasicReactComponent = () => {
  return (
    <div onClick={() => console.log('hello there')}>
      Hello there
    </div>
  );
};

const App = () => {
  const viewer = useRef(null);

  // if using a class, equivalent of componentDidMount 
  useEffect(() => {
    WebViewer(
      {
        path: '/webviewer/lib',
        initialDoc: '/files/PDFTRON_about.pdf',
      },
      viewer.current,
    ).then((instance) => {
      const { documentViewer, annotationManager, Annotations } = instance.Core;

      const myCustomPanel = {
        tab:{
          dataElement: 'customPanelTab',
          title: 'customPanelTab',
          img: 'https://www.pdftron.com/favicon-32x32.png',
        },
        panel: {
          dataElement: 'customPanel',
          render: function() {
            return <BasicReactComponent />;
          }
        }
      };
  
      instance.UI.setCustomPanel(myCustomPanel);
    });
  }, []);

  return (
    <div className="App">
      <div className="header">React sample</div>
      <div className="webviewer" ref={viewer}></div>
    </div>
  );
};

export default App;

Best regards,
Maggie V.
Software Developer
PDFTron

@maggiev Yeah there is a type issue if you do it the way you suggest that im not sure how to get around. For now ill just do it the way I pasted.