@grafana/create-plugin 6.2.0-canary.2283.19289604632.0 → 6.2.0-canary.2314.19505018775.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/dist/migrations/migrations.js +10 -0
- package/dist/migrations/scripts/006-webpack-nested-fix.js +80 -0
- package/dist/migrations/scripts/007-remove-testing-library-types.js +25 -0
- package/package.json +3 -2
- package/src/migrations/migrations.ts +11 -0
- package/src/migrations/scripts/006-webpack-nested-fix.test.ts +169 -0
- package/src/migrations/scripts/006-webpack-nested-fix.ts +117 -0
- package/src/migrations/scripts/007-remove-testing-library-types.test.ts +137 -0
- package/src/migrations/scripts/007-remove-testing-library-types.ts +25 -0
- package/templates/backend/go.mod +67 -47
- package/templates/backend/go.sum +197 -222
- package/templates/backend-app/go.mod +67 -48
- package/templates/backend-app/go.sum +197 -222
- package/templates/common/.config/types/setupTests.d.ts +1 -0
- package/templates/common/.config/webpack/webpack.config.ts +1 -1
- package/templates/common/_package.json +2 -3
- package/templates/github/workflows/bundle-stats.yml +1 -1
- package/templates/github/workflows/ci.yml +11 -11
- package/templates/github/workflows/cp-update.yml +1 -1
- package/templates/github/workflows/is-compatible.yml +3 -3
- package/templates/github/workflows/release.yml +1 -1
- package/templates/panel/.config/AGENTS/fundamentals.md +81 -0
- package/templates/panel/.config/AGENTS/howto/add-panel-options.md +130 -0
- package/templates/panel/AGENTS.md +3 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -187,7 +187,7 @@ const config = async (env: Env): Promise<Configuration> => {
|
|
|
187
187
|
new ReplaceInFileWebpackPlugin([
|
|
188
188
|
{
|
|
189
189
|
dir: DIST_DIR,
|
|
190
|
-
|
|
190
|
+
test: [/(^|\/)plugin\.json$/, /(^|\/)README\.md$/],
|
|
191
191
|
rules: [
|
|
192
192
|
{
|
|
193
193
|
search: /\%VERSION\%/g,
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"author": "{{ sentenceCase orgName }}",
|
|
17
17
|
"license": "Apache-2.0",
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@grafana/eslint-config": "^
|
|
19
|
+
"@grafana/eslint-config": "^9.0.0",
|
|
20
20
|
"@grafana/plugin-e2e": "^3.0.1",
|
|
21
21
|
"@grafana/tsconfig": "^2.0.1",
|
|
22
22
|
"@playwright/test": "^1.52.0",{{#if useExperimentalRspack}}
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
"@types/react": "^18.3.0",
|
|
34
34
|
"@types/react-dom": "^18.3.0",{{#if isAppType}}{{#unless useReactRouterV6}}
|
|
35
35
|
"@types/react-router-dom": "^{{ reactRouterVersion }}",{{/unless}}{{/if}}
|
|
36
|
-
"@types/testing-library__jest-dom": "5.14.8",
|
|
37
36
|
"@typescript-eslint/eslint-plugin": "^8.3.0",
|
|
38
37
|
"@typescript-eslint/parser": "^8.3.0",{{#unless useExperimentalRspack}}
|
|
39
38
|
"copy-webpack-plugin": "^11.0.0",{{/unless}}
|
|
@@ -42,7 +41,7 @@
|
|
|
42
41
|
"eslint-config-prettier": "^8.8.0",
|
|
43
42
|
"eslint-plugin-jsdoc": "^51.2.3",
|
|
44
43
|
"eslint-plugin-react": "^7.37.5",
|
|
45
|
-
"eslint-plugin-react-hooks": "^
|
|
44
|
+
"eslint-plugin-react-hooks": "^7.0.0",
|
|
46
45
|
"eslint-webpack-plugin": "^5.0.0",{{#unless useExperimentalRspack}}
|
|
47
46
|
"fork-ts-checker-webpack-plugin": "^8.0.0",{{/unless}}
|
|
48
47
|
"glob": "^10.2.7",
|
|
@@ -25,16 +25,16 @@ jobs:
|
|
|
25
25
|
env:
|
|
26
26
|
GRAFANA_ACCESS_POLICY_TOKEN: $\{{ secrets.GRAFANA_ACCESS_POLICY_TOKEN }}
|
|
27
27
|
steps:
|
|
28
|
-
- uses: actions/checkout@
|
|
28
|
+
- uses: actions/checkout@v5
|
|
29
29
|
with:
|
|
30
30
|
persist-credentials: false
|
|
31
31
|
{{#if_eq packageManagerName "pnpm"}}
|
|
32
32
|
# pnpm action uses the packageManager field in package.json to
|
|
33
33
|
# understand which version to install.
|
|
34
|
-
- uses: pnpm/action-setup@
|
|
34
|
+
- uses: pnpm/action-setup@v4
|
|
35
35
|
{{/if_eq}}
|
|
36
36
|
- name: Setup Node.js environment
|
|
37
|
-
uses: actions/setup-node@
|
|
37
|
+
uses: actions/setup-node@v6
|
|
38
38
|
with:
|
|
39
39
|
node-version: '22'
|
|
40
40
|
cache: '{{ packageManagerName }}'
|
|
@@ -61,7 +61,7 @@ jobs:
|
|
|
61
61
|
|
|
62
62
|
- name: Setup Go environment
|
|
63
63
|
if: steps.check-for-backend.outputs.has-backend == 'true'
|
|
64
|
-
uses: actions/setup-go@
|
|
64
|
+
uses: actions/setup-go@v6
|
|
65
65
|
with:
|
|
66
66
|
go-version: '1.22'
|
|
67
67
|
|
|
@@ -122,7 +122,7 @@ jobs:
|
|
|
122
122
|
ARCHIVE: $\{{ steps.metadata.outputs.archive }}
|
|
123
123
|
|
|
124
124
|
- name: Archive Build
|
|
125
|
-
uses: actions/upload-artifact@
|
|
125
|
+
uses: actions/upload-artifact@v5
|
|
126
126
|
with:
|
|
127
127
|
name: $\{{ steps.metadata.outputs.plugin-id }}-$\{{ steps.metadata.outputs.plugin-version }}
|
|
128
128
|
path: $\{{ steps.metadata.outputs.plugin-id }}
|
|
@@ -140,7 +140,7 @@ jobs:
|
|
|
140
140
|
matrix: $\{{ steps.resolve-versions.outputs.matrix }}
|
|
141
141
|
steps:
|
|
142
142
|
- name: Checkout
|
|
143
|
-
uses: actions/checkout@
|
|
143
|
+
uses: actions/checkout@v5
|
|
144
144
|
with:
|
|
145
145
|
persist-credentials: false
|
|
146
146
|
|
|
@@ -162,12 +162,12 @@ jobs:
|
|
|
162
162
|
name: e2e test $\{{ matrix.GRAFANA_IMAGE.name }}@$\{{ matrix.GRAFANA_IMAGE.VERSION }}
|
|
163
163
|
runs-on: ubuntu-latest
|
|
164
164
|
steps:
|
|
165
|
-
- uses: actions/checkout@
|
|
165
|
+
- uses: actions/checkout@v5
|
|
166
166
|
with:
|
|
167
167
|
persist-credentials: false
|
|
168
168
|
|
|
169
169
|
- name: Download plugin
|
|
170
|
-
uses: actions/download-artifact@
|
|
170
|
+
uses: actions/download-artifact@v6
|
|
171
171
|
with:
|
|
172
172
|
path: dist
|
|
173
173
|
name: $\{{ needs.build.outputs.plugin-id }}-$\{{ needs.build.outputs.plugin-version }}
|
|
@@ -180,10 +180,10 @@ jobs:
|
|
|
180
180
|
{{#if_eq packageManagerName "pnpm"}}
|
|
181
181
|
# pnpm action uses the packageManager field in package.json to
|
|
182
182
|
# understand which version to install.
|
|
183
|
-
- uses: pnpm/action-setup@
|
|
183
|
+
- uses: pnpm/action-setup@v4
|
|
184
184
|
{{/if_eq}}
|
|
185
185
|
- name: Setup Node.js environment
|
|
186
|
-
uses: actions/setup-node@
|
|
186
|
+
uses: actions/setup-node@v6
|
|
187
187
|
with:
|
|
188
188
|
node-version: '22'
|
|
189
189
|
cache: '{{ packageManagerName }}'
|
|
@@ -242,7 +242,7 @@ jobs:
|
|
|
242
242
|
needs: [playwright-tests]
|
|
243
243
|
runs-on: ubuntu-latest
|
|
244
244
|
steps:
|
|
245
|
-
- uses: actions/checkout@
|
|
245
|
+
- uses: actions/checkout@v5
|
|
246
246
|
with:
|
|
247
247
|
# required for playwright-gh-pages
|
|
248
248
|
persist-credentials: true
|
|
@@ -15,7 +15,7 @@ jobs:
|
|
|
15
15
|
release:
|
|
16
16
|
runs-on: ubuntu-latest
|
|
17
17
|
steps:
|
|
18
|
-
- uses: grafana/plugin-actions/create-plugin-update@create-plugin-update/
|
|
18
|
+
- uses: grafana/plugin-actions/create-plugin-update@create-plugin-update/v2.0.1
|
|
19
19
|
with:
|
|
20
20
|
# Make sure to save the token in your repository secrets as `GH_PAT_TOKEN`
|
|
21
21
|
token: $\{{ secrets.GH_PAT_TOKEN }}
|
|
@@ -7,18 +7,18 @@ jobs:
|
|
|
7
7
|
permissions:
|
|
8
8
|
contents: read
|
|
9
9
|
steps:
|
|
10
|
-
- uses: actions/checkout@
|
|
10
|
+
- uses: actions/checkout@v5
|
|
11
11
|
with:
|
|
12
12
|
persist-credentials: false
|
|
13
13
|
|
|
14
14
|
{{#if_eq packageManagerName "pnpm"}}
|
|
15
15
|
# pnpm action uses the packageManager field in package.json to
|
|
16
16
|
# understand which version to install.
|
|
17
|
-
- uses: pnpm/action-setup@
|
|
17
|
+
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
|
18
18
|
{{/if_eq}}
|
|
19
19
|
|
|
20
20
|
- name: Setup Node.js environment
|
|
21
|
-
uses: actions/setup-node@
|
|
21
|
+
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
|
22
22
|
with:
|
|
23
23
|
node-version: '22'
|
|
24
24
|
cache: '{{ packageManagerName }}'
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Project overview
|
|
2
|
+
This repository contains a **Grafana panel plugin**, providing a custom visualization for Grafana dashboards.
|
|
3
|
+
Panel plugins are used to:
|
|
4
|
+
|
|
5
|
+
* Display data from Grafana data sources in custom ways
|
|
6
|
+
* Add interactive behavior (drill-downs, navigation, etc.)
|
|
7
|
+
* Visualize or control external systems (IoT, integrations, custom controls)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Plugin anatomy
|
|
12
|
+
|
|
13
|
+
A typical panel plugin includes:
|
|
14
|
+
|
|
15
|
+
## **plugin.json**
|
|
16
|
+
* Declares plugin ID, type (`panel`), name, version
|
|
17
|
+
* Loaded by Grafana at startup
|
|
18
|
+
|
|
19
|
+
## **Main module (`src/module.ts`)**
|
|
20
|
+
* Exports: `new PanelPlugin(PanelComponent)`
|
|
21
|
+
* Registers panel options, migrations, defaults
|
|
22
|
+
|
|
23
|
+
## **Panel component (`src/components/Panel.tsx`)**
|
|
24
|
+
* React component receiving: `data`, `timeRange`, `width`, `height`, `options`
|
|
25
|
+
* Renders visualization using Grafana data frames and field configs
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
# Agent goals
|
|
30
|
+
|
|
31
|
+
Agents must:
|
|
32
|
+
|
|
33
|
+
* Preserve the existing options schema unless adding a migration handler
|
|
34
|
+
* Follow idiomatic React + TypeScript patterns used in official Grafana examples
|
|
35
|
+
* Treat **`AGENTS.md` as the authoritative source** over `.config/AGENTS/*`
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
# Repository layout (Compact)
|
|
40
|
+
|
|
41
|
+
* `plugin.json` — Panel plugin manifest
|
|
42
|
+
* `src/module.ts` — Main plugin entry
|
|
43
|
+
* `src/components/` — Panel React components
|
|
44
|
+
* `src/types.ts` — Option and model types
|
|
45
|
+
* `tests/` — E2E tests (if present)
|
|
46
|
+
* `provisioning/` — Local development provisioning
|
|
47
|
+
* `README.md` — Human documentation
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# Coding guidelines
|
|
52
|
+
|
|
53
|
+
* Use **TypeScript** and **functional React components**
|
|
54
|
+
* Use **@grafana/ui**, **@grafana/data**, **@grafana/runtime**
|
|
55
|
+
* Respect `width` and `height`; keep layout responsive
|
|
56
|
+
* Avoid unnecessary dependencies; ensure Grafana compatibility
|
|
57
|
+
* Use **Emotion** for custom styling
|
|
58
|
+
* Follow existing file structure
|
|
59
|
+
* Keep code typed and predictable
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
# Safety & constraints (Compact)
|
|
64
|
+
|
|
65
|
+
Agents must **not**:
|
|
66
|
+
|
|
67
|
+
* Change plugin IDs or plugin type in `plugin.json`
|
|
68
|
+
* Modify anything under `.config/*`
|
|
69
|
+
* Add a backend — panel plugins are **frontend only**
|
|
70
|
+
* Remove or change existing options without a migration handler
|
|
71
|
+
* Break public APIs (options, field configs, panel props)
|
|
72
|
+
|
|
73
|
+
Agents **should**:
|
|
74
|
+
|
|
75
|
+
* Keep the plugin backward compatible
|
|
76
|
+
* Mirror patterns from Grafana’s official panel plugin examples
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
# How-to
|
|
81
|
+
* [How-to add panel options](./howto/add-panel-options.md)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Adding a New Panel Option
|
|
2
|
+
|
|
3
|
+
Always complete **all three steps**:
|
|
4
|
+
|
|
5
|
+
### **1. Extend the options type**
|
|
6
|
+
|
|
7
|
+
File: `src/types.ts`
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
export interface SimpleOptions {
|
|
11
|
+
// existing...
|
|
12
|
+
myOptionName: MyOptionType;
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
* Property name must match the builder `path`.
|
|
17
|
+
* Type must match expected editor output.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
### **2. Register the option in `setPanelOptions`**
|
|
22
|
+
|
|
23
|
+
File: `src/module.ts` inside `setPanelOptions((builder) => { ... })`
|
|
24
|
+
|
|
25
|
+
Use the correct builder method:
|
|
26
|
+
|
|
27
|
+
| Type | Builder |
|
|
28
|
+
| ------------------ | -------------------- |
|
|
29
|
+
| boolean | `addBooleanSwitch` |
|
|
30
|
+
| number | `addNumberInput` |
|
|
31
|
+
| string | `addTextInput` |
|
|
32
|
+
| select | `addSelect` |
|
|
33
|
+
| radio | `addRadio` |
|
|
34
|
+
| radio group | `addRadio` |
|
|
35
|
+
| slider | `addSliderInput` |
|
|
36
|
+
| color picker | `addColorPicker` |
|
|
37
|
+
| unit picker | `addUnitPicker` |
|
|
38
|
+
| field namer picker | `addFieldNamePicker` |
|
|
39
|
+
|
|
40
|
+
Template:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
builder.addXxx({
|
|
44
|
+
path: 'myOptionName', // must match type property
|
|
45
|
+
name: 'My option',
|
|
46
|
+
defaultValue: <default>,
|
|
47
|
+
description: '',
|
|
48
|
+
settings: { /* optional */ },
|
|
49
|
+
// Optional visibility rule:
|
|
50
|
+
// showIf: (opts) => opts.someOtherOption,
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Rules:
|
|
55
|
+
|
|
56
|
+
* Every option **must** have a `defaultValue`.
|
|
57
|
+
* Put numeric constraints in `settings` (`min`, `max`, `step`).
|
|
58
|
+
* Use `showIf` for conditional display.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
### **3. Use the option in the panel component**
|
|
63
|
+
|
|
64
|
+
File: `src/Panel.tsx` (or equivalent)
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
export const Panel = ({ options }) => {
|
|
68
|
+
const { myOptionName } = options;
|
|
69
|
+
// apply it in rendering/logic
|
|
70
|
+
};
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
No option may remain unused.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
# Quick Editor Recipes
|
|
78
|
+
|
|
79
|
+
### **Boolean**
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
.addBooleanSwitch({ path: 'flag', name: 'Flag', defaultValue: false })
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### **Number**
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
.addNumberInput({
|
|
89
|
+
path: 'max',
|
|
90
|
+
name: 'Max value',
|
|
91
|
+
defaultValue: 10,
|
|
92
|
+
settings: { min: 1, max: 100, step: 1 },
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### **String**
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
.addTextInput({ path: 'label', name: 'Label', defaultValue: '' })
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### **Select**
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
.addSelect({
|
|
106
|
+
path: 'mode',
|
|
107
|
+
name: 'Mode',
|
|
108
|
+
defaultValue: 'auto',
|
|
109
|
+
settings: { options: [
|
|
110
|
+
{ value: 'a', label: 'A' },
|
|
111
|
+
{ value: 'b', label: 'B' },
|
|
112
|
+
]},
|
|
113
|
+
})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### **Radio**
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
.addRadio({
|
|
120
|
+
path: 'size',
|
|
121
|
+
name: 'Size',
|
|
122
|
+
defaultValue: 'md',
|
|
123
|
+
settings: { options: [
|
|
124
|
+
{ value: 'sm', label: 'Small' },
|
|
125
|
+
{ value: 'md', label: 'Medium' },
|
|
126
|
+
{ value: 'lg', label: 'Large' },
|
|
127
|
+
]},
|
|
128
|
+
showIf: (o) => o.showSize,
|
|
129
|
+
})
|
|
130
|
+
```
|