diff --git a/desktop/app/src/__tests__/PluginContainer.node.tsx b/desktop/app/src/__tests__/PluginContainer.node.tsx
index 8a63baa94..a0fe4cbf0 100644
--- a/desktop/app/src/__tests__/PluginContainer.node.tsx
+++ b/desktop/app/src/__tests__/PluginContainer.node.tsx
@@ -16,6 +16,7 @@ import {
SandyPluginDefinition,
FlipperClient,
TestUtils,
+ usePlugin,
} from 'flipper-plugin';
import {selectPlugin} from '../reducers/connections';
@@ -92,8 +93,17 @@ test('PluginContainer can render Sandy plugins', async () => {
function MySandyPlugin() {
renders++;
- const sandyContext = useContext(SandyPluginContext);
- expect(sandyContext).not.toBe(null);
+ const sandyApi = usePlugin(plugin);
+ expect(Object.keys(sandyApi)).toEqual([
+ 'connectedStub',
+ 'disconnectedStub',
+ ]);
+ expect(() => {
+ // eslint-disable-next-line
+ usePlugin(function bla() {
+ return {};
+ });
+ }).toThrowError(/didn't match the type of the requested plugin/);
return
Hello from Sandy
;
}
diff --git a/desktop/flipper-plugin/src/__tests__/TestPlugin.tsx b/desktop/flipper-plugin/src/__tests__/TestPlugin.tsx
index 4ef442af8..66c533742 100644
--- a/desktop/flipper-plugin/src/__tests__/TestPlugin.tsx
+++ b/desktop/flipper-plugin/src/__tests__/TestPlugin.tsx
@@ -9,6 +9,7 @@
import * as React from 'react';
import {FlipperClient} from '../plugin/Plugin';
+import {usePlugin} from '../plugin/PluginContext';
type Events = {
inc: {
@@ -67,6 +68,11 @@ export function plugin(client: FlipperClient) {
}
export function Component() {
- // TODO T69105011 add test for usePlugin including type assertions
- return Hi from test plugin
;
+ const api = usePlugin(plugin);
+
+ // @ts-expect-error
+ api.bla;
+
+ // TODO N.b.: state updates won't be visible
+ return Hi from test plugin {api.state.count}
;
}
diff --git a/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx b/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx
index 705144680..911909da5 100644
--- a/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx
+++ b/desktop/flipper-plugin/src/__tests__/test-utils.node.tsx
@@ -53,7 +53,8 @@ test('it can render a plugin', () => {
- Hi from test plugin
+ Hi from test plugin
+ 0
diff --git a/desktop/flipper-plugin/src/index.ts b/desktop/flipper-plugin/src/index.ts
index d6d6ad9c9..4470f96b1 100644
--- a/desktop/flipper-plugin/src/index.ts
+++ b/desktop/flipper-plugin/src/index.ts
@@ -12,7 +12,7 @@ import * as TestUtilites from './test-utils/test-utils';
export {SandyPluginInstance, FlipperClient} from './plugin/Plugin';
export {SandyPluginDefinition} from './plugin/SandyPluginDefinition';
export {SandyPluginRenderer} from './plugin/PluginRenderer';
-export {SandyPluginContext} from './plugin/PluginContext';
+export {SandyPluginContext, usePlugin} from './plugin/PluginContext';
// It's not ideal that this exists in flipper-plugin sources directly,
// but is the least pain for plugin authors.
diff --git a/desktop/flipper-plugin/src/plugin/PluginContext.tsx b/desktop/flipper-plugin/src/plugin/PluginContext.tsx
index 49cdca3ad..d9bff9c5f 100644
--- a/desktop/flipper-plugin/src/plugin/PluginContext.tsx
+++ b/desktop/flipper-plugin/src/plugin/PluginContext.tsx
@@ -7,9 +7,28 @@
* @format
*/
-import {createContext} from 'react';
-import {SandyPluginInstance} from './Plugin';
+import {createContext, useContext} from 'react';
+import {SandyPluginInstance, FlipperPluginFactory} from './Plugin';
export const SandyPluginContext = createContext<
SandyPluginInstance | undefined
>(undefined);
+
+export function usePlugin>(
+ plugin: PluginFactory,
+): ReturnType {
+ const pluginInstance = useContext(SandyPluginContext);
+ if (!pluginInstance) {
+ throw new Error('Plugin context not available');
+ }
+ // In principle we don't *need* the plugin, but having it passed it makes sure the
+ // return of this function is strongly typed, without the user needing to create it's own
+ // context.
+ // But since we pass it, let's make sure the user did request the proper context
+ if (pluginInstance.definition.module.plugin !== plugin) {
+ throw new Error(
+ `Plugin context (${pluginInstance.definition.module.plugin}) didn't match the type of the requested plugin (${plugin})`,
+ );
+ }
+ return pluginInstance.instanceApi;
+}