From 770880f694413b7e489987dd1a776891e68f55c8 Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Tue, 10 Jun 2025 11:26:09 -0700 Subject: [PATCH 1/3] feat: Allow passing HTML elements to display in toasts. --- src/html_toast.ts | 49 +++++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 3 +++ 2 files changed, 52 insertions(+) create mode 100644 src/html_toast.ts diff --git a/src/html_toast.ts b/src/html_toast.ts new file mode 100644 index 00000000..63850932 --- /dev/null +++ b/src/html_toast.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from 'blockly/core'; + +/** + * Configuration options for toasts. + */ +interface HtmlToastOptions extends Blockly.ToastOptions { + element?: HTMLElement; +} + +/** + * Custom toast implementation that supports HTML elements in toast messages. + */ +class HtmlToast extends Blockly.Toast { + /** + * Creates the body of the toast for display. + * + * @param workspace The workspace the toast will be displayed on. + * @param options Configuration options for toast appearance/behavior. + * @returns The body for the toast. + */ + protected static override createDom( + workspace: Blockly.WorkspaceSvg, + options: Blockly.ToastOptions, + ) { + const dom = super.createDom(workspace, options); + const contents = dom.querySelector('div'); + if ( + contents && + 'element' in options && + options.element instanceof HTMLElement + ) { + contents.innerHTML = ''; + contents.appendChild(options.element); + } + return dom; + } +} + +/** + * Registers HtmlToast as the default toast implementation for Blockly. */ +export function registerHtmlToast() { + Blockly.dialog.setToast(HtmlToast.show.bind(HtmlToast)); +} diff --git a/src/index.ts b/src/index.ts index efe96d58..32f55d73 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import * as Blockly from 'blockly/core'; import {NavigationController} from './navigation_controller'; import {enableBlocksOnDrag} from './disabled_blocks'; +import {registerHtmlToast} from './html_toast'; /** Plugin for keyboard navigation. */ export class KeyboardNavigation { @@ -82,6 +83,8 @@ export class KeyboardNavigation { }); workspace.getSvgGroup().appendChild(this.workspaceFocusRing); this.resizeWorkspaceRings(); + + registerHtmlToast(); } private resizeWorkspaceRings() { From 7a6a5cb7db8d665d4139aa8a99dd2d120240460c Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Wed, 11 Jun 2025 13:33:35 -0700 Subject: [PATCH 2/3] chore: Satisfy the linter and clarify comments. --- src/html_toast.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/html_toast.ts b/src/html_toast.ts index 63850932..7e6c73c4 100644 --- a/src/html_toast.ts +++ b/src/html_toast.ts @@ -15,6 +15,10 @@ interface HtmlToastOptions extends Blockly.ToastOptions { /** * Custom toast implementation that supports HTML elements in toast messages. + * + * After registering, call + `Blockly.dialog.toast(workspace, {element: , message: });` + * to display an HTML-based toast. */ class HtmlToast extends Blockly.Toast { /** @@ -26,7 +30,7 @@ class HtmlToast extends Blockly.Toast { */ protected static override createDom( workspace: Blockly.WorkspaceSvg, - options: Blockly.ToastOptions, + options: HtmlToastOptions, ) { const dom = super.createDom(workspace, options); const contents = dom.querySelector('div'); @@ -43,7 +47,8 @@ class HtmlToast extends Blockly.Toast { } /** - * Registers HtmlToast as the default toast implementation for Blockly. */ + * Registers HtmlToast as the default toast implementation for Blockly. + */ export function registerHtmlToast() { Blockly.dialog.setToast(HtmlToast.show.bind(HtmlToast)); } From 80cdcfca98991dd590ca55ff7883da1075c6559e Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Wed, 11 Jun 2025 13:43:20 -0700 Subject: [PATCH 3/3] chore: Add a test for HTML toasts. --- test/webdriverio/test/toast_test.ts | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 test/webdriverio/test/toast_test.ts diff --git a/test/webdriverio/test/toast_test.ts b/test/webdriverio/test/toast_test.ts new file mode 100644 index 00000000..cd4721d5 --- /dev/null +++ b/test/webdriverio/test/toast_test.ts @@ -0,0 +1,38 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as chai from 'chai'; +import * as Blockly from 'blockly/core'; +import {PAUSE_TIME, testFileLocations, testSetup} from './test_setup.js'; + +suite('HTML toasts', function () { + setup(async function () { + this.browser = await testSetup(testFileLocations.BASE); + await this.browser.pause(PAUSE_TIME); + }); + + test('Can be displayed', async function () { + const equal = await this.browser.execute(() => { + const element = document.createElement('div'); + element.id = 'testToast'; + element.innerHTML = 'This is a test'; + + const options = { + element, + message: 'Placeholder', + }; + Blockly.dialog.toast( + Blockly.getMainWorkspace() as Blockly.WorkspaceSvg, + options, + ); + + // Ensure that the element displayed in the toast is the one we specified. + return document.querySelector('.blocklyToast #testToast') === element; + }); + + chai.assert.isTrue(equal); + }); +});