From 52341f7a042ccaecc7499f329626b1e8c34a2662 Mon Sep 17 00:00:00 2001 From: David Bourgault Date: Mon, 5 Oct 2026 13:35:07 -0400 Subject: [PATCH 01/11] Migrate build from @grafana/toolkit to @grafana/create-plugin @grafana/toolkit was deprecated in Grafana 9 and removed in Grafana 10, and it no longer installs on current Node (its eslint-plugin-jsdoc pin refuses anything past Node 17). Replace it with the scaffolding that @grafana/create-plugin 7.11 generates, via `create-plugin migrate`. Runtime dependencies are deliberately untouched here: Grafana 9, React 17 and OpenLayers 6 all stay put so this commit isolates the build-system change. - Build: webpack 5 + SWC under .config/, replacing the toolkit's bundler. - Lint: ESLint 9 flat config (@grafana/eslint-config). Autofixed `no-var`, and widened two useMemo dep lists that the new React Compiler rule rejected for depending on optional-chained subproperties. - Test: jest 29 + @swc/jest. Inline snapshots re-recorded for jest 29's snapshot format (`Object {` -> `{`); values are unchanged. jest 29 cannot write inline snapshots through prettier 3, so prettierPath is disabled. - Types: constrain ObservablePropsWrapper to `T extends {}`, which TS 5.9 requires to spread T into JSX. Matches the fix upstream in Grafana. - Pin a single `ol` copy via npm overrides. @grafana/data ships its own exact-pinned ol and its exported layer types reference it, so a second copy makes every BaseLayer crossing the Grafana boundary unassignable. - Package manager: yarn 1 -> npm, matching current scaffolding. - CI: node 14 -> 22, refreshed actions, dropped the deprecated ::set-output calls, and replaced the hand-rolled release job with grafana/plugin-actions/build-plugin. - Dev env: compose now builds Grafana from .config/Dockerfile and mounts dist/, keeping the CrateDB and TimescaleDB services. Data sources and dashboards are provisioned from provisioning/ instead of the curl script, so set-up-grafana.sh, data_sources/ and the stock grafana.ini are gone. The dashboards were stored as /api/dashboards/db payloads ({meta, dashboard}); file provisioning wants the bare dashboard object, so they are unwrapped. The Postgres data sources move `database` into jsonData, where current Grafana reads it from. Co-Authored-By: Claude Opus 5 (1M context) --- .config/.cprc.json | 3 + .config/.prettierrc.js | 16 + .config/AGENTS/e2e-testing.md | 173 + .config/AGENTS/instructions.md | 34 + .config/AGENTS/skills/build-plugin.md | 49 + .config/AGENTS/skills/validate-plugin.md | 57 + .config/Dockerfile | 54 + .config/README.md | 175 + .config/bundler/constants.ts | 2 + .config/bundler/copyFiles.ts | 23 + .config/bundler/externals.ts | 45 + .config/bundler/utils.ts | 68 + .config/docker-compose-base.yaml | 25 + .config/entrypoint.sh | 18 + .config/eslint.config.mjs | 38 + .config/jest-setup.js | 28 + .config/jest.config.js | 44 + .config/jest/mocks/react-inlinesvg.tsx | 25 + .config/jest/utils.js | 39 + .config/playwright.config.ts | 54 + .config/rspack/BuildModeRspackPlugin.ts | 36 + .config/rspack/liveReloadPlugin.ts | 133 + .config/rspack/rspack.config.ts | 224 + .config/supervisord/supervisord.conf | 15 + .config/tsconfig.json | 29 + .config/types/bundler-rules.d.ts | 37 + .config/types/setupTests.d.ts | 1 + .config/types/webpack-plugins.d.ts | 83 + .config/webpack/BuildModeWebpackPlugin.ts | 33 + .config/webpack/webpack.config.ts | 237 + .github/workflows/bundle-stats.yml | 27 + .github/workflows/ci.yml | 105 +- .github/workflows/codeql-analysis.yml | 8 +- .github/workflows/cp-update.yml | 20 + .github/workflows/is-compatible.yml | 37 + .github/workflows/release.yml | 171 +- .gitignore | 30 +- .nvmrc | 1 + .prettierrc.js | 3 +- CONTRIBUTING.md | 93 +- dashboards/map.json | 3447 ++-- dashboards/timescaledb-heatmap.json | 867 +- dashboards/timescaledb-map.json | 917 +- data_sources/createdb.json | 31 - data_sources/postgres.json | 31 - docker-compose.yaml | 38 + docker-compose.yml | 36 - eslint.config.mjs | 39 + etc/grafana/grafana.ini | 1006 -- jest-setup.js | 2 + jest.config.js | 57 +- package-lock.json | 17674 ++++++++++++++++++++ package.json | 97 +- provisioning/dashboards/dashboards.yaml | 13 + provisioning/datasources/datasources.yaml | 37 + provisioning/plugins/.gitkeep | 0 set-up-grafana.sh | 13 - src/GeomapPanel.tsx | 12 +- src/components/ObservablePropsWrapper.tsx | 2 +- src/editor/LayerEditor.tsx | 4 +- src/editor/MapViewEditor.tsx | 8 +- src/extension.ts | 2 +- src/gazetteer/gazetteer.test.ts | 20 +- src/layers/basemaps/nextzen.ts | 16 +- src/layers/data/idwMap.tsx | 2 +- src/migrations.test.ts | 84 +- src/plugin.json | 3 +- tsconfig.json | 10 +- yarn.lock | 9908 ----------- 69 files changed, 22580 insertions(+), 14089 deletions(-) create mode 100644 .config/.cprc.json create mode 100644 .config/.prettierrc.js create mode 100644 .config/AGENTS/e2e-testing.md create mode 100644 .config/AGENTS/instructions.md create mode 100644 .config/AGENTS/skills/build-plugin.md create mode 100644 .config/AGENTS/skills/validate-plugin.md create mode 100644 .config/Dockerfile create mode 100644 .config/README.md create mode 100644 .config/bundler/constants.ts create mode 100644 .config/bundler/copyFiles.ts create mode 100644 .config/bundler/externals.ts create mode 100644 .config/bundler/utils.ts create mode 100644 .config/docker-compose-base.yaml create mode 100644 .config/entrypoint.sh create mode 100644 .config/eslint.config.mjs create mode 100644 .config/jest-setup.js create mode 100644 .config/jest.config.js create mode 100644 .config/jest/mocks/react-inlinesvg.tsx create mode 100644 .config/jest/utils.js create mode 100644 .config/playwright.config.ts create mode 100644 .config/rspack/BuildModeRspackPlugin.ts create mode 100644 .config/rspack/liveReloadPlugin.ts create mode 100644 .config/rspack/rspack.config.ts create mode 100644 .config/supervisord/supervisord.conf create mode 100644 .config/tsconfig.json create mode 100644 .config/types/bundler-rules.d.ts create mode 100644 .config/types/setupTests.d.ts create mode 100644 .config/types/webpack-plugins.d.ts create mode 100644 .config/webpack/BuildModeWebpackPlugin.ts create mode 100644 .config/webpack/webpack.config.ts create mode 100644 .github/workflows/bundle-stats.yml create mode 100644 .github/workflows/cp-update.yml create mode 100644 .github/workflows/is-compatible.yml create mode 100644 .nvmrc delete mode 100644 data_sources/createdb.json delete mode 100644 data_sources/postgres.json create mode 100644 docker-compose.yaml delete mode 100644 docker-compose.yml create mode 100644 eslint.config.mjs delete mode 100644 etc/grafana/grafana.ini create mode 100644 jest-setup.js create mode 100644 package-lock.json create mode 100644 provisioning/dashboards/dashboards.yaml create mode 100644 provisioning/datasources/datasources.yaml create mode 100644 provisioning/plugins/.gitkeep delete mode 100644 set-up-grafana.sh delete mode 100644 yarn.lock diff --git a/.config/.cprc.json b/.config/.cprc.json new file mode 100644 index 0000000..f9eca27 --- /dev/null +++ b/.config/.cprc.json @@ -0,0 +1,3 @@ +{ + "version": "7.11.0" +} diff --git a/.config/.prettierrc.js b/.config/.prettierrc.js new file mode 100644 index 0000000..bf506f5 --- /dev/null +++ b/.config/.prettierrc.js @@ -0,0 +1,16 @@ +/* + * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ + * + * In order to extend the configuration follow the steps in .config/README.md + */ + +module.exports = { + endOfLine: 'auto', + printWidth: 120, + trailingComma: 'es5', + semi: true, + jsxSingleQuote: false, + singleQuote: true, + useTabs: false, + tabWidth: 2, +}; diff --git a/.config/AGENTS/e2e-testing.md b/.config/AGENTS/e2e-testing.md new file mode 100644 index 0000000..b22fe72 --- /dev/null +++ b/.config/AGENTS/e2e-testing.md @@ -0,0 +1,173 @@ +--- +name: e2e testing instructions for a grafana plugin +description: Guides how to write e2e tests using @grafana/plugin-e2e +--- + +# E2E testing a Grafana plugin + +This plugin uses `@grafana/plugin-e2e` and Playwright for end-to-end testing. + +- Import `test` and `expect` from `@grafana/plugin-e2e`, or from your project's fixtures entrypoint (for example `./fixtures`) if it re-exports them. Do not import them from `@playwright/test` directly. +- Always use `@grafana/plugin-e2e` fixtures and page models instead of raw Playwright navigation. They handle Grafana version differences automatically. +- Place test files in `tests/` as `*.spec.ts`. +- Each test must be independent and assume fresh state. +- If tests fail against newer Grafana versions, update `@grafana/plugin-e2e` first. It evolves alongside Grafana core to handle selector and API changes. + +## Selecting elements + +### Grafana selectors + +- Use Grafana e2e-selectors whenever possible. Always get them from the `selectors` fixture provided by `@grafana/plugin-e2e` - never import from `@grafana/e2e-selectors` directly. The fixture resolves the correct selectors for the Grafana version under test. +- Always use the `getByGrafanaSelector` method (exposed by all plugin-e2e page models) to resolve selectors to Playwright locators. It handles the `aria-label` vs `data-testid` difference across Grafana versions automatically. + ```typescript + panelEditPage.getByGrafanaSelector(selectors.components.CodeEditor.container).click(); + ``` + +### Scoping locators + +Scope locators to the narrowest context possible. +```typescript +// bad - matches any "URL" text on the page +page.getByText('URL').click(); +// good - scoped to the plugin's wrapper +page.getByTestId('plugin-url-wrapper').getByText('URL').click(); +``` + +### Form elements + +The `InlineField` and `Field` components can be used interchangeably in the examples below. + +**Input** - use `getByRole('textbox', { name: '