diff --git a/Composer/packages/client/__tests__/pages/publish/Publish.test.tsx b/Composer/packages/client/__tests__/pages/publish/Publish.test.tsx index 5d498305a3..e42d04f5b8 100644 --- a/Composer/packages/client/__tests__/pages/publish/Publish.test.tsx +++ b/Composer/packages/client/__tests__/pages/publish/Publish.test.tsx @@ -35,6 +35,7 @@ const state = { name: 'azurePublish', description: 'azure publish', instructions: 'plugin instruction', + extensionId: 'azurePublish', schema: { default: { test: 'test', diff --git a/Composer/packages/client/package.json b/Composer/packages/client/package.json index 00f12fa533..ff3fa93a80 100644 --- a/Composer/packages/client/package.json +++ b/Composer/packages/client/package.json @@ -9,7 +9,7 @@ }, "scripts": { "start": "yarn build:extension-bundles && node scripts/start.js", - "build": "node --max_old_space_size=4096 scripts/build.js", + "build": "yarn build:extension-bundles && node --max_old_space_size=4096 scripts/build.js", "build:extension-bundles": "webpack --config ./config/extensions.config.js --env production", "clean": "rimraf build", "test": "jest", diff --git a/Composer/packages/client/src/components/PluginHost/PluginHost.tsx b/Composer/packages/client/src/components/PluginHost/PluginHost.tsx index 0df82eabd5..72bc3bff14 100644 --- a/Composer/packages/client/src/components/PluginHost/PluginHost.tsx +++ b/Composer/packages/client/src/components/PluginHost/PluginHost.tsx @@ -2,14 +2,18 @@ // Licensed under the MIT License. /** @jsx jsx */ -import { jsx, SerializedStyles } from '@emotion/core'; -import React, { useState, useEffect, useRef } from 'react'; +import { jsx, css, SerializedStyles } from '@emotion/core'; +import React, { useEffect, useRef } from 'react'; import { Shell } from '@botframework-composer/types'; import { PluginType } from '@bfc/extension-client'; import { PluginAPI } from '../../plugins/api'; -import { iframeStyle } from './styles'; +export const iframeStyle = css` + height: 100%; + width: 100%; + border: 0; +`; interface PluginHostProps { extraIframeStyles?: SerializedStyles[]; @@ -19,6 +23,11 @@ interface PluginHostProps { shell?: Shell; } +function resetIframe(iframeDoc: Document) { + iframeDoc.head.innerHTML = ''; + iframeDoc.body.innerHTML = ''; +} + /** Binds closures around Composer client code to plugin iframe's window object */ function attachPluginAPI(win: Window, type: PluginType, shell?: object) { const api = { ...PluginAPI[type], ...PluginAPI.auth }; @@ -45,20 +54,35 @@ function injectScript(doc: Document, id: string, src: string, async: boolean, on */ export const PluginHost: React.FC = (props) => { const targetRef = useRef(null); - const [isLoaded, setIsLoaded] = useState(false); const { extraIframeStyles = [], pluginType, pluginName, bundleId, shell } = props; + const loadBundle = (name: string, bundle: string, type: PluginType) => { + const iframeWindow = targetRef.current?.contentWindow as Window; + const iframeDocument = targetRef.current?.contentDocument as Document; + + attachPluginAPI(iframeWindow, type, shell); + + //load the bundle for the specified plugin + const pluginScriptId = `plugin-${type}-${name}`; + const bundleUri = `/api/extensions/${name}/${bundle}`; + // If plugin bundles end up being too large and block the client thread due to the load, enable the async flag on this call + injectScript(iframeDocument, pluginScriptId, bundleUri, false); + }; + useEffect(() => { // renders the plugin's UI inside of the iframe - if (pluginName && pluginType) { - const iframeDocument = targetRef.current?.contentDocument as Document; + if (pluginName && pluginType && targetRef.current) { + const iframeDocument = targetRef.current.contentDocument as Document; + + // cleanup + resetIframe(iframeDocument); - // // load the preload script to setup the plugin API + // load the preload script to setup the plugin API injectScript(iframeDocument, 'preload-bundle', '/plugin-host-preload.js', false); const onPreloaded = (ev) => { if (ev.data === 'host-preload-complete') { - setIsLoaded(true); + loadBundle(pluginName, bundleId, pluginType); } }; @@ -68,29 +92,15 @@ export const PluginHost: React.FC = (props) => { window.removeEventListener('message', onPreloaded); }; } - }, [pluginName, pluginType, bundleId, targetRef]); - - useEffect(() => { - if (isLoaded && pluginType && pluginName && bundleId) { - const iframeWindow = targetRef.current?.contentWindow as Window; - const iframeDocument = targetRef.current?.contentDocument as Document; - - attachPluginAPI(iframeWindow, pluginType, shell); - - //load the bundle for the specified plugin - const pluginScriptId = `plugin-${pluginType}-${pluginName}`; - const bundleUri = `/api/extensions/${pluginName}/${bundleId}`; - // If plugin bundles end up being too large and block the client thread due to the load, enable the async flag on this call - injectScript(iframeDocument, pluginScriptId, bundleUri, false); - } - }, [isLoaded]); + }, [pluginName, pluginType, bundleId]); // sync the shell to the iframe store when shell changes useEffect(() => { - if (isLoaded && targetRef.current) { - targetRef.current.contentWindow?.Composer.sync(shell); + const frameApi = targetRef.current?.contentWindow?.Composer; + if (frameApi && typeof frameApi.sync === 'function') { + frameApi.sync(shell); } - }, [isLoaded, shell]); + }, [shell]); - return ; + return