grafana-react 0.0.0 → 0.0.2

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.
Files changed (113) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -2
  3. package/build/cli/cli.test.d.ts +8 -0
  4. package/build/cli/cli.test.js +78 -0
  5. package/build/cli/index.d.ts +13 -0
  6. package/build/cli/index.js +266 -0
  7. package/build/components/annotation/annotation.d.ts +27 -0
  8. package/build/components/annotation/annotation.js +12 -0
  9. package/build/components/base.d.ts +23 -0
  10. package/build/components/base.js +28 -0
  11. package/build/components/dashboard/dashboard.d.ts +33 -0
  12. package/build/components/dashboard/dashboard.js +11 -0
  13. package/build/components/index.d.ts +44 -0
  14. package/build/components/index.js +54 -0
  15. package/build/components/link/link.d.ts +25 -0
  16. package/build/components/link/link.js +10 -0
  17. package/build/components/override/override.d.ts +22 -0
  18. package/build/components/override/override.js +11 -0
  19. package/build/components/panels/core/alert-list/alert-list.d.ts +33 -0
  20. package/build/components/panels/core/alert-list/alert-list.js +15 -0
  21. package/build/components/panels/core/annotations-list/annotations-list.d.ts +31 -0
  22. package/build/components/panels/core/annotations-list/annotations-list.js +15 -0
  23. package/build/components/panels/core/bar-chart/bar-chart.d.ts +47 -0
  24. package/build/components/panels/core/bar-chart/bar-chart.js +16 -0
  25. package/build/components/panels/core/bar-gauge/bar-gauge.d.ts +68 -0
  26. package/build/components/panels/core/bar-gauge/bar-gauge.js +30 -0
  27. package/build/components/panels/core/candlestick/candlestick.d.ts +42 -0
  28. package/build/components/panels/core/candlestick/candlestick.js +17 -0
  29. package/build/components/panels/core/canvas/canvas.d.ts +18 -0
  30. package/build/components/panels/core/canvas/canvas.js +12 -0
  31. package/build/components/panels/core/dashboard-list/dashboard-list.d.ts +35 -0
  32. package/build/components/panels/core/dashboard-list/dashboard-list.js +15 -0
  33. package/build/components/panels/core/datagrid/datagrid.d.ts +16 -0
  34. package/build/components/panels/core/datagrid/datagrid.js +12 -0
  35. package/build/components/panels/core/flame-graph/flame-graph.d.ts +16 -0
  36. package/build/components/panels/core/flame-graph/flame-graph.js +12 -0
  37. package/build/components/panels/core/gauge/gauge.d.ts +64 -0
  38. package/build/components/panels/core/gauge/gauge.js +30 -0
  39. package/build/components/panels/core/geomap/geomap.d.ts +20 -0
  40. package/build/components/panels/core/geomap/geomap.js +12 -0
  41. package/build/components/panels/core/heatmap/heatmap.d.ts +25 -0
  42. package/build/components/panels/core/heatmap/heatmap.js +16 -0
  43. package/build/components/panels/core/histogram/histogram.d.ts +39 -0
  44. package/build/components/panels/core/histogram/histogram.js +16 -0
  45. package/build/components/panels/core/logs/logs.d.ts +33 -0
  46. package/build/components/panels/core/logs/logs.js +17 -0
  47. package/build/components/panels/core/news/news.d.ts +20 -0
  48. package/build/components/panels/core/news/news.js +14 -0
  49. package/build/components/panels/core/node-graph/node-graph.d.ts +18 -0
  50. package/build/components/panels/core/node-graph/node-graph.js +12 -0
  51. package/build/components/panels/core/pie-chart/pie-chart.d.ts +33 -0
  52. package/build/components/panels/core/pie-chart/pie-chart.js +16 -0
  53. package/build/components/panels/core/stat/stat.d.ts +54 -0
  54. package/build/components/panels/core/stat/stat.js +18 -0
  55. package/build/components/panels/core/state-timeline/state-timeline.d.ts +37 -0
  56. package/build/components/panels/core/state-timeline/state-timeline.js +16 -0
  57. package/build/components/panels/core/status-history/status-history.d.ts +35 -0
  58. package/build/components/panels/core/status-history/status-history.js +16 -0
  59. package/build/components/panels/core/table/table.d.ts +63 -0
  60. package/build/components/panels/core/table/table.js +25 -0
  61. package/build/components/panels/core/text/text.d.ts +16 -0
  62. package/build/components/panels/core/text/text.js +10 -0
  63. package/build/components/panels/core/timeseries/timeseries.d.ts +103 -0
  64. package/build/components/panels/core/timeseries/timeseries.js +31 -0
  65. package/build/components/panels/core/traces/traces.d.ts +18 -0
  66. package/build/components/panels/core/traces/traces.js +12 -0
  67. package/build/components/panels/core/trend/trend.d.ts +39 -0
  68. package/build/components/panels/core/trend/trend.js +16 -0
  69. package/build/components/panels/core/xy-chart/xy-chart.d.ts +44 -0
  70. package/build/components/panels/core/xy-chart/xy-chart.js +17 -0
  71. package/build/components/panels/index.d.ts +32 -0
  72. package/build/components/panels/index.js +38 -0
  73. package/build/components/panels/plugins/business-variable-panel/business-variable-panel.d.ts +85 -0
  74. package/build/components/panels/plugins/business-variable-panel/business-variable-panel.js +15 -0
  75. package/build/components/plugin-panel/plugin-panel.d.ts +25 -0
  76. package/build/components/plugin-panel/plugin-panel.js +17 -0
  77. package/build/components/query/query.d.ts +33 -0
  78. package/build/components/query/query.js +19 -0
  79. package/build/components/row/row.d.ts +27 -0
  80. package/build/components/row/row.js +13 -0
  81. package/build/components/variable/variable.d.ts +32 -0
  82. package/build/components/variable/variable.js +12 -0
  83. package/build/index.d.ts +33 -0
  84. package/build/index.js +52 -0
  85. package/build/lib/index.d.ts +5 -0
  86. package/build/lib/index.js +5 -0
  87. package/build/lib/renderer.d.ts +24 -0
  88. package/build/lib/renderer.js +1531 -0
  89. package/build/lib/renderer.test.d.ts +4 -0
  90. package/build/lib/renderer.test.js +480 -0
  91. package/build/lib/utils.d.ts +70 -0
  92. package/build/lib/utils.js +229 -0
  93. package/build/lib/utils.test.d.ts +4 -0
  94. package/build/lib/utils.test.js +252 -0
  95. package/build/types/common/axis.d.ts +76 -0
  96. package/build/types/common/axis.js +8 -0
  97. package/build/types/common/enums.d.ts +50 -0
  98. package/build/types/common/enums.js +9 -0
  99. package/build/types/common/field-config.d.ts +123 -0
  100. package/build/types/common/field-config.js +8 -0
  101. package/build/types/common/index.d.ts +10 -0
  102. package/build/types/common/index.js +7 -0
  103. package/build/types/common/viz-options.d.ts +94 -0
  104. package/build/types/common/viz-options.js +8 -0
  105. package/build/types/display.d.ts +29 -0
  106. package/build/types/display.js +4 -0
  107. package/build/types/grafana-json.d.ts +147 -0
  108. package/build/types/grafana-json.js +6 -0
  109. package/build/types/index.d.ts +10 -0
  110. package/build/types/index.js +7 -0
  111. package/build/types/panel-base.d.ts +72 -0
  112. package/build/types/panel-base.js +4 -0
  113. package/package.json +67 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kiwi Research
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,5 +1,91 @@
1
1
  # grafana-react
