import type { FunctionComponent, MutableRefObject } from 'react'; import React, { useEffect, useMemo, useRef } from 'react'; import type { Context } from '@atlassian/clientside-extensions-registry'; import { PanelExtension } from '@atlassian/clientside-extensions'; export interface PanelHandlerProps { render: PanelExtension.PanelRenderExtension; RootType?: 'div' | 'span'; /* Hello dear reader of inline comments. You might rightfully ask: what the hell is "context" doing here. Well turns out you are not the first. The reason is, that async-panel is reusing the panel handler under the hood. And the only way to let the panel have access to the context is by passing it through via a "contextProvider" (check the AsyncPanelExtension). So yeah, that is why we are here. And that is why "context" has to stay. */ context?: Context<{}>; } const isDiv = (RooType: string): RooType is 'div' => RooType === 'div'; export const PanelHandler: FunctionComponent = ({ render, RootType = 'div', context }) => { const panelApi = useMemo(() => new PanelExtension.Api(), []); const ref = useRef(null); useEffect( function callMountAndUnmountLifecycleCallbacks() { const node = ref.current; if (render) { render(panelApi, context); } if (node) { panelApi.getRenderCallback()(node); } return () => { if (node) { panelApi.getCleanupCallback()(node); } }; }, [panelApi, render, context], ); if (isDiv(RootType)) { return } />; } return } />; };