diff --git a/apps/silverback-gatsby/src/templates/webform.tsx b/apps/silverback-gatsby/src/templates/webform.tsx index 40aeb093f..7c12c7d68 100644 --- a/apps/silverback-gatsby/src/templates/webform.tsx +++ b/apps/silverback-gatsby/src/templates/webform.tsx @@ -45,6 +45,22 @@ const Webform: React.FC< marginLeft: '-0.25em', marginRight: '-0.25em', }} + cssStylesToInject={ + location.search.includes('test-inject-css=true') + ? ` + /* + comment with special chars + #$@;\`'()* + */ + * { + color: green; + } + .form-item-optional-text-field { + margin-bottom: 200px; + } + ` + : undefined + } /> ); diff --git a/packages/composer/amazeelabs/silverback_iframe_theme/iframeCommand.js b/packages/composer/amazeelabs/silverback_iframe_theme/iframeCommand.js index c8d1ab048..6b3933273 100644 --- a/packages/composer/amazeelabs/silverback_iframe_theme/iframeCommand.js +++ b/packages/composer/amazeelabs/silverback_iframe_theme/iframeCommand.js @@ -82,23 +82,11 @@ }, }; - // Ask parent for the base URL to adjust links. waitForParentIframe(function (parentIFrame) { - parentIFrame.sendMessage({ action: 'getBaseUrl' }, '*'); + parentIFrame.sendMessage({ action: 'init' }, '*'); }); - // Update links using the given base URL. - window.addEventListener('message', (event) => { - // The message looks like this: - // [iFrameSizer]message:"silverback-iframe-base-url:http://localhost:8000" - var prefix = '[iFrameSizer]message:"silverback-iframe-base-url:'; - if (typeof event.data !== 'string' || event.data.indexOf(prefix) !== 0) { - return; - } - var baseUrl = event.data.substr( - prefix.length, - event.data.length - prefix.length - 1, - ); + var updateBaseUrlInLinks = (baseUrl) => { $('a:visible').each(function () { var $this = $(this); var href = $this.attr('href'); @@ -132,5 +120,58 @@ }); // This class is used by integration tests. $('body').addClass('silverback-iframe-links-processed'); + }; + + var injectCssStyles = (styles) => { + var id = 'silverback-iframe-injected-styles'; + var el = document.getElementById(id); + if (!el) { + el = document.createElement('style'); + el.id = id; + document.head.appendChild(el); + } + el.textContent = styles; + }; + + window.addEventListener('message', (event) => { + var parsed = parseMessage(event.data); + if (!parsed) { + return; + } + if (parsed.type === 'init') { + updateBaseUrlInLinks(parsed.baseUrl); + if (parsed.injectStyles) { + injectCssStyles(parsed.injectStyles); + } + } }); + + /** + * + * @param {string} message + * @returns {{type: 'init', baseUrl: string, injectStyles: string | undefined} | null} + */ + function parseMessage(message) { + if (typeof message !== 'string') { + return null; + } + var prefix = '[iFrameSizer]message:'; + if (!message.startsWith(prefix)) { + return null; + } + var parsed = null; + try { + parsed = JSON.parse(message.substring(prefix.length)); + } catch (e) { + return null; + } + if ( + typeof parsed !== 'object' || + typeof parsed.silverbackIframe !== 'object' || + parsed.silverbackIframe.type !== 'init' + ) { + return null; + } + return parsed.silverbackIframe; + } })(jQuery, Drupal, drupalSettings); diff --git a/packages/npm/@amazeelabs/silverback-iframe/src/components/SilverbackIframe.tsx b/packages/npm/@amazeelabs/silverback-iframe/src/components/SilverbackIframe.tsx index 888fc7641..8117a7c2f 100644 --- a/packages/npm/@amazeelabs/silverback-iframe/src/components/SilverbackIframe.tsx +++ b/packages/npm/@amazeelabs/silverback-iframe/src/components/SilverbackIframe.tsx @@ -11,6 +11,11 @@ type OwnProps = { buildMessages: (htmlMessages: Array) => JSX.Element | null; redirect: (url: string, htmlMessages?: Array) => void; scroll?: (to: string, iframeWrapper: HTMLElement) => void; + /** + * Not recommended for using in production. + * Because injecting CSS takes time and produces flashing. + */ + cssStylesToInject?: string; }; type Props = OwnProps & IframeResizer.IframeResizerProps; @@ -19,6 +24,7 @@ export const SilverbackIframe = ({ buildMessages, redirect, scroll, + cssStylesToInject, ...iframeResizerProps }: Props) => { const silverbackIframeReference = useRef(null); @@ -50,9 +56,15 @@ export const SilverbackIframe = ({ if (!isIframeCommand(message)) { return; } - if (message.action === 'getBaseUrl') { + if (message.action === 'init') { iframeRef.current?.sendMessage( - `silverback-iframe-base-url:${window.location.origin}`, + { + silverbackIframe: { + type: 'init', + baseUrl: window.location.origin, + injectStyles: cssStylesToInject, + }, + }, '*', ); return; diff --git a/packages/npm/@amazeelabs/silverback-iframe/src/types/__tests__/iframe-command.ts b/packages/npm/@amazeelabs/silverback-iframe/src/types/__tests__/iframe-command.ts index 6f27267dd..c2cb97ef3 100644 --- a/packages/npm/@amazeelabs/silverback-iframe/src/types/__tests__/iframe-command.ts +++ b/packages/npm/@amazeelabs/silverback-iframe/src/types/__tests__/iframe-command.ts @@ -22,8 +22,8 @@ const sets = [ result: false, }, { - case: 'getBaseUrl', - data: { action: 'getBaseUrl' }, + case: 'init', + data: { action: 'init' }, result: true, }, { diff --git a/packages/npm/@amazeelabs/silverback-iframe/src/types/iframe-command.ts b/packages/npm/@amazeelabs/silverback-iframe/src/types/iframe-command.ts index 7a17c8de9..adc652fb9 100644 --- a/packages/npm/@amazeelabs/silverback-iframe/src/types/iframe-command.ts +++ b/packages/npm/@amazeelabs/silverback-iframe/src/types/iframe-command.ts @@ -1,5 +1,5 @@ -export type IframeCommandGetBaseUrl = { - action: 'getBaseUrl'; +export type IframeCommandInit = { + action: 'init'; }; export type IframeCommandRedirect = { @@ -20,14 +20,17 @@ export type IframeCommandScroll = { }; export type IframeCommand = - | IframeCommandGetBaseUrl + | IframeCommandInit | IframeCommandRedirect | IframeCommandOther | IframeCommandScroll; export const isIframeCommand = (variable: any): variable is IframeCommand => { if (typeof variable === 'object' && typeof variable.action === 'string') { - if (['getBaseUrl', 'scroll'].includes(variable.action)) { + if (variable.action === 'init') { + return true; + } + if (variable.action === 'scroll' && typeof variable.scroll === 'string') { return true; } if ( diff --git a/packages/tests/silverback-gatsby/package.json b/packages/tests/silverback-gatsby/package.json index dcfa52117..c41be1e54 100644 --- a/packages/tests/silverback-gatsby/package.json +++ b/packages/tests/silverback-gatsby/package.json @@ -23,6 +23,8 @@ "test:integration": "playwright install chromium && pnpm test:readonly && pnpm test:mutating", "test:readonly": "playwright test --pass-with-no-tests", "test:mutating": "playwright test --pass-with-no-tests -c playwright.config.mutating.ts", + "headed:readonly": "pnpm test:readonly --headed", + "headed:mutating": "pnpm test:mutating --headed", "dev:readonly": "DEBUG=pw:api pnpm test:readonly --ui", "dev:mutating": "DEBUG=pw:api pnpm test:mutating --ui" } diff --git a/packages/tests/silverback-gatsby/specs/webform.spec.ts b/packages/tests/silverback-gatsby/specs/webform.spec.ts new file mode 100644 index 000000000..146d8103e --- /dev/null +++ b/packages/tests/silverback-gatsby/specs/webform.spec.ts @@ -0,0 +1,29 @@ +import { gatsby } from '@amazeelabs/silverback-playwright'; +import { expect, test } from '@playwright/test'; + +import { getIframe } from '../../silverback-drupal/common'; + +test('injected CSS styles', async ({ page }) => { + // Check if it isn't green by default. + await page.goto(`${gatsby.baseUrl}/en/form/for-testing-confirmation-options`); + await expect((await getIframe(page)).locator('h1')).not.toHaveCSS( + 'color', + 'rgb(0, 128, 0)', + ); + + // Inject CSS. + await page.goto( + `${gatsby.baseUrl}/en/form/for-testing-confirmation-options?test-inject-css=true`, + ); + // Check if it's green. + await expect((await getIframe(page)).locator('h1')).toHaveCSS( + 'color', + 'rgb(0, 128, 0)', + ); + // Check if we have additional margin at the bottom of the field. + await expect( + (await getIframe(page)).locator('.form-item-optional-text-field'), + ).toHaveCSS('margin-bottom', '200px'); + // Check if the iframe was resized. We should still see the Submit button. + await expect((await getIframe(page)).locator('text=Submit')).toBeVisible(); +});