diff --git a/public/app/features/plugins/extensions/validators.test.tsx b/public/app/features/plugins/extensions/validators.test.tsx
index afaaa04b543..b57f1f9e3a8 100644
--- a/public/app/features/plugins/extensions/validators.test.tsx
+++ b/public/app/features/plugins/extensions/validators.test.tsx
@@ -272,6 +272,18 @@ describe('Plugin Extension Validators', () => {
expect(isReactComponent(() =>
Some text
)).toBe(true);
});
+ it('should return TRUE if we pass in a component wrapped with React.memo()', () => {
+ const Component = () => Some text
;
+ const wrapped = React.memo(() => (
+
+
+
+ ));
+ wrapped.displayName = 'MyComponent';
+
+ expect(isReactComponent(wrapped)).toBe(true);
+ });
+
it('should return FALSE if we pass in a valid React component', () => {
expect(isReactComponent('Foo bar')).toBe(false);
expect(isReactComponent(123)).toBe(false);
diff --git a/public/app/features/plugins/extensions/validators.ts b/public/app/features/plugins/extensions/validators.ts
index 4ea9d9072c0..add198e178f 100644
--- a/public/app/features/plugins/extensions/validators.ts
+++ b/public/app/features/plugins/extensions/validators.ts
@@ -129,7 +129,14 @@ export function isPromise(value: unknown): value is Promise {
}
export function isReactComponent(component: unknown): component is React.ComponentType {
+ const hasReactTypeProp = (obj: unknown): obj is { $$typeof: Symbol } =>
+ typeof obj === 'object' && obj !== null && '$$typeof' in obj;
+
+ // The sandbox wraps the plugin components with React.memo.
+ const isReactMemoObject = (obj: unknown): boolean =>
+ hasReactTypeProp(obj) && obj.$$typeof === Symbol.for('react.memo');
+
// We currently don't have any strict runtime-checking for this.
// (The main reason is that we don't want to start depending on React implementation details.)
- return typeof component === 'function';
+ return typeof component === 'function' || isReactMemoObject(component);
}