2
2
 
3
- React DSL for Grafana dashboards. Coming soon.
3
+ React-based DSL for creating Grafana dashboards. Write dashboards as JSX components and compile them to Grafana JSON.
4
4
 
5
- https://github.com/kiwi-research/grafana-react
5
+ <p align="center">
6
+ <img src="docs/public/img/hero-code.png" alt="JSX code for Stat panel" height="180" />
7
+ &nbsp;&nbsp;&nbsp;
8
+ <img src="docs/public/img/hero-grafana-stat-panel.png" alt="Rendered Grafana Stat panel" height="140" />
9
+ </p>
10
+
11
+ ## Features
12
+
13
+ - **Declarative JSX Syntax** - Write dashboards using familiar React patterns
14
+ - **Component Composition** - Create reusable dashboard components
15
+ - **Type Safety** - Full TypeScript support with comprehensive types
16
+ - **CLI Tool** - Build, validate, and watch dashboard files
17
+ - **Zero Runtime** - Compiles to static JSON, no React in production
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install grafana-react react@19 tsx@4
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```tsx
28
+ import { Dashboard, Row, Stat, Timeseries, Variable } from 'grafana-react';
29
+
30
+ export default function MyDashboard() {
31
+ return (
32
+ <Dashboard uid="my-dashboard" title="My Dashboard" datasource="prometheus">
33
+ <Variable name="instance">label_values(up, instance)</Variable>
34
+
35
+ <Row title="Summary">
36
+ <Stat
37
+ title="CPU %"
38
+ unit="percent"
39
+ thresholds={{ 70: 'yellow', 90: 'red' }}
40
+ >
41
+ 100 - avg(rate(cpu_idle[$__rate_interval])) * 100
42
+ </Stat>
43
+ </Row>
44
+
45
+ <Row title="Details">
46
+ <Timeseries title="CPU Over Time" unit="percent" stack legend="right">
47
+ sum(rate(node_cpu_seconds_total[5m])) by (mode) * 100
48
+ </Timeseries>
49
+ </Row>
50
+ </Dashboard>
51
+ );
52
+ }
53
+ ```
54
+
55
+ Build to JSON using the CLI:
56
+
57
+ ```bash
58
+ npx grafana-react build my-dashboard.tsx output/my-dashboard.json
59
+ ```
60
+
61
+ Or programmatically with `renderToString`:
62
+
63
+ ```ts
64
+ import React from 'react';
65
+ import { renderToString } from 'grafana-react';
66
+
67
+ // Use the JSON string directly, e.g. add as a ConfigMap with Pulumi
68
+ const json = renderToString(React.createElement(MyDashboard));
69
+ ```
70
+
71
+ ## Documentation
72
+
73
+ Full documentation is available at: **[kiwi-research.github.io/grafana-react](https://kiwi-research.github.io/grafana-react)**
74
+
75
+ - [Getting Started](https://kiwi-research.github.io/grafana-react/getting-started/installation/)
76
+ - [Components Reference](https://kiwi-research.github.io/grafana-react/components/structure/)
77
+ - [CLI Usage](https://kiwi-research.github.io/grafana-react/guides/cli-usage/)
78
+ - [Reusable Components](https://kiwi-research.github.io/grafana-react/guides/reusable-components/)
79
+
80
+ ## CLI Commands
81
+
82
+ ```bash
83
+ npx grafana-react build <input.tsx> [output.json] # Build single dashboard
84
+ npx grafana-react build-all <dir> [output-dir] # Build all dashboards
85
+ npx grafana-react validate <input.tsx> # Validate without output
86
+ npx grafana-react watch <dir> [output-dir] # Watch and rebuild
87
+ ```
88
+
89
+ ## License
90
+
91
+ MIT
@@ -0,0 +1,8 @@
1
+ /**
2
+ * CLI integration tests
3
+ *
4
+ * These tests verify the CLI's basic functionality.
5
+ * Note: Full dashboard compilation tests are limited because Node.js
6
+ * cannot dynamically import .tsx files without a TypeScript loader.
7
+ */
8
+ export {};
@@ -0,0 +1,78 @@
1
+ /**
2
+ * CLI integration tests
3
+ *
4
+ * These tests verify the CLI's basic functionality.
5
+ * Note: Full dashboard compilation tests are limited because Node.js
6
+ * cannot dynamically import .tsx files without a TypeScript loader.
7
+ */
8
+ import { describe, it } from 'node:test';
9
+ import assert from 'node:assert';
10
+ import { execSync, spawnSync } from 'node:child_process';
11
+ import * as path from 'node:path';
12
+ // Path to the built CLI
13
+ const CLI_PATH = path.resolve(import.meta.dirname, '../../build/cli/index.js');
14
+ describe('CLI integration', () => {
15
+ it('shows help with --help flag', () => {
16
+ const result = execSync(`node ${CLI_PATH} --help`, {
17
+ encoding: 'utf8',
18
+ });
19
+ assert.ok(result.includes('grafana-react'));
20
+ assert.ok(result.includes('build'));
21
+ assert.ok(result.includes('validate'));
22
+ assert.ok(result.includes('watch'));
23
+ assert.ok(result.includes('build-all'));
24
+ });
25
+ it('shows help with no arguments', () => {
26
+ const result = execSync(`node ${CLI_PATH}`, {
27
+ encoding: 'utf8',
28
+ });
29
+ assert.ok(result.includes('Usage:'));
30
+ });
31
+ it('shows version with --version flag', () => {
32
+ const result = execSync(`node ${CLI_PATH} --version`, {
33
+ encoding: 'utf8',
34
+ });
35
+ assert.ok(result.includes('grafana-react v'));
36
+ });
37
+ it('shows version with -v flag', () => {
38
+ const result = execSync(`node ${CLI_PATH} -v`, {
39
+ encoding: 'utf8',
40
+ });
41
+ assert.ok(result.includes('grafana-react v'));
42
+ });
43
+ it('exits with error for missing input file on build', () => {
44
+ const result = spawnSync('node', [CLI_PATH, 'build'], {
45
+ encoding: 'utf8',
46
+ });
47
+ assert.strictEqual(result.status, 1);
48
+ assert.ok(result.stderr.includes('Missing input file'));
49
+ });
50
+ it('exits with error for missing input directory on build-all', () => {
51
+ const result = spawnSync('node', [CLI_PATH, 'build-all'], {
52
+ encoding: 'utf8',
53
+ });
54
+ assert.strictEqual(result.status, 1);
55
+ assert.ok(result.stderr.includes('Missing input directory'));
56
+ });
57
+ it('exits with error for missing input file on validate', () => {
58
+ const result = spawnSync('node', [CLI_PATH, 'validate'], {
59
+ encoding: 'utf8',
60
+ });
61
+ assert.strictEqual(result.status, 1);
62
+ assert.ok(result.stderr.includes('Missing input file'));
63
+ });
64
+ it('exits with error for non-existent file', () => {
65
+ const result = spawnSync('node', [CLI_PATH, 'build', '/nonexistent/file.tsx'], {
66
+ encoding: 'utf8',
67
+ });
68
+ assert.strictEqual(result.status, 1);
69
+ assert.ok(result.stderr.includes('File not found'));
70
+ });
71
+ it('exits with error for unknown command', () => {
72
+ const result = spawnSync('node', [CLI_PATH, 'unknown-command'], {
73
+ encoding: 'utf8',
74
+ });
75
+ assert.strictEqual(result.status, 1);
76
+ assert.ok(result.stderr.includes('Unknown command'));
77
+ });
78
+ });
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Grafana React CLI
4
+ *
5
+ * Build Grafana dashboards from React/TSX files.
6
+ *
7
+ * Usage:
8
+ * grafana-react build <input> [output] Build a single dashboard
9
+ * grafana-react build-all <dir> [outdir] Build all dashboards in a directory
10
+ * grafana-react validate <input> Validate a dashboard without output
11
+ * grafana-react watch <dir> [outdir] Watch and rebuild on changes
12
+ */
13
+ export {};
@@ -0,0 +1,266 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Grafana React CLI
4
+ *
5
+ * Build Grafana dashboards from React/TSX files.
6
+ *
7
+ * Usage:
8
+ * grafana-react build <input> [output] Build a single dashboard
9
+ * grafana-react build-all <dir> [outdir] Build all dashboards in a directory
10
+ * grafana-react validate <input> Validate a dashboard without output
11
+ * grafana-react watch <dir> [outdir] Watch and rebuild on changes
12
+ */
13
+ // Register tsx loader for TypeScript/TSX support
14
+ try {
15
+ const { register } = await import('tsx/esm/api');
16
+ register();
17
+ }
18
+ catch {
19
+ console.error(`Error: The grafana-react CLI requires 'tsx' to be installed.
20
+
21
+ Install it with:
22
+ npm install tsx@4
23
+
24
+ Or install all peer dependencies:
25
+ npm install react@19 tsx@4
26
+ `);
27
+ process.exit(1);
28
+ }
29
+ import * as fs from 'node:fs';
30
+ import * as path from 'node:path';
31
+ import React from 'react';
32
+ import { renderToString } from '../lib/renderer.js';
33
+ // ============================================================================
34
+ // CLI Helpers
35
+ // ============================================================================
36
+ function printUsage() {
37
+ console.log(`
38
+ grafana-react - Build Grafana dashboards from React/TSX files
39
+
40
+ Usage:
41
+ grafana-react build <input.tsx> [output.json]
42
+ Build a single dashboard from a TSX file.
43
+ If output is not specified, prints to stdout.
44
+
45
+ grafana-react build-all <input-dir> [output-dir]
46
+ Build all *.dashboard.tsx files in a directory.
47
+ Output files are named *.gen.json in the output directory.
48
+
49
+ grafana-react validate <input.tsx>
50
+ Validate a dashboard file without generating output.
51
+
52
+ grafana-react watch <input-dir> [output-dir]
53
+ Watch for changes and rebuild automatically.
54
+
55
+ Options:
56
+ --help, -h Show this help message
57
+ --version, -v Show version
58
+
59
+ Examples:
60
+ grafana-react build dashboards/node.dashboard.tsx
61
+ grafana-react build dashboards/node.dashboard.tsx output/node.json
62
+ grafana-react build-all dashboards/ output/
63
+ grafana-react validate dashboards/node.dashboard.tsx
64
+ `);
65
+ }
66
+ function printVersion() {
67
+ const pkgPath = new URL('../../package.json', import.meta.url);
68
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
69
+ console.log(`grafana-react v${pkg.version}`);
70
+ }
71
+ function error(message) {
72
+ console.error(`Error: ${message}`);
73
+ process.exit(1);
74
+ }
75
+ function success(message) {
76
+ console.log(`\u2713 ${message}`);
77
+ }
78
+ // ============================================================================
79
+ // Build Functions
80
+ // ============================================================================
81
+ async function loadDashboard(inputFile) {
82
+ const absolutePath = path.resolve(inputFile);
83
+ if (!fs.existsSync(absolutePath)) {
84
+ error(`File not found: ${absolutePath}`);
85
+ }
86
+ const ext = path.extname(absolutePath);
87
+ if (ext !== '.tsx' && ext !== '.ts') {
88
+ error(`Expected .tsx or .ts file, got ${ext}`);
89
+ }
90
+ // Import the module (tsx loader handles TSX transformation)
91
+ const module = (await import(absolutePath));
92
+ // Find the dashboard component
93
+ const Component = module.default ??
94
+ module.Dashboard ??
95
+ Object.values(module).find((exp) => typeof exp === 'function' &&
96
+ /dashboard/i.test(exp.name ?? ''));
97
+ if (!Component) {
98
+ error('No dashboard component found. Export as default or named "Dashboard"');
99
+ }
100
+ return React.createElement(Component);
101
+ }
102
+ async function buildOne(inputFile, outputFile) {
103
+ const element = await loadDashboard(inputFile);
104
+ const json = renderToString(element);
105
+ if (outputFile) {
106
+ const absoluteOutput = path.resolve(outputFile);
107
+ const outputDir = path.dirname(absoluteOutput);
108
+ if (!fs.existsSync(outputDir)) {
109
+ fs.mkdirSync(outputDir, { recursive: true });
110
+ }
111
+ fs.writeFileSync(absoluteOutput, json + '\n');
112
+ success(`Built ${path.relative(process.cwd(), absoluteOutput)}`);
113
+ }
114
+ else {
115
+ console.log(json);
116
+ }
117
+ }
118
+ async function buildAll(inputDir, outputDir) {
119
+ const absoluteInput = path.resolve(inputDir);
120
+ if (!fs.existsSync(absoluteInput)) {
121
+ error(`Directory not found: ${absoluteInput}`);
122
+ }
123
+ const files = findDashboardFiles(absoluteInput);
124
+ if (files.length === 0) {
125
+ console.log('No dashboard files found (*.dashboard.tsx)');
126
+ return;
127
+ }
128
+ console.log(`Found ${files.length} dashboard file(s)`);
129
+ const resolvedOutputDir = outputDir ? path.resolve(outputDir) : absoluteInput;
130
+ for (const file of files) {
131
+ const relativePath = path.relative(absoluteInput, file);
132
+ const outputName = relativePath.replace(/\.dashboard\.tsx$/, '.gen.json');
133
+ const outputPath = path.join(resolvedOutputDir, outputName);
134
+ try {
135
+ const element = await loadDashboard(file);
136
+ const json = renderToString(element);
137
+ const outputFileDir = path.dirname(outputPath);
138
+ if (!fs.existsSync(outputFileDir)) {
139
+ fs.mkdirSync(outputFileDir, { recursive: true });
140
+ }
141
+ fs.writeFileSync(outputPath, json + '\n');
142
+ success(`${relativePath} -> ${path.relative(process.cwd(), outputPath)}`);
143
+ }
144
+ catch (err) {
145
+ console.error(`\u2717 ${relativePath}: ${err.message}`);
146
+ }
147
+ }
148
+ }
149
+ async function validate(inputFile) {
150
+ const element = await loadDashboard(inputFile);
151
+ // Just render to validate - don't output
152
+ renderToString(element);
153
+ success(`Valid: ${path.relative(process.cwd(), inputFile)}`);
154
+ }
155
+ async function watch(inputDir, outputDir) {
156
+ const absoluteInput = path.resolve(inputDir);
157
+ const resolvedOutputDir = outputDir ? path.resolve(outputDir) : absoluteInput;
158
+ console.log(`Watching ${absoluteInput} for changes...`);
159
+ // Initial build
160
+ await buildAll(inputDir, outputDir);
161
+ // Watch for changes
162
+ const { watch: fsWatch } = await import('node:fs');
163
+ fsWatch(absoluteInput, { recursive: true }, async (_eventType, filename) => {
164
+ if (!filename?.endsWith('.dashboard.tsx'))
165
+ return;
166
+ const filePath = path.join(absoluteInput, filename);
167
+ const relativePath = path.relative(absoluteInput, filePath);
168
+ const outputName = relativePath.replace(/\.dashboard\.tsx$/, '.gen.json');
169
+ const outputPath = path.join(resolvedOutputDir, outputName);
170
+ console.log(`\nRebuilding ${filename}...`);
171
+ try {
172
+ // Clear module cache for hot reload
173
+ const absolutePath = path.resolve(filePath);
174
+ delete require.cache?.[absolutePath];
175
+ const element = await loadDashboard(filePath);
176
+ const json = renderToString(element);
177
+ const outputFileDir = path.dirname(outputPath);
178
+ if (!fs.existsSync(outputFileDir)) {
179
+ fs.mkdirSync(outputFileDir, { recursive: true });
180
+ }
181
+ fs.writeFileSync(outputPath, json + '\n');
182
+ success(`${relativePath} -> ${path.relative(process.cwd(), outputPath)}`);
183
+ }
184
+ catch (err) {
185
+ console.error(`\u2717 ${relativePath}: ${err.message}`);
186
+ }
187
+ });
188
+ // Keep process alive
189
+ process.on('SIGINT', () => {
190
+ console.log('\nStopping watch...');
191
+ process.exit(0);
192
+ });
193
+ }
194
+ // ============================================================================
195
+ // File Discovery
196
+ // ============================================================================
197
+ function findDashboardFiles(dir) {
198
+ const files = [];
199
+ function walk(currentDir) {
200
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
201
+ for (const entry of entries) {
202
+ const fullPath = path.join(currentDir, entry.name);
203
+ if (entry.isDirectory() && entry.name !== 'node_modules') {
204
+ walk(fullPath);
205
+ }
206
+ else if (entry.isFile() && entry.name.endsWith('.dashboard.tsx')) {
207
+ files.push(fullPath);
208
+ }
209
+ }
210
+ }
211
+ walk(dir);
212
+ return files.sort();
213
+ }
214
+ // ============================================================================
215
+ // Main
216
+ // ============================================================================
217
+ async function main() {
218
+ const args = process.argv.slice(2);
219
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
220
+ printUsage();
221
+ process.exit(0);
222
+ }
223
+ if (args.includes('--version') || args.includes('-v')) {
224
+ printVersion();
225
+ process.exit(0);
226
+ }
227
+ const command = args[0];
228
+ switch (command) {
229
+ case 'build':
230
+ if (!args[1]) {
231
+ error('Missing input file. Usage: grafana-react build <input.tsx> [output.json]');
232
+ }
233
+ await buildOne(args[1], args[2]);
234
+ break;
235
+ case 'build-all':
236
+ if (!args[1]) {
237
+ error('Missing input directory. Usage: grafana-react build-all <input-dir> [output-dir]');
238
+ }
239
+ await buildAll(args[1], args[2]);
240
+ break;
241
+ case 'validate':
242
+ if (!args[1]) {
243
+ error('Missing input file. Usage: grafana-react validate <input.tsx>');
244
+ }
245
+ await validate(args[1]);
246
+ break;
247
+ case 'watch':
248
+ if (!args[1]) {
249
+ error('Missing input directory. Usage: grafana-react watch <input-dir> [output-dir]');
250
+ }
251
+ await watch(args[1], args[2]);
252
+ break;
253
+ default:
254
+ // Treat as build command if it's a file path
255
+ if (args[0].endsWith('.tsx') || args[0].endsWith('.ts')) {
256
+ await buildOne(args[0], args[1]);
257
+ }
258
+ else {
259
+ error(`Unknown command: ${command}. Use --help for usage.`);
260
+ }
261
+ }
262
+ }
263
+ main().catch((err) => {
264
+ console.error('Error:', err.message);
265
+ process.exit(1);
266
+ });
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Annotation - Alert annotation markers
3
+ *
4
+ * Annotations display markers on panels when alerts fire.
5
+ *
6
+ * @example
7
+ * <Annotation name="Alerts" color="light-red" title="{{alertname}}">
8
+ * ALERTS{alertstate="firing"}
9
+ * </Annotation>
10
+ */
11
+ export interface AnnotationProps {
12
+ /** Annotation name */
13
+ name: string;
14
+ /** Marker color */
15
+ color?: string;
16
+ /** Title format using {{label}} syntax */
17
+ title?: string;
18
+ /** Tag keys to include */
19
+ tags?: string;
20
+ /** Hide annotation from view */
21
+ hide?: boolean;
22
+ /** Enable annotation */
23
+ enabled?: boolean;
24
+ /** PromQL expression as child */
25
+ children?: string;
26
+ }
27
+ export declare const Annotation: import("react").FC<AnnotationProps>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Annotation - Alert annotation markers
3
+ *
4
+ * Annotations display markers on panels when alerts fire.
5
+ *
6
+ * @example
7
+ * <Annotation name="Alerts" color="light-red" title="{{alertname}}">
8
+ * ALERTS{alertstate="firing"}
9
+ * </Annotation>
10
+ */
11
+ import { createComponent } from '../base.js';
12
+ export const Annotation = createComponent('annotation');
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Base component utilities
3
+ *
4
+ * Provides the foundation for creating grafana-react components.
5
+ * Components don't render to DOM - they create a virtual tree
6
+ * that gets converted to Grafana JSON by the renderer.
7
+ */
8
+ import type React from 'react';
9
+ /** Symbol used to identify component types */
10
+ export declare const COMPONENT_TYPE: unique symbol;
11
+ /** All component types */
12
+ export type ComponentType = 'dashboard' | 'row' | 'variable' | 'annotation' | 'link' | 'query' | 'override' | 'stat' | 'timeseries' | 'table' | 'bargauge' | 'heatmap' | 'gauge' | 'text' | 'barchart' | 'piechart' | 'histogram' | 'state-timeline' | 'status-history' | 'candlestick' | 'trend' | 'xychart' | 'logs' | 'datagrid' | 'nodeGraph' | 'traces' | 'flamegraph' | 'canvas' | 'geomap' | 'dashlist' | 'alertlist' | 'annolist' | 'news' | 'plugin' | 'business-variable';
13
+ /**
14
+ * Create a grafana-react component with type marker
15
+ *
16
+ * Components created this way don't render anything - they just
17
+ * carry their props and children for the renderer to process.
18
+ */
19
+ export declare function createComponent<P extends object>(type: ComponentType): React.FC<P>;
20
+ /**
21
+ * Get the component type from a React element
22
+ */
23
+ export declare function getComponentType(element: React.ReactElement): ComponentType | null;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Base component utilities
3
+ *
4
+ * Provides the foundation for creating grafana-react components.
5
+ * Components don't render to DOM - they create a virtual tree
6
+ * that gets converted to Grafana JSON by the renderer.
7
+ */
8
+ /** Symbol used to identify component types */
9
+ export const COMPONENT_TYPE = Symbol('grafana-component-type');
10
+ /**
11
+ * Create a grafana-react component with type marker
12
+ *
13
+ * Components created this way don't render anything - they just
14
+ * carry their props and children for the renderer to process.
15
+ */
16
+ export function createComponent(type) {
17
+ const Component = () => null;
18
+ Component[COMPONENT_TYPE] = type;
19
+ Component.displayName = type.charAt(0).toUpperCase() + type.slice(1);
20
+ return Component;
21
+ }
22
+ /**
23
+ * Get the component type from a React element
24
+ */
25
+ export function getComponentType(element) {
26
+ const type = element.type;
27
+ return type?.[COMPONENT_TYPE] ?? null;
28
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Dashboard - Root container for Grafana dashboards
3
+ *
4
+ * @example
5
+ * <Dashboard uid="my-dashboard" title="My Dashboard" tags={['tag1']}>
6
+ * <Variable name="instance" />
7
+ * <Row title="Summary">...</Row>
8
+ * </Dashboard>
9
+ */
10
+ import type { ReactNode } from 'react';
11
+ export interface DashboardProps {
12
+ /** Unique identifier for the dashboard */
13
+ uid: string;
14
+ /** Dashboard title */
15
+ title: string;
16
+ /** Dashboard tags for filtering */
17
+ tags?: string[];
18
+ /** Default datasource UID for all panels */
19
+ datasource?: string;
20
+ /** Datasource type (e.g., 'prometheus', 'victoriametrics-metrics-datasource'). Defaults to 'prometheus' */
21
+ datasourceType?: string;
22
+ /** Auto-refresh interval (e.g., '5s', '1m', 'auto') */
23
+ refresh?: string | 'auto' | false;
24
+ /** Default time range (e.g., '1h', '6h', '24h') */
25
+ time?: string;
26
+ /** Timezone setting */
27
+ timezone?: 'browser' | 'utc';
28
+ /** Tooltip sharing mode */
29
+ tooltip?: 'shared' | 'single' | 'hidden';
30
+ /** Dashboard children */
31
+ children?: ReactNode;
32
+ }
33
+ export declare const Dashboard: import("react").FC<DashboardProps>;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Dashboard - Root container for Grafana dashboards
3
+ *
4
+ * @example
5
+ * <Dashboard uid="my-dashboard" title="My Dashboard" tags={['tag1']}>
6
+ * <Variable name="instance" />
7
+ * <Row title="Summary">...</Row>
8
+ * </Dashboard>
9
+ */
10
+ import { createComponent } from '../base.js';
11
+ export const Dashboard = createComponent('dashboard');
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Component exports
3
+ *
4
+ * All components and their prop types are exported from here.
5
+ */
6
+ export { COMPONENT_TYPE, getComponentType, createComponent } from './base.js';
7
+ export type { ComponentType } from './base.js';
8
+ export { Dashboard, type DashboardProps } from './dashboard/dashboard.js';
9
+ export { Row, type RowProps } from './row/row.js';
10
+ export { Variable, type VariableProps } from './variable/variable.js';
11
+ export { Annotation, type AnnotationProps } from './annotation/annotation.js';
12
+ export { Link, type LinkProps } from './link/link.js';
13
+ export { Query, type QueryProps } from './query/query.js';
14
+ export { Override, type OverrideProps } from './override/override.js';
15
+ export { PluginPanel, type PluginPanelProps, } from './plugin-panel/plugin-panel.js';
16
+ export { Stat, type StatProps } from './panels/core/stat/stat.js';
17
+ export { Timeseries, type TimeseriesProps, } from './panels/core/timeseries/timeseries.js';
18
+ export { Table, type TableProps } from './panels/core/table/table.js';
19
+ export { BarGauge, type BarGaugeProps, } from './panels/core/bar-gauge/bar-gauge.js';
20
+ export { Heatmap, type HeatmapProps } from './panels/core/heatmap/heatmap.js';
21
+ export { Gauge, type GaugeProps } from './panels/core/gauge/gauge.js';
22
+ export { Text, type TextProps } from './panels/core/text/text.js';
23
+ export { BarChart, type BarChartProps, } from './panels/core/bar-chart/bar-chart.js';
24
+ export { PieChart, type PieChartProps, } from './panels/core/pie-chart/pie-chart.js';
25
+ export { Histogram, type HistogramProps, } from './panels/core/histogram/histogram.js';
26
+ export { StateTimeline, type StateTimelineProps, } from './panels/core/state-timeline/state-timeline.js';
27
+ export { StatusHistory, type StatusHistoryProps, } from './panels/core/status-history/status-history.js';
28
+ export { Candlestick, type CandlestickProps, } from './panels/core/candlestick/candlestick.js';
29
+ export { Trend, type TrendProps } from './panels/core/trend/trend.js';
30
+ export { XYChart, type XYChartProps } from './panels/core/xy-chart/xy-chart.js';
31
+ export { Logs, type LogsProps } from './panels/core/logs/logs.js';
32
+ export { Datagrid, type DatagridProps, } from './panels/core/datagrid/datagrid.js';
33
+ export { NodeGraph, type NodeGraphProps, } from './panels/core/node-graph/node-graph.js';
34
+ export { Traces, type TracesProps } from './panels/core/traces/traces.js';
35
+ export { FlameGraph, type FlameGraphProps, } from './panels/core/flame-graph/flame-graph.js';
36
+ export { Canvas, type CanvasProps } from './panels/core/canvas/canvas.js';
37
+ export { Geomap, type GeomapProps } from './panels/core/geomap/geomap.js';
38
+ export { DashboardList, type DashboardListProps, } from './panels/core/dashboard-list/dashboard-list.js';
39
+ export { AlertList, type AlertListProps, } from './panels/core/alert-list/alert-list.js';
40
+ export { AnnotationsList, type AnnotationsListProps, } from './panels/core/annotations-list/annotations-list.js';
41
+ export { News, type NewsProps } from './panels/core/news/news.js';
42
+ export { BusinessVariablePanel, type BusinessVariablePanelProps, type TreeViewLevel, type VariableGroup, } from './panels/plugins/business-variable-panel/business-variable-panel.js';
43
+ import { Fragment } from 'react';
44
+ export { Fragment };