diff --git a/e2e/custom-plugins/frontend-sandbox-panel-test/module.js b/e2e/custom-plugins/frontend-sandbox-panel-test/module.js
new file mode 100644
index 00000000000..dafd0aceb31
--- /dev/null
+++ b/e2e/custom-plugins/frontend-sandbox-panel-test/module.js
@@ -0,0 +1,41 @@
+define(['react', '@grafana/data'], function (React, grafanaData) {
+ const HelloWorld = () => {
+ const createIframe = () => {
+ // direct iframe creation
+ const iframe = document.createElement('iframe');
+ iframe.src = 'about:blank';
+ iframe.id = 'createElementIframe';
+ iframe.style.width = '10%';
+ iframe.style.height = '10%';
+ iframe.style.border = 'none';
+ document.body.appendChild(iframe);
+
+ // via innerHTML
+ const div = document.createElement('div');
+ document.body.appendChild(div);
+ div.innerHTML =
+ '';
+
+ const adjacentIframe = ``;
+ document.querySelector('body').insertAdjacentHTML('beforeend', adjacentIframe);
+ };
+ const handleClick2 = () => {
+ console.log('hello world 2');
+ };
+ const handleClick3 = () => {
+ console.log('hello world 3');
+ };
+
+ return React.createElement(
+ 'div',
+ { className: 'frontend-sandbox-test' },
+ React.createElement('button', { onClick: createIframe, 'data-testid': 'panel-button-1' }, 'Craete iframes'),
+ React.createElement('button', { onClick: handleClick2, 'data-testid': 'panel-button-2' }, 'Button 2'),
+ React.createElement('button', { onClick: handleClick3, 'data-testid': 'panel-button-3' }, 'Button 3')
+ );
+ };
+
+ const plugin = new grafanaData.PanelPlugin(HelloWorld);
+
+ return { plugin };
+});
diff --git a/e2e/custom-plugins/frontend-sandbox-panel-test/plugin.json b/e2e/custom-plugins/frontend-sandbox-panel-test/plugin.json
new file mode 100644
index 00000000000..a77283a0a6b
--- /dev/null
+++ b/e2e/custom-plugins/frontend-sandbox-panel-test/plugin.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "https://raw.githubusercontent.com/grafana/grafana/master/docs/sources/developers/plugins/plugin.schema.json",
+ "type": "panel",
+ "name": "Sandbox test plugin",
+ "id": "sandbox-test-panel",
+ "info": {
+ "keywords": ["panel"],
+ "description": "",
+ "author": {
+ "name": "Grafana"
+ },
+ "logos": {
+ "small": "img/logo.svg",
+ "large": "img/logo.svg"
+ },
+ "links": [],
+ "screenshots": [],
+ "version": "1.0.0",
+ "updated": "2023-06-27"
+ },
+ "dependencies": {
+ "grafanaDependency": ">=10.0",
+ "plugins": []
+ }
+}
diff --git a/e2e/plugins-sandbox-suite/panelSandbox.spec.ts b/e2e/plugins-sandbox-suite/panelSandbox.spec.ts
new file mode 100644
index 00000000000..ab75b59711f
--- /dev/null
+++ b/e2e/plugins-sandbox-suite/panelSandbox.spec.ts
@@ -0,0 +1,27 @@
+import { e2e } from '@grafana/e2e';
+
+import panelSandboxDashboard from './panelSandboxDashboard.json';
+
+const DASHBOARD_ID = 'c46b2460-16b7-42a5-82d1-b07fbf431950';
+
+describe('Panel sandbox', () => {
+ beforeEach(() => e2e.flows.login(e2e.env('USERNAME'), e2e.env('PASSWORD'), true));
+
+ describe('Sandbox disabled', () => {
+ const queryParams = { '__feature.pluginsFrontendSandbox': false };
+
+ it('Add iframe to body', () => {
+ e2e.flows.importDashboard(panelSandboxDashboard, 1000, true);
+ e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams });
+
+ cy.get('[data-testid="panel-button-1"]').click();
+
+ cy.get('#createElementIframe').should('exist');
+ cy.get('#innerHTMLIframe').should('exist');
+ cy.get('#adjacentIframe').should('exist');
+ });
+ });
+
+ afterEach(() => e2e.flows.revertAllChanges());
+ after(() => e2e().clearCookies());
+});
diff --git a/e2e/plugins-sandbox-suite/panelSandboxDashboard.json b/e2e/plugins-sandbox-suite/panelSandboxDashboard.json
new file mode 100644
index 00000000000..0ea1ca81d24
--- /dev/null
+++ b/e2e/plugins-sandbox-suite/panelSandboxDashboard.json
@@ -0,0 +1,58 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "grafana",
+ "uid": "-- Grafana --"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "id": 118,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "datasource": {
+ "type": "testdata",
+ "uid": "PD8C576611E62080A"
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 0
+ },
+ "id": 1,
+ "title": "Sandbox Panel test",
+ "type": "sandbox-test-panel"
+ }
+ ],
+ "refresh": "",
+ "schemaVersion": 38,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-6h",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "",
+ "title": "Sandbox Panel Test",
+ "uid": "c46b2460-16b7-42a5-82d1-b07fbf431950",
+ "version": 1,
+ "weekStart": ""
+}
diff --git a/e2e/start-and-run-suite b/e2e/start-and-run-suite
index d29b7775b96..3351a19fdc4 100755
--- a/e2e/start-and-run-suite
+++ b/e2e/start-and-run-suite
@@ -16,7 +16,7 @@ if [ "$BASE_URL" != "" ]; then
echo -e "BASE_URL set, skipping starting server"
else
# Start it in the background
- ./scripts/grafana-server/start-server $LICENSE_PATH 2>&1 > scripts/grafana-server/server.log &
+ GF_PATHS_PLUGINS=../../../e2e/custom-plugins/ ./scripts/grafana-server/start-server $LICENSE_PATH 2>&1 > scripts/grafana-server/server.log &
./scripts/grafana-server/wait-for-grafana
fi
diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts
index 2ef92f24027..94da6d52272 100644
--- a/packages/grafana-runtime/src/config.ts
+++ b/packages/grafana-runtime/src/config.ts
@@ -194,7 +194,9 @@ export class GrafanaBootConfig implements GrafanaConfig {
systemDateFormats.update(this.dateFormats);
}
- overrideFeatureTogglesFromUrl(this);
+ if (this.buildInfo.env === 'development') {
+ overrideFeatureTogglesFromUrl(this);
+ }
if (this.featureToggles.disableAngular) {
this.angularSupportEnabled = false;