diff --git a/packages/dockview/src/__tests__/react.spec.tsx b/packages/dockview/src/__tests__/react.spec.tsx index 298d96fed..9725cc0ff 100644 --- a/packages/dockview/src/__tests__/react.spec.tsx +++ b/packages/dockview/src/__tests__/react.spec.tsx @@ -1,4 +1,4 @@ -import { ReactPart } from '../react'; +import { ReactPart, isReactComponent } from '../react'; import * as React from 'react'; import { render, screen, act } from '@testing-library/react'; @@ -40,6 +40,25 @@ describe('react', () => { expect(screen.getByTestId('valueB').textContent).toBe('22'); }); }); + + describe('isReactElement', () => { + test('functional component', () => { + const FunctionalComponent: React.FC = () =>
; + expect(isReactComponent(FunctionalComponent)).toBeTruthy(); + }); + + test('React.memo component', () => { + const memoComponent = React.memo(() =>
); + expect(isReactComponent(memoComponent)).toBeTruthy(); + }); + + test('forward ref component', () => { + const ForwardRefComponent = React.forwardRef((props, ref) => ( +
+ )); + expect(isReactComponent(ForwardRefComponent)).toBeTruthy(); + }); + }); }); const Component = (props: TestInterface) => { diff --git a/packages/dockview/src/react.ts b/packages/dockview/src/react.ts index 378112165..a87593536 100644 --- a/packages/dockview/src/react.ts +++ b/packages/dockview/src/react.ts @@ -102,14 +102,14 @@ export class ReactPart

throw new Error('invalid operation: resource is already disposed'); } - if (typeof this.component !== 'function') { + if (!isReactComponent(this.component)) { /** * we know this isn't a React.FunctionComponent so throw an error here. - * if we do not intercept this the React library will throw a very obsure error - * for the same reason, at least at this point we will emit a sensible stacktrace. + * if we do not intercept then React library will throw a very obsure error + * for the same reason... at least at this point we will emit a sensible stacktrace. */ throw new Error( - 'Invalid Operation. dockview only supports React Functional Components.' + 'Dockview: Only React.memo(...), React.ForwardRef(...) and functional components are accepted as components' ); } @@ -192,9 +192,15 @@ export const usePortalsLifecycle: PortalLifecycleHook = () => { return [portals, addPortal]; }; -// it does the job... -export function isReactElement( - element: unknown -): element is React.ReactElement { - return !!(element as React.ReactElement)?.type; + +export function isReactComponent(component: any): boolean { + /** + * Yes, we could use "react-is" but that would introduce an unwanted peer dependency + * so for now we will check in a rather crude fashion... + */ + return ( + typeof component === 'function' /** Functional Componnts */ || + !!(component as React.ExoticComponent) + ?.$$typeof /** React.memo(...) Components */ + ); }