mirror of
https://github.com/grafana/grafana.git
synced 2025-12-23 05:04:29 +08:00
Compare commits
119 Commits
docs/add-t
...
jackw/poc-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c7aac77d3 | ||
|
|
d17b2d3939 | ||
|
|
7da5a86f7e | ||
|
|
67c4bd604a | ||
|
|
9bbbdd3d6e | ||
|
|
80ea8a0901 | ||
|
|
5e8f0c6f52 | ||
|
|
db4ef2d0be | ||
|
|
1feace3bbe | ||
|
|
d3469ceca0 | ||
|
|
846afe2e68 | ||
|
|
d830e2085d | ||
|
|
1bd546568f | ||
|
|
f75463e170 | ||
|
|
bf68d041fa | ||
|
|
dbb2097f40 | ||
|
|
0911638c91 | ||
|
|
a6ab645029 | ||
|
|
63dc588531 | ||
|
|
c5d89b53d8 | ||
|
|
f6b49880df | ||
|
|
030eb759a9 | ||
|
|
eb7af07b19 | ||
|
|
6550ffcc23 | ||
|
|
12b2a63b55 | ||
|
|
762d9a0801 | ||
|
|
9da6111c05 | ||
|
|
f016bfe60e | ||
|
|
3b44fab842 | ||
|
|
6f150098fd | ||
|
|
caaa1fe62f | ||
|
|
54b4cbf3f1 | ||
|
|
6690095d18 | ||
|
|
e54ce29a29 | ||
|
|
416afce642 | ||
|
|
5c10f2677b | ||
|
|
16ae94dadc | ||
|
|
43189a3848 | ||
|
|
af0d904bfe | ||
|
|
22955700ca | ||
|
|
727a3723a1 | ||
|
|
edda259bad | ||
|
|
5e21abeed7 | ||
|
|
0a01254c68 | ||
|
|
6c812409fe | ||
|
|
4ba0bbb602 | ||
|
|
ad36be9612 | ||
|
|
60a7faca5b | ||
|
|
830187d688 | ||
|
|
5298d168cc | ||
|
|
57f74c0740 | ||
|
|
0e643f352b | ||
|
|
be481d2780 | ||
|
|
1115cbb722 | ||
|
|
1fd1bb5dff | ||
|
|
732dc952de | ||
|
|
904084614b | ||
|
|
4c9f0387fa | ||
|
|
8d3d389362 | ||
|
|
8f2ebceddf | ||
|
|
ac5eb6afab | ||
|
|
f7be501f9a | ||
|
|
d7f6f87873 | ||
|
|
c9731da894 | ||
|
|
6ccec33c6a | ||
|
|
785c05f185 | ||
|
|
6b55313560 | ||
|
|
06d4038d4f | ||
|
|
e56168fdff | ||
|
|
30aa59179a | ||
|
|
a3f7e26220 | ||
|
|
9f8eb74d45 | ||
|
|
41d72308ae | ||
|
|
8fdd1f530d | ||
|
|
447a65e09a | ||
|
|
2752ed52ff | ||
|
|
b56928bc89 | ||
|
|
ea1bc89872 | ||
|
|
6b2f16ae9d | ||
|
|
8a3870f297 | ||
|
|
4101d2adbf | ||
|
|
b14f695bf7 | ||
|
|
1a3a100de5 | ||
|
|
7c10280b7f | ||
|
|
c6cc559b5a | ||
|
|
619ce4bb8b | ||
|
|
bf053dc744 | ||
|
|
886be61495 | ||
|
|
7e924511b3 | ||
|
|
f77294c268 | ||
|
|
feff659957 | ||
|
|
c7368baa30 | ||
|
|
b89599a014 | ||
|
|
ecc1fbdb54 | ||
|
|
024773f33d | ||
|
|
b53f9e97f0 | ||
|
|
03960b45f8 | ||
|
|
a5a024610c | ||
|
|
d8531fcb44 | ||
|
|
18064278c8 | ||
|
|
58b4ed1411 | ||
|
|
17bb5cec36 | ||
|
|
11b6a42bb6 | ||
|
|
8ad543a486 | ||
|
|
f20c275bc8 | ||
|
|
44d534b80e | ||
|
|
ca490c9ba1 | ||
|
|
c8996b2dd4 | ||
|
|
9d8bcd636f | ||
|
|
4c2ca3247d | ||
|
|
248fb12625 | ||
|
|
e2bacbc74a | ||
|
|
8daeb1b8e3 | ||
|
|
e7c65e61b8 | ||
|
|
c765ef878a | ||
|
|
77412b96a7 | ||
|
|
4a82103632 | ||
|
|
db6d3e458f | ||
|
|
18cbc6cd00 |
8855
.betterer.results
8855
.betterer.results
File diff suppressed because it is too large
Load Diff
16
.github/CODEOWNERS
vendored
16
.github/CODEOWNERS
vendored
@@ -439,9 +439,10 @@
|
||||
/project.json @grafana/frontend-ops
|
||||
/.nxignore @grafana/frontend-ops
|
||||
/tsconfig.json @grafana/frontend-ops
|
||||
/tsconfig.test.json @grafana/frontend-ops
|
||||
/.editorconfig @grafana/frontend-ops
|
||||
/eslint.config.js @grafana/frontend-ops
|
||||
/.betterer.eslint.config.js @grafana/frontend-ops
|
||||
/eslint.config.cjs @grafana/frontend-ops
|
||||
/.betterer.eslint.config.cjs @grafana/frontend-ops
|
||||
/.gitattributes @grafana/frontend-ops
|
||||
/.gitignore @grafana/frontend-ops
|
||||
/.ignore @grafana/frontend-ops
|
||||
@@ -453,16 +454,16 @@
|
||||
/lerna.json @grafana/frontend-ops
|
||||
/.prettierrc.js @grafana/frontend-ops
|
||||
/.vim @zoltanbedi
|
||||
/jest.config.js @grafana/frontend-ops
|
||||
/jest.config.cjs @grafana/frontend-ops
|
||||
/latest.json @grafana/frontend-ops
|
||||
/stylelint.config.js @grafana/frontend-ops
|
||||
/tools/ @grafana/frontend-ops
|
||||
/lefthook.yml @grafana/frontend-ops
|
||||
/lefthook.rc @grafana/frontend-ops
|
||||
/.husky/pre-commit @grafana/frontend-ops
|
||||
/cypress.config.js @grafana/grafana-frontend-platform
|
||||
/cypress.config.cjs @grafana/grafana-frontend-platform
|
||||
/.levignore.js @grafana/plugins-platform-frontend
|
||||
playwright.config.ts @grafana/plugins-platform-frontend
|
||||
e2e/plugin-e2e/playwright.config.ts @grafana/plugins-platform-frontend
|
||||
|
||||
# public folder
|
||||
/public/app/api/ @grafana/grafana-frontend-platform
|
||||
@@ -603,6 +604,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/public/app/dev.ts @grafana/frontend-ops
|
||||
/public/app/core/utils/metrics.ts @grafana/plugins-platform-frontend
|
||||
/public/app/index.ts @grafana/frontend-ops
|
||||
/public/app/viteGlobals.ts @grafana/frontend-ops
|
||||
/public/app/AppWrapper.tsx @grafana/frontend-ops
|
||||
/public/app/partials/ @grafana/grafana-frontend-platform
|
||||
|
||||
@@ -620,7 +622,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/scripts/import_many_dashboards.sh @torkelo
|
||||
/scripts/mixin-check.sh @bergquist
|
||||
/scripts/openapi3/ @grafana/grafana-operator-experience-squad
|
||||
/scripts/prepare-packagejson.js @grafana/frontend-ops
|
||||
/scripts/protobuf-check.sh @grafana/plugins-platform-backend
|
||||
/scripts/stripnulls.sh @grafana/grafana-as-code
|
||||
/scripts/tag_release.sh @grafana/grafana-developer-enablement-squad
|
||||
@@ -642,7 +643,8 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
.pa11yci.conf.js @grafana/grafana-frontend-platform
|
||||
.pa11yci-pr.conf.js @grafana/grafana-frontend-platform
|
||||
.betterer.results @grafanabot
|
||||
.betterer.ts @grafana/grafana-frontend-platform
|
||||
.betterer.cjs @grafana/grafana-frontend-platform
|
||||
vite.config.ts @grafana/frontend-ops
|
||||
|
||||
# Design system
|
||||
/public/img/icons/unicons/ @grafana/design-system
|
||||
|
||||
@@ -202,7 +202,7 @@ jobs:
|
||||
with:
|
||||
script: |
|
||||
const filePath = 'result.json';
|
||||
const script = require('./.github/workflows/scripts/json-file-to-job-output.js');
|
||||
const { default: script } = await import ('${{ github.workspace }}/.github/workflows/scripts/json-file-to-job-output.js')
|
||||
await script({ core, filePath });
|
||||
|
||||
# Check if label exists
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
module.exports = async ({ core, filePath }) => {
|
||||
try {
|
||||
const fs = require('fs').promises;
|
||||
const content = await fs.readFile(filePath)
|
||||
const result = JSON.parse(content);
|
||||
|
||||
core.startGroup('Parsing json file...');
|
||||
const jsonToJobOutput = async ({ core, filePath }) => {
|
||||
try {
|
||||
const fs = await import('fs/promises');
|
||||
const content = await fs.readFile(filePath)
|
||||
const result = JSON.parse(content);
|
||||
|
||||
for (const property in result) {
|
||||
core.info(`${property} <- ${result[property]}`);
|
||||
core.setOutput(property, result[property]);
|
||||
}
|
||||
core.startGroup('Parsing json file...');
|
||||
|
||||
core.endGroup();
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
}
|
||||
for (const property in result) {
|
||||
core.info(`${property} <- ${result[property]}`);
|
||||
core.setOutput(property, result[property]);
|
||||
}
|
||||
|
||||
core.endGroup();
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
export default jsonToJobOutput
|
||||
|
||||
15
.gitignore
vendored
15
.gitignore
vendored
@@ -7,6 +7,7 @@ awsconfig
|
||||
/.awcache
|
||||
/dist
|
||||
/public/build
|
||||
/public/build_tmp
|
||||
/emails/dist
|
||||
/reports
|
||||
/e2e/tmp
|
||||
@@ -179,11 +180,12 @@ compilation-stats.json
|
||||
/e2e/extensions
|
||||
!/e2e/extensions/.keep
|
||||
/e2e/extensions-suite
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
/playwright/.auth/
|
||||
test-results/
|
||||
playwright-report/
|
||||
blob-report/
|
||||
playwright/.cache/
|
||||
playwright/.auth/
|
||||
|
||||
# grafana server
|
||||
/scripts/grafana-server/server.log
|
||||
|
||||
@@ -233,3 +235,6 @@ public/app/plugins/**/dist/
|
||||
public/mockServiceWorker.js
|
||||
|
||||
/e2e/test-plugins/*/dist
|
||||
|
||||
# vite
|
||||
stats.html
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
module.exports = {
|
||||
const config = {
|
||||
trailingComma: 'es5',
|
||||
singleQuote: true,
|
||||
printWidth: 120,
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -89,6 +89,9 @@ cdn_url =
|
||||
# `0` means there is no timeout for reading the request.
|
||||
read_timeout = 0
|
||||
|
||||
# Tell the backend server where to load frontend assets from when developing Grafana.
|
||||
frontend_dev_server =
|
||||
|
||||
# This setting enables you to specify additional headers that the server adds to HTTP(S) responses.
|
||||
[server.custom_response_headers]
|
||||
#exampleHeader1 = exampleValue1
|
||||
|
||||
@@ -92,6 +92,9 @@
|
||||
# `0` means there is no timeout for reading the request.
|
||||
;read_timeout = 0
|
||||
|
||||
# Tell the backend server where to load frontend assets from when developing Grafana.
|
||||
;frontend_dev_server =
|
||||
|
||||
# This setting enables you to specify additional headers that the server adds to HTTP(S) responses.
|
||||
[server.custom_response_headers]
|
||||
#exampleHeader1 = exampleValue1
|
||||
|
||||
@@ -2,10 +2,10 @@ const { defineConfig } = require('cypress');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const benchmarkPlugin = require('./e2e/cypress/plugins/benchmark/index');
|
||||
const readProvisions = require('./e2e/cypress/plugins/readProvisions');
|
||||
const smtpTester = require('./e2e/cypress/plugins/smtpTester');
|
||||
const typescriptPreprocessor = require('./e2e/cypress/plugins/typescriptPreprocessor');
|
||||
const benchmarkPlugin = require('./e2e/cypress/plugins/benchmark/index.cjs');
|
||||
const readProvisions = require('./e2e/cypress/plugins/readProvisions.cjs');
|
||||
const smtpTester = require('./e2e/cypress/plugins/smtpTester.cjs');
|
||||
const typescriptPreprocessor = require('./e2e/cypress/plugins/typescriptPreprocessor.cjs');
|
||||
|
||||
module.exports = defineConfig({
|
||||
projectId: 'zb7k1c',
|
||||
@@ -1,8 +1,8 @@
|
||||
const fs = require('fs');
|
||||
const { fromPairs } = require('lodash');
|
||||
|
||||
const { CDPDataCollector } = require('./CDPDataCollector');
|
||||
const { formatResults } = require('./formatting');
|
||||
const { CDPDataCollector } = require('./CDPDataCollector.cjs');
|
||||
const { formatResults } = require('./formatting.cjs');
|
||||
|
||||
const remoteDebuggingPortOptionPrefix = '--remote-debugging-port=';
|
||||
|
||||
@@ -2,10 +2,10 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const benchmarkPlugin = require('./benchmark');
|
||||
const extendConfig = require('./extendConfig');
|
||||
const readProvisions = require('./readProvisions');
|
||||
const smtpTester = require('./smtpTester');
|
||||
const typescriptPreprocessor = require('./typescriptPreprocessor');
|
||||
const extendConfig = require('./extendConfig.cjs');
|
||||
const readProvisions = require('./readProvisions.cjs');
|
||||
const smtpTester = require('./smtpTester.cjs');
|
||||
const typescriptPreprocessor = require('./typescriptPreprocessor.cjs');
|
||||
|
||||
module.exports = (on, config) => {
|
||||
if (config.env['BENCHMARK_PLUGIN_ENABLED'] === true) {
|
||||
@@ -1,4 +1,4 @@
|
||||
require('./commands');
|
||||
import './commands.js';
|
||||
|
||||
Cypress.Screenshot.defaults({
|
||||
screenshotOnRunFailure: false,
|
||||
|
||||
5
e2e/plugin-e2e/package.json
Normal file
5
e2e/plugin-e2e/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "grafana-e2e-playwright-tests",
|
||||
"description": "This package.json exists to override the 'type': 'module' setting in root package.json",
|
||||
"type": "commonjs"
|
||||
}
|
||||
@@ -3,15 +3,13 @@ import path, { dirname } from 'path';
|
||||
|
||||
import { PluginOptions } from '@grafana/plugin-e2e';
|
||||
|
||||
const testDirRoot = 'e2e/plugin-e2e/';
|
||||
|
||||
export default defineConfig<PluginOptions>({
|
||||
fullyParallel: true,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
reporter: 'html',
|
||||
reporter: [['html', { outputFolder: '../../playwright-report' }]],
|
||||
use: {
|
||||
baseURL: `http://${process.env.HOST || 'localhost'}:${process.env.PORT || 3000}`,
|
||||
trace: 'retain-on-failure',
|
||||
@@ -44,7 +42,7 @@ export default defineConfig<PluginOptions>({
|
||||
// Run all tests in parallel using user with admin role
|
||||
{
|
||||
name: 'admin',
|
||||
testDir: path.join(testDirRoot, '/plugin-e2e-api-tests/as-admin-user'),
|
||||
testDir: './plugin-e2e-api-tests/as-admin-user',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -54,7 +52,7 @@ export default defineConfig<PluginOptions>({
|
||||
// Run all tests in parallel using user with viewer role
|
||||
{
|
||||
name: 'viewer',
|
||||
testDir: path.join(testDirRoot, '/plugin-e2e-api-tests/as-viewer-user'),
|
||||
testDir: './plugin-e2e-api-tests/as-viewer-user',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/viewer.json',
|
||||
@@ -63,7 +61,7 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
{
|
||||
name: 'elasticsearch',
|
||||
testDir: path.join(testDirRoot, '/elasticsearch'),
|
||||
testDir: './elasticsearch',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -72,7 +70,7 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
{
|
||||
name: 'mysql',
|
||||
testDir: path.join(testDirRoot, '/mysql'),
|
||||
testDir: './mysql',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -81,7 +79,7 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
{
|
||||
name: 'mssql',
|
||||
testDir: path.join(testDirRoot, '/mssql'),
|
||||
testDir: './mssql',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -108,7 +106,7 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
{
|
||||
name: 'cloudwatch',
|
||||
testDir: path.join(testDirRoot, '/cloudwatch'),
|
||||
testDir: './cloudwatch',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -117,7 +115,7 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
{
|
||||
name: 'azuremonitor',
|
||||
testDir: path.join(testDirRoot, '/azuremonitor'),
|
||||
testDir: 'e2e/test-plugins/azuremonitor',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -126,7 +124,7 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
{
|
||||
name: 'cloudmonitoring',
|
||||
testDir: path.join(testDirRoot, '/cloudmonitoring'),
|
||||
testDir: 'e2e/test-plugins/cloudmonitoring',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -135,7 +133,7 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
{
|
||||
name: 'graphite',
|
||||
testDir: path.join(testDirRoot, '/graphite'),
|
||||
testDir: 'e2e/test-plugins/graphite',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -144,7 +142,7 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
{
|
||||
name: 'influxdb',
|
||||
testDir: path.join(testDirRoot, '/influxdb'),
|
||||
testDir: 'e2e/test-plugins/influxdb',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -153,7 +151,7 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
{
|
||||
name: 'opentsdb',
|
||||
testDir: path.join(testDirRoot, '/opentsdb'),
|
||||
testDir: 'e2e/test-plugins/opentsdb',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -162,7 +160,7 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
{
|
||||
name: 'jaeger',
|
||||
testDir: path.join(testDirRoot, '/jaeger'),
|
||||
testDir: 'e2e/test-plugins/jaeger',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -171,7 +169,7 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
{
|
||||
name: 'grafana-postgresql-datasource',
|
||||
testDir: path.join(testDirRoot, '/grafana-postgresql-datasource'),
|
||||
testDir: 'e2e/test-plugins/grafana-postgresql-datasource',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -180,7 +178,7 @@ export default defineConfig<PluginOptions>({
|
||||
},
|
||||
{
|
||||
name: 'zipkin',
|
||||
testDir: path.join(testDirRoot, '/zipkin'),
|
||||
testDir: 'e2e/test-plugins/zipkin',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
storageState: 'playwright/.auth/admin.json',
|
||||
@@ -41,7 +41,7 @@ declare -A cypressConfig=(
|
||||
[viewportWidth]=1920
|
||||
[viewportHeight]=1080
|
||||
[trashAssetsBeforeRuns]=false
|
||||
[reporter]=./e2e/log-reporter.js
|
||||
[reporter]=./e2e/log-reporter.cjs
|
||||
[baseUrl]=${BASE_URL:-"http://$HOST:$PORT"}
|
||||
)
|
||||
|
||||
|
||||
@@ -13,11 +13,14 @@ const unicornPlugin = require('eslint-plugin-unicorn');
|
||||
const grafanaConfig = require('@grafana/eslint-config/flat');
|
||||
const grafanaPlugin = require('@grafana/eslint-plugin');
|
||||
|
||||
const bettererConfig = require('./.betterer.eslint.config');
|
||||
const getEnvConfig = require('./scripts/webpack/env-util');
|
||||
const bettererConfig = require('./scripts/betterer/.betterer.eslint.config.js');
|
||||
const getEnvConfig = require('./scripts/webpack/env-util.cjs');
|
||||
|
||||
const envConfig = getEnvConfig();
|
||||
const enableBettererRules = envConfig.frontend_dev_betterer_eslint_rules;
|
||||
/**
|
||||
* @type {Record<`frontend_dev_${string}`, unknown>}
|
||||
*/
|
||||
const frontendEnvConfig = getEnvConfig();
|
||||
const enableBettererRules = frontendEnvConfig.frontend_dev_betterer_eslint_rules;
|
||||
|
||||
/**
|
||||
* @type {Array<import('eslint').Linter.Config>}
|
||||
@@ -27,7 +27,10 @@ module.exports = {
|
||||
verbose: false,
|
||||
testEnvironment: 'jsdom',
|
||||
transform: {
|
||||
'^.+\\.(ts|tsx|js|jsx)$': [require.resolve('ts-jest'), { isolatedModules: true }],
|
||||
'^.+\\.(ts|tsx|js|jsx)$': [
|
||||
require.resolve('ts-jest'),
|
||||
{ tsconfig: '<rootDir>/tsconfig.test.json', isolatedModules: true },
|
||||
],
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
`/node_modules/(?!${esModules})`, // exclude es modules to prevent TS complaining
|
||||
@@ -38,7 +41,7 @@ module.exports = {
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
|
||||
setupFiles: ['jest-canvas-mock', './public/test/jest-setup.ts'],
|
||||
testTimeout: 30000,
|
||||
resolver: `<rootDir>/public/test/jest-resolver.js`,
|
||||
resolver: '<rootDir>/public/test/jest-resolver.cjs',
|
||||
setupFilesAfterEnv: ['./public/test/setupTests.ts'],
|
||||
globals: {
|
||||
__webpack_public_path__: '', // empty string
|
||||
@@ -58,6 +61,6 @@ module.exports = {
|
||||
'@bsull/augurs': '<rootDir>/public/test/mocks/augurs.ts',
|
||||
},
|
||||
// Log the test results with dynamic Loki tags. Drone CI only
|
||||
reporters: ['default', ['<rootDir>/public/test/log-reporter.js', { enable: process.env.DRONE === 'true' }]],
|
||||
reporters: ['default', ['<rootDir>/public/test/log-reporter.cjs', { enable: process.env.DRONE === 'true' }]],
|
||||
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
|
||||
};
|
||||
56
package.json
56
package.json
@@ -5,11 +5,12 @@
|
||||
"name": "grafana",
|
||||
"version": "11.6.0-pre",
|
||||
"repository": "github:grafana/grafana",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production nx exec --verbose -- webpack --config scripts/webpack/webpack.prod.js --progress",
|
||||
"build:nominify": "yarn run build -- --env noMinify=1",
|
||||
"build:stats": "NODE_ENV=production webpack --progress --config scripts/webpack/webpack.stats.js",
|
||||
"dev": "NODE_ENV=dev nx exec -- webpack --config scripts/webpack/webpack.dev.js",
|
||||
"build": "NODE_ENV=production nx exec --verbose -- vite build",
|
||||
"build:nominify": "NO_MINIFY=1 yarn run build",
|
||||
"build:stats": "ANALYZE_BUNDLE=1 yarn run build",
|
||||
"dev": "NODE_ENV=dev nx exec -- vite",
|
||||
"e2e": "./e2e/start-and-run-suite",
|
||||
"e2e:old-arch": "./e2e/start-and-run-suite old-arch",
|
||||
"e2e:schema-v2": "./e2e/start-and-run-suite dashboards-schema-v2",
|
||||
@@ -19,7 +20,7 @@
|
||||
"e2e:enterprise": "./e2e/start-and-run-suite enterprise",
|
||||
"e2e:enterprise:dev": "./e2e/start-and-run-suite enterprise dev",
|
||||
"e2e:enterprise:debug": "./e2e/start-and-run-suite enterprise debug",
|
||||
"e2e:playwright": "yarn playwright test",
|
||||
"e2e:playwright": "yarn playwright test -c ./e2e/plugin-e2e/playwright.config.ts",
|
||||
"e2e:playwright:server": "yarn e2e:plugin:build && ./e2e/plugin-e2e/start-and-run-suite",
|
||||
"e2e:storybook": "PORT=9001 ./e2e/run-suite storybook true",
|
||||
"e2e:plugin:build": "nx run-many -t build --projects='@test-plugins/*'",
|
||||
@@ -41,7 +42,7 @@
|
||||
"prettier:check": "prettier --check --list-different=false --log-level=warn \"**/*.{ts,tsx,scss,md,mdx,json,js}\"",
|
||||
"prettier:checkDocs": "prettier --check --list-different=false --log-level=warn \"docs/**/*.md\" \"*.md\" \"packages/**/*.{ts,tsx,scss,md,mdx,json}\"",
|
||||
"prettier:write": "prettier --list-different \"**/*.{js,ts,tsx,scss,md,mdx,json}\" --write",
|
||||
"start": "NODE_ENV=dev nx exec -- webpack --config scripts/webpack/webpack.dev.js --watch",
|
||||
"start": "NODE_ENV=development nx exec -- vite",
|
||||
"start:liveReload": "yarn start -- --env liveReload=1",
|
||||
"start:noTsCheck": "yarn start -- --env noTsCheck=1",
|
||||
"start:noLint": "yarn start -- --env noTsCheck=1 --env noLint=1",
|
||||
@@ -55,16 +56,16 @@
|
||||
"watch": "yarn start -d watch,start core:start --watchTheme",
|
||||
"ci:test-frontend": "yarn run test:ci",
|
||||
"i18n:stats": "node ./scripts/cli/reportI18nStats.mjs",
|
||||
"betterer": "betterer --tsconfig ./scripts/cli/tsconfig.json",
|
||||
"betterer": "betterer --config ./scripts/betterer/.betterer.ts --results ./scripts/betterer/.betterer.results --tsconfig ./scripts/cli/tsconfig.json",
|
||||
"betterer:stats": "ts-node --transpile-only --project ./scripts/cli/tsconfig.json ./scripts/cli/reportBettererStats.ts",
|
||||
"betterer:issues": "ts-node --transpile-only --project ./scripts/cli/tsconfig.json ./scripts/cli/generateBettererIssues.ts",
|
||||
"betterer:ci": "betterer ci --tsconfig ./scripts/cli/tsconfig.json",
|
||||
"betterer:ci": "betterer ci --config ./scripts/betterer/.betterer.ts --results ./scripts/betterer/.betterer.results --tsconfig ./scripts/cli/tsconfig.json",
|
||||
"plugin:build": "nx run-many -t build --projects='tag:scope:plugin'",
|
||||
"plugin:build:commit": "nx run-many -t build:commit --projects='tag:scope:plugin'",
|
||||
"plugin:build:dev": "nx run-many -t dev --projects='tag:scope:plugin' --maxParallel=100",
|
||||
"generate-icons": "nx run grafana-icons:generate",
|
||||
"process-specs": "node --experimental-strip-types scripts/process-specs.ts",
|
||||
"generate-apis": "yarn process-specs && rtk-query-codegen-openapi ./scripts/generate-rtk-apis.ts"
|
||||
"generate-apis": "yarn process-specs && rtk-query-codegen-openapi ./scripts/generate-rtk-apis.cts"
|
||||
},
|
||||
"grafana": {
|
||||
"whatsNewUrl": "https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-v%[1]s-%[2]s/",
|
||||
@@ -84,7 +85,6 @@
|
||||
"@grafana/tsconfig": "^2.0.0",
|
||||
"@manypkg/get-packages": "^2.2.0",
|
||||
"@playwright/test": "1.50.1",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "0.5.15",
|
||||
"@react-types/button": "3.10.2",
|
||||
"@react-types/menu": "3.9.14",
|
||||
"@react-types/overlays": "3.8.12",
|
||||
@@ -113,15 +113,16 @@
|
||||
"@types/eslint": "9.6.1",
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/file-saver": "2.0.7",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"@types/glob": "^8.0.0",
|
||||
"@types/google.analytics": "^0.0.46",
|
||||
"@types/gtag.js": "^0.0.20",
|
||||
"@types/history": "4.7.11",
|
||||
"@types/html-minifier-terser": "^7",
|
||||
"@types/ini": "^4",
|
||||
"@types/jest": "29.5.14",
|
||||
"@types/jquery": "3.5.32",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/jsurl": "^1.2.28",
|
||||
"@types/lodash": "4.17.15",
|
||||
"@types/logfmt": "^1.2.3",
|
||||
"@types/lucene": "^2",
|
||||
@@ -151,11 +152,10 @@
|
||||
"@types/systemjs": "6.15.1",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/uuid": "10.0.0",
|
||||
"@types/webpack-assets-manifest": "^5",
|
||||
"@types/webpack-env": "^1.18.4",
|
||||
"@types/yargs": "17.0.33",
|
||||
"@typescript-eslint/eslint-plugin": "8.22.0",
|
||||
"@typescript-eslint/parser": "8.22.0",
|
||||
"@vitejs/plugin-react-swc": "^3.7.2",
|
||||
"autoprefixer": "10.4.20",
|
||||
"babel-loader": "9.2.1",
|
||||
"blob-polyfill": "9.0.20240710",
|
||||
@@ -163,11 +163,8 @@
|
||||
"chance": "^1.0.10",
|
||||
"chrome-remote-interface": "0.33.2",
|
||||
"codeowners": "^5.1.1",
|
||||
"copy-webpack-plugin": "12.0.2",
|
||||
"core-js": "3.40.0",
|
||||
"crashme": "0.0.15",
|
||||
"css-loader": "7.1.2",
|
||||
"css-minimizer-webpack-plugin": "7.0.0",
|
||||
"cypress": "13.10.0",
|
||||
"cypress-file-upload": "5.0.8",
|
||||
"cypress-recurse": "^1.35.3",
|
||||
@@ -188,13 +185,10 @@
|
||||
"eslint-plugin-testing-library": "^7.0.0",
|
||||
"eslint-plugin-unicorn": "^56.0.0",
|
||||
"eslint-scope": "^8.1.0",
|
||||
"eslint-webpack-plugin": "4.2.0",
|
||||
"expose-loader": "5.0.0",
|
||||
"fishery": "^2.2.2",
|
||||
"fork-ts-checker-webpack-plugin": "9.0.2",
|
||||
"fs-extra": "11.2.0",
|
||||
"glob": "11.0.1",
|
||||
"html-loader": "5.1.0",
|
||||
"html-webpack-plugin": "5.6.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"http-server": "14.1.1",
|
||||
"i18next-parser": "9.3.0",
|
||||
"ini": "^5.0.0",
|
||||
@@ -209,45 +203,35 @@
|
||||
"jimp": "^1.6.0",
|
||||
"jsdom-testing-mocks": "^1.13.1",
|
||||
"lerna": "8.1.8",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"msw": "2.7.0",
|
||||
"mutationobserver-shim": "0.3.7",
|
||||
"ngtemplate-loader": "2.1.0",
|
||||
"node-notifier": "10.0.1",
|
||||
"nx": "19.8.2",
|
||||
"openapi-types": "^12.1.3",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"postcss": "8.5.1",
|
||||
"postcss-loader": "8.1.1",
|
||||
"postcss-reporter": "7.1.0",
|
||||
"postcss-scss": "4.0.9",
|
||||
"prettier": "3.4.2",
|
||||
"pseudoizer": "^0.1.0",
|
||||
"react-refresh": "0.14.0",
|
||||
"react-select-event": "5.5.1",
|
||||
"redux-mock-store": "1.5.5",
|
||||
"rimraf": "6.0.1",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"rudder-sdk-js": "2.48.44",
|
||||
"sass": "1.83.4",
|
||||
"sass-loader": "16.0.4",
|
||||
"smtp-tester": "^2.1.0",
|
||||
"style-loader": "4.0.0",
|
||||
"stylelint": "16.14.1",
|
||||
"stylelint-config-sass-guidelines": "12.1.0",
|
||||
"terser-webpack-plugin": "5.3.11",
|
||||
"testing-library-selector": "0.3.1",
|
||||
"tracelib": "1.0.1",
|
||||
"ts-jest": "29.2.5",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.7.3",
|
||||
"vite": "^6.0.3",
|
||||
"vite-plugin-environment": "^1.1.3",
|
||||
"webpack": "5.97.1",
|
||||
"webpack-assets-manifest": "^5.1.0",
|
||||
"webpack-cli": "6.0.1",
|
||||
"webpack-dev-server": "5.2.0",
|
||||
"webpack-livereload-plugin": "3.0.2",
|
||||
"webpack-manifest-plugin": "5.0.0",
|
||||
"webpack-merge": "6.0.1",
|
||||
"webpackbar": "^7.0.0",
|
||||
"webpack-cli": "5.1.4",
|
||||
"yaml": "^2.0.0",
|
||||
"yargs": "^17.5.1"
|
||||
},
|
||||
@@ -341,7 +325,6 @@
|
||||
"js-yaml": "^4.1.0",
|
||||
"json-markup": "^1.1.0",
|
||||
"json-source-map": "0.6.1",
|
||||
"jsurl": "^0.1.5",
|
||||
"kbar": "0.1.0-beta.45",
|
||||
"leven": "^4.0.0",
|
||||
"lodash": "4.17.21",
|
||||
@@ -410,7 +393,6 @@
|
||||
"symbol-observable": "4.0.0",
|
||||
"systemjs": "6.15.1",
|
||||
"systemjs-cjs-extra": "0.2.1",
|
||||
"tether-drop": "https://github.com/torkelo/drop",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tslib": "2.8.1",
|
||||
"tween-functions": "^1.2.0",
|
||||
|
||||
@@ -30,9 +30,7 @@
|
||||
"scripts": {
|
||||
"build": "tsc -p ./tsconfig.build.json && rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"clean": "rimraf ./dist ./compiled ./package.tgz",
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
|
||||
"prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js",
|
||||
"postpack": "mv package.json.bak package.json"
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "7.0.1",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Registry, RegistryItem } from '../utils/Registry';
|
||||
* @alpha
|
||||
*/
|
||||
export interface MonacoLanguageRegistryItem extends RegistryItem {
|
||||
init: () => Worker;
|
||||
init: () => string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { sanitizeUrl as braintreeSanitizeUrl } from '@braintree/sanitize-url';
|
||||
import DOMPurify from 'dompurify';
|
||||
import * as xss from 'xss';
|
||||
import { FilterXSS, type IWhiteList, escapeAttrValue, getDefaultCSSWhiteList, whiteList } from 'xss';
|
||||
|
||||
const XSSWL = Object.keys(xss.whiteList).reduce<xss.IWhiteList>((acc, element) => {
|
||||
acc[element] = xss.whiteList[element]?.concat(['class', 'style']);
|
||||
const XSSWL = Object.keys(whiteList).reduce<IWhiteList>((acc, element) => {
|
||||
acc[element] = whiteList[element]?.concat(['class', 'style']);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
@@ -11,12 +11,12 @@ const XSSWL = Object.keys(xss.whiteList).reduce<xss.IWhiteList>((acc, element) =
|
||||
// We don't allow the sandbox attribute, since it can be overridden, instead we add it below.
|
||||
XSSWL.iframe = ['src', 'width', 'height'];
|
||||
|
||||
const sanitizeTextPanelWhitelist = new xss.FilterXSS({
|
||||
const sanitizeTextPanelWhitelist = new FilterXSS({
|
||||
// Add sandbox attribute to iframe tags if an attribute is allowed.
|
||||
onTagAttr: function (tag, name, value, isWhiteAttr) {
|
||||
if (tag === 'iframe') {
|
||||
return isWhiteAttr
|
||||
? ` ${name}="${xss.escapeAttrValue(sanitizeUrl(value))}" sandbox credentialless referrerpolicy=no-referrer`
|
||||
? ` ${name}="${escapeAttrValue(sanitizeUrl(value))}" sandbox credentialless referrerpolicy=no-referrer`
|
||||
: '';
|
||||
}
|
||||
return;
|
||||
@@ -24,7 +24,7 @@ const sanitizeTextPanelWhitelist = new xss.FilterXSS({
|
||||
whiteList: XSSWL,
|
||||
css: {
|
||||
whiteList: {
|
||||
...xss.getDefaultCSSWhiteList(),
|
||||
...getDefaultCSSWhiteList(),
|
||||
'flex-direction': true,
|
||||
'flex-wrap': true,
|
||||
'flex-basis': true,
|
||||
|
||||
@@ -34,9 +34,7 @@
|
||||
"build": "tsc -p ./tsconfig.build.json && rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"bundle": "rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"clean": "rimraf ./dist ./compiled ./package.tgz",
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
|
||||
"prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js",
|
||||
"postpack": "mv package.json.bak package.json"
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "16.0.0",
|
||||
|
||||
@@ -34,9 +34,7 @@
|
||||
"build": "tsc -p ./tsconfig.build.json && rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"bundle": "rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"clean": "rimraf ./dist ./compiled ./package.tgz",
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
|
||||
"prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js",
|
||||
"postpack": "mv package.json.bak package.json"
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults",
|
||||
|
||||
@@ -28,9 +28,10 @@
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf ./dist ./compiled ./package.tgz ./src/icons-gen",
|
||||
"generate": "yarn clean && npx @svgr/cli ./svg --silent && mv ./src/icons-gen/index.ts ./src",
|
||||
"typecheck": "yarn generate && tsc --emitDeclarationOnly false --noEmit",
|
||||
"generate": "yarn clean && npx @svgr/cli ./svg --silent --no-prettier && mv ./src/icons-gen/index.ts ./src && yarn prettier:write",
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
|
||||
"lint": "eslint --ext .ts,.tsx ./src",
|
||||
"prettier:write": "prettier --list-different \"**/*.{js,ts,tsx}\" --write",
|
||||
"prettier:check": "prettier --check --list-different=false --log-level=warn \"**/*.{ts,tsx,scss,md,mdx,json}\"",
|
||||
"build": "yarn generate && rollup -c rollup.config.ts --configPlugin esbuild"
|
||||
},
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"jsx": "react-jsx",
|
||||
"alwaysStrict": true,
|
||||
"declaration": false,
|
||||
"resolveJsonModule": true
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"ts-node": {
|
||||
"compilerOptions": {
|
||||
|
||||
@@ -227,6 +227,8 @@ const config = async (env: Record<string, unknown>): Promise<Configuration> => {
|
||||
|
||||
stats: 'minimal',
|
||||
|
||||
target: 'web',
|
||||
|
||||
watchOptions: {
|
||||
ignored: ['**/node_modules', '**/dist', '**/.yarn'],
|
||||
},
|
||||
|
||||
@@ -31,9 +31,7 @@
|
||||
"build": "tsc -p ./tsconfig.build.json && rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"bundle": "rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"clean": "rimraf ./dist ./compiled ./package.tgz",
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
|
||||
"prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js",
|
||||
"postpack": "mv package.json.bak package.json"
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.13.5",
|
||||
|
||||
@@ -32,9 +32,7 @@
|
||||
"build": "tsc -p ./tsconfig.build.json && rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"bundle": "rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"clean": "rimraf ./dist ./compiled ./package.tgz",
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
|
||||
"prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js",
|
||||
"postpack": "mv package.json.bak package.json"
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/data": "11.6.0-pre",
|
||||
|
||||
@@ -31,9 +31,7 @@
|
||||
"build": "tsc -p ./tsconfig.build.json && rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"bundle": "rollup -c rollup.config.ts --configPlugin esbuild",
|
||||
"clean": "rimraf ./dist ./compiled ./package.tgz",
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
|
||||
"prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js",
|
||||
"postpack": "mv package.json.bak package.json"
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@grafana/tsconfig": "^2.0.0",
|
||||
|
||||
@@ -37,9 +37,7 @@
|
||||
"clean": "rimraf ./dist ./compiled ./package.tgz",
|
||||
"storybook": "storybook dev -p 9001 -c .storybook --no-open",
|
||||
"storybook:build": "storybook build -o ./dist/storybook -c .storybook",
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
|
||||
"prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js",
|
||||
"postpack": "mv package.json.bak package.json"
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults",
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import $ from 'jquery';
|
||||
|
||||
import { ThresholdsMode, FieldConfig, FieldColorModeId, createTheme } from '@grafana/data';
|
||||
|
||||
import { Gauge, Props } from './Gauge';
|
||||
|
||||
jest.mock('jquery', () => ({
|
||||
plot: jest.fn(),
|
||||
}));
|
||||
|
||||
const field: FieldConfig = {
|
||||
min: 0,
|
||||
max: 100,
|
||||
@@ -38,6 +35,9 @@ const props: Props = {
|
||||
};
|
||||
|
||||
describe('Gauge', () => {
|
||||
// @ts-ignore - mock jquery plot to prevent console.errors
|
||||
$.plot = jest.fn();
|
||||
|
||||
it('should render without blowing up', () => {
|
||||
expect(() => render(<Gauge {...props} />)).not.toThrow();
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { PureComponent } from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export interface LayoutItemContextProps {
|
||||
boostZIndex(): () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an API for downstream components (e.g. within panels) to inform the layout
|
||||
* that anchored tooltips or context menus could overflow the panel bounds. The layout
|
||||
* system can then boost the z-index of items with any anchored contents to prevent the overflown
|
||||
* content from rendering underneath adjacent layout items (e.g. other panels) that naturally
|
||||
* render later/higher in the stacking order
|
||||
*
|
||||
* This is used by VizTooltips and Annotations, which anchor to data points or time range within
|
||||
* the viz drawing area
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const LayoutItemContext = createContext<LayoutItemContextProps>({
|
||||
boostZIndex: () => () => {},
|
||||
});
|
||||
@@ -35,7 +35,7 @@ export interface State {
|
||||
|
||||
export class Typeahead extends PureComponent<Props, State> {
|
||||
static contextType = ThemeContext;
|
||||
context!: React.ContextType<typeof ThemeContext>;
|
||||
declare context: React.ContextType<typeof ThemeContext>;
|
||||
listRef = createRef<FixedSizeList>();
|
||||
|
||||
state: State = {
|
||||
|
||||
@@ -284,6 +284,8 @@ export { Divider } from './Divider/Divider';
|
||||
export { getDragStyles, type DragHandlePosition } from './DragHandle/DragHandle';
|
||||
export { useSplitter } from './Splitter/useSplitter';
|
||||
|
||||
export { LayoutItemContext, type LayoutItemContextProps } from './Layout/LayoutItemContext';
|
||||
|
||||
/** @deprecated Please use non-legacy versions of these components */
|
||||
const LegacyForms = {
|
||||
SecretFormField,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Libraries
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { uniqBy } from 'lodash';
|
||||
import { PureComponent } from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
@@ -23,6 +23,7 @@ type IndexViewData struct {
|
||||
NewGrafanaVersion string
|
||||
AppName string
|
||||
AppNameBodyClass string
|
||||
FrontendDevServer bool
|
||||
FavIcon template.URL
|
||||
AppleTouchIcon template.URL
|
||||
AppTitle string
|
||||
@@ -39,11 +40,12 @@ type IndexViewData struct {
|
||||
type EntryPointAssets struct {
|
||||
ContentDeliveryURL string `json:"cdn,omitempty"`
|
||||
JSFiles []EntryPointAsset `json:"jsFiles"`
|
||||
CSSFiles []EntryPointAsset `json:"cssFiles"`
|
||||
Dark string `json:"dark"`
|
||||
Light string `json:"light"`
|
||||
Swagger []EntryPointAsset `json:"swagger"`
|
||||
SwaggerCSSFiles []EntryPointAsset `json:"swaggerCssFiles"`
|
||||
PreloadJSFiles []EntryPointAsset `json:"preloadJsFiles"`
|
||||
// CSSFiles []EntryPointAsset `json:"cssFiles"`
|
||||
Dark string `json:"dark"`
|
||||
Light string `json:"light"`
|
||||
// Swagger []EntryPointAsset `json:"swagger"`
|
||||
// SwaggerCSSFiles []EntryPointAsset `json:"swaggerCssFiles"`
|
||||
}
|
||||
|
||||
type EntryPointAsset struct {
|
||||
@@ -61,13 +63,16 @@ func (a *EntryPointAssets) SetContentDeliveryURL(prefix string) {
|
||||
for i, p := range a.JSFiles {
|
||||
a.JSFiles[i].FilePath = prefix + p.FilePath
|
||||
}
|
||||
for i, p := range a.CSSFiles {
|
||||
a.CSSFiles[i].FilePath = prefix + p.FilePath
|
||||
}
|
||||
for i, p := range a.Swagger {
|
||||
a.Swagger[i].FilePath = prefix + p.FilePath
|
||||
}
|
||||
for i, p := range a.SwaggerCSSFiles {
|
||||
a.SwaggerCSSFiles[i].FilePath = prefix + p.FilePath
|
||||
for i, p := range a.PreloadJSFiles {
|
||||
a.PreloadJSFiles[i].FilePath = prefix + p.FilePath
|
||||
}
|
||||
// for i, p := range a.CSSFiles {
|
||||
// a.CSSFiles[i].FilePath = prefix + p.FilePath
|
||||
// }
|
||||
// for i, p := range a.Swagger {
|
||||
// a.Swagger[i].FilePath = prefix + p.FilePath
|
||||
// }
|
||||
// for i, p := range a.SwaggerCSSFiles {
|
||||
// a.SwaggerCSSFiles[i].FilePath = prefix + p.FilePath
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV
|
||||
ThemeType: theme.Type,
|
||||
AppUrl: appURL,
|
||||
AppSubUrl: appSubURL,
|
||||
FrontendDevServer: hs.Cfg.FrontendDevServer,
|
||||
NewsFeedEnabled: hs.Cfg.NewsFeedEnabled,
|
||||
GoogleAnalyticsId: settings.GoogleAnalyticsId,
|
||||
GoogleAnalytics4Id: settings.GoogleAnalytics4Id,
|
||||
|
||||
@@ -1,135 +1,195 @@
|
||||
{
|
||||
"AdminAuthentication.js": {
|
||||
"src": "public/build/AdminAuthentication.abcc504db867d1fa3469.js",
|
||||
"integrity": "sha256-GtJEBbWfWAU2h2hoW+OiHUHNhHuKDzJ6mBEsVKyRc6M= sha384-SfBCROVbdjMZbxWZzYcYq1Ur4qVOmMgCQwRj9rc23op01045mOoRnCSwnENSNnhY sha512-RwKiHjN7M4Wf5BtO0nKFMoZTp0mCPVttkg4SU76+93VVdf8XEnTIpM6Trd+v4GX37wNrWEd34lh/Xaidq5t33g=="
|
||||
"_vendor-!~{004}~.js": {
|
||||
"file": "public/build/vendor-BrUJaDlA.css",
|
||||
"src": "_vendor-!~{004}~.js"
|
||||
},
|
||||
"AdminEditOrgPage.js": {
|
||||
"src": "public/build/AdminEditOrgPage.960dc1174d73f4fd46c3.js",
|
||||
"integrity": "sha256-y9TYWD3xHk5MQiBpvSpKvFgtp7yHovE0hfrUppxsQuw= sha384-kCrsPv6DmRzbRW/ajBaGu5X67nNRRAuOaZZYONCm8YA6KQclrEHG8navPSsMROnk sha512-2oE8w7f0s/vo6SOgDteuYSvuvAAjqMFjPpD4t7b11sah4B007L9dIVs9MF3jTSKsSHlG61hK6L2QCgv+9FtrFg=="
|
||||
"_vendor-r65R1Ntf.js": {
|
||||
"file": "public/build/vendor-r65R1Ntf.js",
|
||||
"name": "vendor",
|
||||
"isDynamicEntry": true,
|
||||
"dynamicImports": [
|
||||
"../packages/grafana-ui/src/components/Monaco/ReactMonacoEditor.tsx",
|
||||
"../node_modules/monaco-promql/promql/promql.js"
|
||||
],
|
||||
"css": [
|
||||
"public/build/vendor-BrUJaDlA.css"
|
||||
],
|
||||
"assets": [
|
||||
"public/build/grot-cta-B42JPuvP.svg",
|
||||
"public/build/grot-not-found-Bjhtecdw.svg",
|
||||
"public/build/grot-completed-D-h3ZMGB.svg"
|
||||
]
|
||||
},
|
||||
"AdminFeatureTogglesPage.js": {
|
||||
"src": "public/build/AdminFeatureTogglesPage.999db7797f1efddde074.js",
|
||||
"integrity": "sha256-fhZz2BcvirKLQlxqcEprDplsSRAWYu+FiZWMRtFig1g= sha384-V2e+1wmlsKjorbNlJNzDfizXd1E4q8VpqaIX8S8FkumdZwFPPCXMsnHd77rjFKks sha512-LSiPxyiP0tmZ7fS/QOQBnC98pqgLDSATU0V9He5ubNEgziolxd/XmqoSDTGtp1WWZfjDxW6SUoKVmwstc0VzxA=="
|
||||
"_braintree-CW7sY1ng.js": {
|
||||
"file": "public/build/braintree-CW7sY1ng.js",
|
||||
"name": "braintree"
|
||||
},
|
||||
"AdminListOrgsPage.js": {
|
||||
"src": "public/build/AdminListOrgsPage.c573cf876050fc991990.js",
|
||||
"integrity": "sha256-RSxk6cc52+j9cITdwi4k5lgzER+sKF7Mx5pn6atMYdE= sha384-xE53wdhi0O9e9A+9NKHGlvJgEeSew6ooXYl6oS8e5Qq9slP2TQygvO48/3CCYWJM sha512-YfXZHm9jyZbHAzcAimOv96ZJWEpQrzjZPxxvyVZZVPfB/8Z1FL7rhKhk0HGIhufXTTLhobZp/9CkFKT4no7zTA=="
|
||||
"_viridis--y8fz-dE.js": {
|
||||
"file": "public/build/viridis--y8fz-dE.js",
|
||||
"name": "viridis",
|
||||
"imports": [
|
||||
"_linear-D23a9ZoO.js",
|
||||
"_vendor-r65R1Ntf.js"
|
||||
]
|
||||
},
|
||||
"AdminSettings.js": {
|
||||
"src": "public/build/AdminSettings.cfdfc2e959a29968d661.js",
|
||||
"integrity": "sha256-v/5L4MXpTnSZXjApozisGbt+y+DPfpjWhH3D6bY83zE= sha384-FTIo/3tLgPKqgxwpxXSa8SLoU5hz4HJcgrVgXD2g06/GUnBFLrJcN3lYqYrvsgLu sha512-kBBoMB2XBtXM/iWW0zAFdI9O3ggetQZdpj6LhsiPDLy9pwRhwPQlnwZjlFkJMMEX0YK2uYl3dn9DUUyTKjtVMg=="
|
||||
"_vis-B4F-Fj9n.js": {
|
||||
"file": "public/build/vis-B4F-Fj9n.js",
|
||||
"name": "vis",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_vendor-r65R1Ntf.js"
|
||||
]
|
||||
},
|
||||
"AlertAmRoutes.js": {
|
||||
"src": "public/build/AlertAmRoutes.bd900447d272cba91413.js",
|
||||
"integrity": "sha256-K6hY1ruuidLNg5aYV4XZVBpTFCcS7T2q4b1mF9lPOf4= sha384-SFNQUdxgwLjHW/JsukI7Juhs6f2+qdxMCNbsD4ix3Ha44HbiXTKPBTmvxMl4r+CS sha512-eSrh2wrOXlLp9bXd+0zsZS4v8xjJTufSi0Kfgx0FSB8WhnjhOnIOX37wszsV00w1AwyO6VE6yN3q2PGTrpUoeg=="
|
||||
"app/features/teams/TeamList.tsx": {
|
||||
"file": "public/build/TeamList-DSwjs7Wa.js",
|
||||
"name": "TeamList",
|
||||
"src": "app/features/teams/TeamList.tsx",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_vendor-r65R1Ntf.js",
|
||||
"app/index.ts",
|
||||
"_api-y46G0_KO.js",
|
||||
"_TeamRolePicker-Yl7zWxKI.js",
|
||||
"_actions-DdmCGKOg.js",
|
||||
"_ProBadge-DkW08ySd.js",
|
||||
"_utils-CQ7yl5br.js"
|
||||
]
|
||||
},
|
||||
"AlertGroups.js": {
|
||||
"src": "public/build/AlertGroups.d04aee7a0d92a3726e5c.js",
|
||||
"integrity": "sha256-GrsAPOHbrj3qJx97ugYdqF7fEtD1KU+VGZC9EnCzXhM= sha384-WjJwpX218WLCpDgRSkYbHvwjFy0ttlIb9fYpG0pLDgRu9iYQKBZXaYMU9PDKB4Iu sha512-hpE6YahXnP4RSOJ3Xd+oS55gmtDWHDnMrDNr2UOXpWbLuQQJu8e8uRAW5cIBl8u2S63F1SC1vGx6d535hECq2A=="
|
||||
"app/features/teams/TeamPages.tsx": {
|
||||
"file": "public/build/TeamPages-1zHLHq7V.js",
|
||||
"name": "TeamPages",
|
||||
"src": "app/features/teams/TeamPages.tsx",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_vendor-r65R1Ntf.js",
|
||||
"app/index.ts",
|
||||
"_EmptyListCTA-C7ObQVwC.js",
|
||||
"_utils-CQ7yl5br.js",
|
||||
"_actions-DdmCGKOg.js",
|
||||
"_TeamRolePicker-Yl7zWxKI.js",
|
||||
"_hooks-t-SNFABd.js",
|
||||
"_SharedPreferences-flYo2s4z.js",
|
||||
"_ProBadge-DkW08ySd.js",
|
||||
"_api-y46G0_KO.js"
|
||||
]
|
||||
},
|
||||
"AlertRuleListIndex.js": {
|
||||
"src": "public/build/AlertRuleListIndex.4682f9ab8dd5a8e68722.js",
|
||||
"integrity": "sha256-xlMu10aTkvXAw8hmtsaEZXTrAqQjaglNkwS6GLRISnU= sha384-hTb2gf6p1Q/M7fULhnDzGOlVjuYjnrVAJCrQ+yNNr0T0EWZkJ+yxrjNh9Q7/9F+G sha512-8xMEU7qfa+rlm62FHWYFb4JNN3E0WNqbhjVdNA2Awq+ZbhSHuzb4XPnmDJoOzakZoG1s+ZgK7GL1wSXBrKbPkw=="
|
||||
"app/features/trails/DataTrailsPage.tsx": {
|
||||
"file": "public/build/DataTrailsPage-CVUXQGkO.js",
|
||||
"name": "DataTrailsPage",
|
||||
"src": "app/features/trails/DataTrailsPage.tsx",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_vendor-r65R1Ntf.js",
|
||||
"app/index.ts"
|
||||
]
|
||||
},
|
||||
"AlertSilences.js": {
|
||||
"src": "public/build/AlertSilences.84de2bedd3634a40f4a4.js",
|
||||
"integrity": "sha256-LamykmTC703pkL5q7hPdOp9zTQWn6ZsMKzRv7v35ubU= sha384-VWDeB7KZFOYL0RWBBXcmvzKds+Yxn2cDU8fSo4Srg165GY29s8+jO6sEG6OY9zj9 sha512-UvlIwoREIwdwkRWdut8+p+GGRgWTyAijW3pNQ1lQRUE8wGNYqBuHa1lGK6Jq4mmhkp1Pqo4/iI4Uk7vrbYFKow=="
|
||||
"app/index.ts": {
|
||||
"file": "public/build/index-fkCrlmGK.js",
|
||||
"name": "index",
|
||||
"src": "app/index.ts",
|
||||
"isEntry": true,
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_vendor-r65R1Ntf.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"locales/de-DE/grafana.json",
|
||||
"locales/en-US/grafana.json",
|
||||
"locales/pseudo-LOCALE/grafana.json",
|
||||
"app/plugins/panel/flamegraph/module.tsx",
|
||||
"app/plugins/panel/geomap/module.tsx",
|
||||
"app/features/teams/TeamList.tsx",
|
||||
"app/features/teams/CreateTeam.tsx",
|
||||
"app/features/teams/TeamPages.tsx",
|
||||
"app/features/trails/DataTrailsPage.tsx",
|
||||
"_vendor-r65R1Ntf.js"
|
||||
],
|
||||
"css": [
|
||||
"public/build/index-DTTxS2na.css"
|
||||
],
|
||||
"assets": [
|
||||
"public/build/outlier_bg-hJPFLdCg.wasm"
|
||||
]
|
||||
},
|
||||
"app.css": {
|
||||
"src": "public/build/grafana.app.91aaa9d81398c147a57c.css",
|
||||
"integrity": "sha256-77rfikk+dYkH82TOmcmleVoDOHZQdhzVX9gDLcgPbtQ= sha384-IOTlZ1IvTVq5ekKLoaE3/SoZ12K1eExOAnSw9BzkgQ3+RcyQpb1S5hO2w//IIkRB sha512-0Ct3uJBFQIkyxYTvMxseA1cphe2RivXQ2MCbiV0hEm5NzWPiY9sq2P4ay5dXz5v35c++4W47KaknoWlc83bQJQ=="
|
||||
"app/core/trustedTypePolicies.ts": {
|
||||
"file": "public/build/trustedTypePolicies-pOmZ7Wr2.js",
|
||||
"name": "trustedTypePolicies",
|
||||
"src": "app/core/trustedTypePolicies.ts",
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"_braintree-CW7sY1ng.js"
|
||||
]
|
||||
},
|
||||
"app.js": {
|
||||
"src": "public/build/app.js",
|
||||
"integrity": "sha256-IOZKp3piC3vddDXP5jy5rIw0vb0KKEOg/k9EGrIxskk= sha384-CBNr5W0pJ23LQMnz5BZI1iVBIExOmF/wpqkEnBtYu9R/yYJIzjpv8KT0a3TBulOi sha512-ockzlzgosuZvLittZrSzh8lexEIZF9iKpy6J9Ii4es3e4D34FpWhHJhDZGpxLVraX4ypLofrDp2Yy0sdUbdi7w=="
|
||||
"app/plugins/panel/flamegraph/module.tsx": {
|
||||
"file": "public/build/module-J53A9Kyn.js",
|
||||
"name": "module",
|
||||
"src": "app/plugins/panel/flamegraph/module.tsx",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_vendor-r65R1Ntf.js",
|
||||
"_FlameGraphContainer-DF3CoxWe.js",
|
||||
"app/index.ts",
|
||||
"_transform-Bj7g0BsI.js",
|
||||
"_linear-D23a9ZoO.js",
|
||||
"_useDebounce-Cf1ms9C9.js"
|
||||
]
|
||||
},
|
||||
"dark.css": {
|
||||
"src": "public/build/grafana.dark.722d809dba5a31f57d49.css",
|
||||
"integrity": "sha256-kyPBeKRFdG+i4aLA6vZ0nIkSD25YTHjcCfQ3e2+M6AA= sha384-o2yYT3suDP1EtvTj4UzLsSS+AAlv0F+n5YVaxDePRM6LSGAEPTxFOfxB3myAqD/x sha512-H3Eos9Ff7d+tB7e+6RORDXY49LjNMa8X3ZxiFn8T/OlHUFRnsfXsvj65p9cMqaRom/4qqx0JtmhtnuBoeaysrg=="
|
||||
"app/plugins/panel/geomap/module.tsx": {
|
||||
"file": "public/build/module-0YAl9hqA.js",
|
||||
"name": "module",
|
||||
"src": "app/plugins/panel/geomap/module.tsx",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_vendor-r65R1Ntf.js",
|
||||
"_ExemplarHoverView-BHzhrBsl.js",
|
||||
"app/index.ts",
|
||||
"_DataHoverView-v8a68bIA.js",
|
||||
"_ColorScale-CltZV5Bm.js",
|
||||
"_utils-GV5L4DHo.js",
|
||||
"_AddLayerButton-B7iJGNLs.js",
|
||||
"_utils-Dvc-eevq.js",
|
||||
"_LayerName-kazir38s.js",
|
||||
"_VizTooltipRow-B1eXWnxp.js",
|
||||
"_quadtree-BhY-3CT1.js"
|
||||
],
|
||||
"css": [
|
||||
"public/build/module-C4D2GwWP.css"
|
||||
]
|
||||
},
|
||||
"dark.js": {
|
||||
"src": "public/build/dark.js",
|
||||
"integrity": "sha256-ON8tNAimyYNoju+WIQP7ux1Aq/oYspSbqoOKvVkeEJY= sha384-figjklDEMn19kCLZ9aq9pUeMGVOC3nlFkQI0PT016cep5KvAd4nyrAl4WA1MALHX sha512-P1xsw+PNS2AVneN4UFfYhYiS3FeEIDPAj4VXAjiQtHqw7qLETqv2K0sYRogoXma23Q/b+h3VL+fw6qtwhJ8WCQ=="
|
||||
"fonts/fontawesome-webfont.woff2": {
|
||||
"file": "public/build/fontawesome-webfont-B-jkhYfk.woff2",
|
||||
"src": "fonts/fontawesome-webfont.woff2"
|
||||
},
|
||||
"default-packages_grafana-data_src_field_fieldComparers_ts-packages_grafana-data_src_transform-9dcf20.js": {
|
||||
"src": "public/build/default-packages_grafana-data_src_field_fieldComparers_ts-packages_grafana-data_src_transform-9dcf20.js",
|
||||
"integrity": "sha256-BfaH57oBHA9HC5GwpCNAREwyuEGsxbPR+eOEarSMqr0= sha384-elHEqjfd9Guir6jsvCIBsh/wCF4oJ6/wTFfgK9TsKyfyUJecmK3y1qlczBevMotL sha512-92Uk9x490/MViSclM7PjI6iQrM3FNRuiHGP5CWqNFjKX7PWLTcBirSs1xkoZ3tc0QG0m+klaJg6eNVEvpF25dQ=="
|
||||
"fonts/grafana-icons.woff": {
|
||||
"file": "public/build/grafana-icons-Dd-C-eHA.woff",
|
||||
"src": "fonts/grafana-icons.woff"
|
||||
},
|
||||
"default-packages_grafana-schema_src_raw_composable_barchart_panelcfg_x_BarChartPanelCfg_types-d3da31.js": {
|
||||
"src": "public/build/default-packages_grafana-schema_src_raw_composable_barchart_panelcfg_x_BarChartPanelCfg_types-d3da31.js",
|
||||
"integrity": "sha256-elDlrzSExWWpznC49+xQWQletnWQ1Fl6VaarANz2oLk= sha384-2UdE8X6NbOjt/i0WXpvew+eSBXKSTySfFdfxrIrALT0erSyvdbB9oASWPfRuTZyi sha512-so/nTAcSfmcReoZrJUusUbj6xxtGdrKiJ50n0CMZ1yqBh2hB7Y15s4POeX8axeJTNlo59wJ5GAbsdVTy4c0QhA=="
|
||||
"locales/de-DE/grafana.json": {
|
||||
"file": "public/build/grafana-BJ4JxwFS.js",
|
||||
"name": "grafana",
|
||||
"src": "locales/de-DE/grafana.json",
|
||||
"isDynamicEntry": true
|
||||
},
|
||||
"default-packages_grafana-ui_src_components_Layout_Stack_Stack_tsx-packages_grafana-ui_src_com-2a3620.js": {
|
||||
"src": "public/build/default-packages_grafana-ui_src_components_Layout_Stack_Stack_tsx-packages_grafana-ui_src_com-2a3620.js",
|
||||
"integrity": "sha256-+0bPuBGKFGglkXvW4oPiolrNveozRLZVLUrbCYsbVcM= sha384-EIayAgykdDWmyilAuXo4ad96v3tRqdWZp+BHdeDpSSsbAMeg+eoBBbk2Yh219kDg sha512-+jn7kmQ9Id8aTIe66TD+vM+W19cTIVexEfkxbxgqXdJyJ72qalN6ccWyP1ro1w/E1R/laZGNLz1LBc1I4u2Isw=="
|
||||
"locales/en-US/grafana.json": {
|
||||
"file": "public/build/grafana-DkCX6RXR.js",
|
||||
"name": "grafana",
|
||||
"src": "locales/en-US/grafana.json",
|
||||
"isDynamicEntry": true
|
||||
},
|
||||
"defaultVendors-node_modules_braintree_sanitize-url_dist_index_js-node_modules_emotion_css_dis-e9d917.js": {
|
||||
"src": "public/build/defaultVendors-node_modules_braintree_sanitize-url_dist_index_js-node_modules_emotion_css_dis-e9d917.js",
|
||||
"integrity": "sha256-6et8tLQscwAftNYIM4u8L42Xi+tFIlkPsKIlyATTuY4= sha384-9AKIelnSKqxpMktU6Fw1uE1Kz6ZuEtI2ExvQ1PTjwPmpAE41LS6WxQ3pSV6qlLyz sha512-zcqwJExa8qzqZ1cCIK/y+HrWhJxP6rgSC+SbqxRStQUZrU4S1DQa1pwe6eSoZ4DPht/8rSeIii1wnl3DyhdVVg=="
|
||||
"locales/pseudo-LOCALE/grafana.json": {
|
||||
"file": "public/build/grafana-BwPdh-XN.js",
|
||||
"name": "grafana",
|
||||
"src": "locales/pseudo-LOCALE/grafana.json",
|
||||
"isDynamicEntry": true
|
||||
},
|
||||
"defaultVendors-node_modules_fingerprintjs_fingerprintjs_dist_fp_esm_js-node_modules_grafana_e-45bb7f.js": {
|
||||
"src": "public/build/defaultVendors-node_modules_fingerprintjs_fingerprintjs_dist_fp_esm_js-node_modules_grafana_e-45bb7f.js",
|
||||
"integrity": "sha256-bVtkMWml/TQfwbkV9ueteGi+GhLh7ajxtVevi3gb4Aw= sha384-8+VsDObIkyeW90JyQELbiOsPKFJaBOmScMhBuWKmhtBmV9/7YvjIvqEPvpz1H5gP sha512-L3PzrsTEqfR+ruNzEaIqt0V0ZyX4V1fqbOe7aXWMIOr8JjJSJT1rMPVqJixy5DOL2f0zLDEc/DJvk4SN4BrDuw=="
|
||||
"sass/grafana.dark.scss": {
|
||||
"file": "public/build/grafana-B9F4fUOy.css",
|
||||
"src": "sass/grafana.dark.scss",
|
||||
"isEntry": true
|
||||
},
|
||||
"defaultVendors-node_modules_swagger-ui-react_index_mjs.js": {
|
||||
"src": "public/build/defaultVendors-node_modules_swagger-ui-react_index_mjs.js",
|
||||
"integrity": "sha256-7qtYNxawpAwQUd6KR+2IgH95hkr0w5hZLGFeubAUkcc= sha384-29fQncD6F/vaoKmk4Ccht9JbnrPo2BSyUdHSkTkF4RWEFkhgVEUo1TlSTDc3iOcd sha512-UhXtPd5nXyvyVR0vNYoO5wINJgb49oeYoqfnNaVQndg3n9uIvPi4lr3iUk/jkBsnVH1dEDWiZIErDPiTyHe4+w=="
|
||||
},
|
||||
"light.css": {
|
||||
"src": "public/build/grafana.light.2fbd901d840329c18394.css",
|
||||
"integrity": "sha256-OumSnRJ7qttDoFmbB3LMxVvrfpFFjABX5b1iZDcRMmk= sha384-Cq4wA1zPU1cqxA/pc2V+iMwVjzsSdL09vM8xg4fSofGen7YIj6sVUgs2X4AdTzm/ sha512-6J9O/4UFL8undkyqbEGAlNnEjm5N8Us5k9rJdcjhZwYKt1UIKXapw8f320k3tv1N0GB0Wt4xO6EKD9MsrIkCSw=="
|
||||
},
|
||||
"light.js": {
|
||||
"src": "public/build/light.js",
|
||||
"integrity": "sha256-TDwcqlc6Pc8yTRNLOk0jp3Wnp06cnF/OSaFvwOnwCVQ= sha384-xJv6KB8Qm9Skru8PY5CRdU9a59e94tcYWonpy5rqaci6fiecQM3aH+eHe41succB sha512-sPXciUAa7rpRofN6b93v9+tWpLP88ER6TqAGpbHkz69mxuQLJv1p580fEfxLeIs3HfLkDXnVPK7rGF0d5Evrog=="
|
||||
},
|
||||
"moment-node_modules_moment_locale_af_js-node_modules_moment_locale_ar-dz_js-node_modules_mome-ddcc36.js": {
|
||||
"src": "public/build/moment-node_modules_moment_locale_af_js-node_modules_moment_locale_ar-dz_js-node_modules_mome-ddcc36.js",
|
||||
"integrity": "sha256-BvrNPeEZbt43PpUlZ4r5pUougn3OzHIHKZN9pa5fotw= sha384-gMrZ0j/kyHwx2bhyt5ZjZOPlK7P7TRN6rBnk/0c9iF2X83GVTWNjhHcldmlYJXnf sha512-B+39Sk+Rwdo9hURcDoWL/61/sbMsTlqfES+FCsMC1r2xUH/Fyw/U2H0CgIi+c3AJyu1GLEvp//QBN0RZxaizhw=="
|
||||
},
|
||||
"runtime.js": {
|
||||
"src": "public/build/runtime.js",
|
||||
"integrity": "sha256-tM4AGASn3Cb8139+wp3w6rlo3ELFAuUW7K4Pifx226o= sha384-DfxxsYWb0+RxiXOr+wtCSzAAYGecffq/iHyn6CN9tHmaORv1sS+rsrnlnJo2jPQD sha512-qSxdqrx0mJLY1mdkbKrkCyqOoIEgFqzCoY9+uIuFRIVDPFbb2nJy0NtaKMQvDJnAzIrJFwzwW1e250T4WqQNiQ=="
|
||||
},
|
||||
"swagger.css": {
|
||||
"src": "public/build/grafana.swagger.2733d417270d5dd49373.css",
|
||||
"integrity": "sha256-GNcHNgIAT7S+J4X7seFjlvNPC1bRhM15d0cQBm3VFoQ= sha384-ywztCBf8uF0tTFjC1mLth33RI2WuFURN3dRy7Bv2PheGzbWJpwlgo9+mtT2Zm7mO sha512-e4c+VedZGqcwLqwfdqRWonggRPO0gjJ7Z0YbXK5z4bFTsUIc+x8ycIJG+eQaf8cuHlsakG4hkWNkRwLBazcFAg=="
|
||||
},
|
||||
"swagger.js": {
|
||||
"src": "public/build/swagger.js",
|
||||
"integrity": "sha256-wLlip7zRYODW/TPcI5JZPRdmWirc1KD+UcNF+8V9RBk= sha384-6VGD+LgCpjMZN/ORSjWcrWa9diUzQO3OfEhP0D2ZluSwP4IT+0kH7KEeD9NVbojd sha512-vZOCFzBZBhd34yGv8z7P4Gw4WLVR9HjpuK0y6Kcw+pCBk5Dv9qHBg3ZVs6s0tOnUmiMWwgL4Ne8f+zgiuJVPqg=="
|
||||
},
|
||||
"entrypoints": {
|
||||
"app": {
|
||||
"assets": {
|
||||
"js": [
|
||||
"public/build/runtime.js",
|
||||
"public/build/default-packages_grafana-ui_src_components_Layout_Stack_Stack_tsx-packages_grafana-ui_src_com-2a3620.js",
|
||||
"public/build/app.js"
|
||||
],
|
||||
"css": ["public/build/grafana.app.91aaa9d81398c147a57c.css"]
|
||||
}
|
||||
},
|
||||
"swagger": {
|
||||
"assets": {
|
||||
"js": [
|
||||
"public/build/runtime.js",
|
||||
"public/build/swagger.js"
|
||||
],
|
||||
"css": ["public/build/grafana.swagger.2733d417270d5dd49373.css"]
|
||||
}
|
||||
},
|
||||
"dark": {
|
||||
"assets": {
|
||||
"js": ["public/build/runtime.js", "public/build/dark.js"],
|
||||
"css": ["public/build/grafana.dark.722d809dba5a31f57d49.css"]
|
||||
}
|
||||
},
|
||||
"light": {
|
||||
"assets": {
|
||||
"js": ["public/build/runtime.js", "public/build/light.js"],
|
||||
"css": ["public/build/grafana.light.2fbd901d840329c18394.css"]
|
||||
}
|
||||
}
|
||||
"sass/grafana.light.scss": {
|
||||
"file": "public/build/grafana-Cu0f8nVU.css",
|
||||
"src": "sass/grafana.light.scss",
|
||||
"isEntry": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
@@ -26,6 +27,18 @@ type ManifestInfo struct {
|
||||
Swagger *EntryPointInfo `json:"swagger,omitempty"`
|
||||
}
|
||||
|
||||
// ViteManifestEntry represents a single entry in the Vite manifest.
|
||||
type ViteManifestEntry struct {
|
||||
File string `json:"file"`
|
||||
Imports []string `json:"imports"`
|
||||
IsDynamicEntry bool `json:"isDynamicEntry"`
|
||||
IsEntry bool `json:"isEntry"`
|
||||
Src string `json:"src"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Manifest map[string]ViteManifestEntry
|
||||
|
||||
type EntryPointInfo struct {
|
||||
Assets struct {
|
||||
JS []string `json:"js,omitempty"`
|
||||
@@ -43,6 +56,28 @@ func GetWebAssets(ctx context.Context, cfg *setting.Cfg, license licensing.Licen
|
||||
ret := entryPointAssetsCache
|
||||
entryPointAssetsCacheMu.RUnlock()
|
||||
|
||||
// see https://vitejs.dev/guide/backend-integration.html
|
||||
if cfg.Env == setting.Dev && cfg.FrontendDevServer {
|
||||
devServerHost := "http://localhost:5173"
|
||||
|
||||
viteDev := &dtos.EntryPointAssets{
|
||||
JSFiles: []dtos.EntryPointAsset{
|
||||
{
|
||||
FilePath: fmt.Sprintf("%s/@vite/client", devServerHost),
|
||||
Integrity: "",
|
||||
},
|
||||
{
|
||||
FilePath: fmt.Sprintf("%s/app/index.ts", devServerHost),
|
||||
Integrity: "",
|
||||
},
|
||||
},
|
||||
Dark: fmt.Sprintf("%s/sass/grafana.dark.scss", devServerHost),
|
||||
Light: fmt.Sprintf("%s/sass/grafana.light.scss", devServerHost),
|
||||
}
|
||||
|
||||
return viteDev, nil
|
||||
}
|
||||
|
||||
if cfg.Env != setting.Dev && ret != nil {
|
||||
return ret, nil
|
||||
}
|
||||
@@ -58,7 +93,7 @@ func GetWebAssets(ctx context.Context, cfg *setting.Cfg, license licensing.Licen
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
result, err = readWebAssetsFromFile(filepath.Join(cfg.StaticRootPath, "build", "assets-manifest.json"))
|
||||
result, err = readWebAssetsFromFile(filepath.Join(cfg.StaticRootPath, "build", ".vite", "manifest.json"))
|
||||
if err == nil {
|
||||
cdn, _ = cfg.GetContentDeliveryURL(license.ContentDeliveryPrefix())
|
||||
if cdn != "" {
|
||||
@@ -84,7 +119,7 @@ func readWebAssetsFromFile(manifestpath string) (*dtos.EntryPointAssets, error)
|
||||
}
|
||||
|
||||
func readWebAssetsFromCDN(ctx context.Context, baseURL string) (*dtos.EntryPointAssets, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, baseURL+"public/build/assets-manifest.json", nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, baseURL+"public/build/.vite/manifest.json", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -103,68 +138,145 @@ func readWebAssetsFromCDN(ctx context.Context, baseURL string) (*dtos.EntryPoint
|
||||
}
|
||||
|
||||
func readWebAssets(r io.Reader) (*dtos.EntryPointAssets, error) {
|
||||
manifest := map[string]ManifestInfo{}
|
||||
manifest := Manifest{}
|
||||
if err := json.NewDecoder(r).Decode(&manifest); err != nil {
|
||||
return nil, fmt.Errorf("failed to read assets-manifest.json %w", err)
|
||||
}
|
||||
|
||||
integrity := make(map[string]string, 100)
|
||||
for _, v := range manifest {
|
||||
if v.Integrity != "" && v.FilePath != "" {
|
||||
integrity[v.FilePath] = v.Integrity
|
||||
// TODO: This is temporary to get the entrypoints for the vite frontend loading.
|
||||
// We need trusted types up front otherwise the entire app will fail to load if they're enabled.
|
||||
var trustedTypePolicyAsset *dtos.EntryPointAsset
|
||||
var otherJSAssets []dtos.EntryPointAsset
|
||||
var preloadJSAssets []dtos.EntryPointAsset
|
||||
var darkCSS, lightCSS string
|
||||
|
||||
for _, entry := range manifest {
|
||||
if entry.IsEntry {
|
||||
asset := dtos.EntryPointAsset{
|
||||
FilePath: entry.File,
|
||||
// Not sure what should happen with integrity here
|
||||
Integrity: "",
|
||||
}
|
||||
if strings.HasSuffix(entry.File, ".js") {
|
||||
if entry.Name == "trustedTypePolicies" {
|
||||
trustedTypePolicyAsset = &asset
|
||||
} else {
|
||||
otherJSAssets = append(otherJSAssets, asset)
|
||||
}
|
||||
preloadJSAssets = append(preloadJSAssets, getPreloadChunks(manifest, entry.Src)...)
|
||||
}
|
||||
if entry.Src == "sass/grafana.dark.scss" && entry.IsEntry {
|
||||
darkCSS = entry.File
|
||||
}
|
||||
if entry.Src == "sass/grafana.light.scss" && entry.IsEntry {
|
||||
lightCSS = entry.File
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entryPoints, ok := manifest["entrypoints"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not find entrypoints in asssets-manifest")
|
||||
}
|
||||
// integrity := make(map[string]string, 100)
|
||||
// for _, v := range manifest {
|
||||
// if v.Integrity != "" && v.FilePath != "" {
|
||||
// integrity[v.FilePath] = v.Integrity
|
||||
// }
|
||||
// }
|
||||
|
||||
if entryPoints.App == nil || len(entryPoints.App.Assets.JS) == 0 {
|
||||
return nil, fmt.Errorf("missing app entry, try running `yarn build`")
|
||||
}
|
||||
if entryPoints.Dark == nil || len(entryPoints.Dark.Assets.CSS) == 0 {
|
||||
return nil, fmt.Errorf("missing dark entry, try running `yarn build`")
|
||||
}
|
||||
if entryPoints.Light == nil || len(entryPoints.Light.Assets.CSS) == 0 {
|
||||
return nil, fmt.Errorf("missing light entry, try running `yarn build`")
|
||||
}
|
||||
if entryPoints.Swagger == nil || len(entryPoints.Swagger.Assets.JS) == 0 {
|
||||
return nil, fmt.Errorf("missing swagger entry, try running `yarn build`")
|
||||
// entryPoints, ok := manifest["entrypoints"]
|
||||
// if !ok {
|
||||
// return nil, fmt.Errorf("could not find entrypoints in asssets-manifest")
|
||||
// }
|
||||
|
||||
// if entryPoints.App == nil || len(entryPoints.App.Assets.JS) == 0 {
|
||||
// return nil, fmt.Errorf("missing app entry, try running `yarn build`")
|
||||
// }
|
||||
// if entryPoints.Dark == nil || len(entryPoints.Dark.Assets.CSS) == 0 {
|
||||
// return nil, fmt.Errorf("missing dark entry, try running `yarn build`")
|
||||
// }
|
||||
// if entryPoints.Light == nil || len(entryPoints.Light.Assets.CSS) == 0 {
|
||||
// return nil, fmt.Errorf("missing light entry, try running `yarn build`")
|
||||
// }
|
||||
// if entryPoints.Swagger == nil || len(entryPoints.Swagger.Assets.JS) == 0 {
|
||||
// return nil, fmt.Errorf("missing swagger entry, try running `yarn build`")
|
||||
// }
|
||||
|
||||
var entryPointJSAssets = append([]dtos.EntryPointAsset{*trustedTypePolicyAsset}, otherJSAssets...)
|
||||
|
||||
if entryPointJSAssets == nil {
|
||||
return nil, fmt.Errorf("no entrypoints found in assets manifest, try running `yarn build`")
|
||||
}
|
||||
|
||||
rsp := &dtos.EntryPointAssets{
|
||||
JSFiles: make([]dtos.EntryPointAsset, 0, len(entryPoints.App.Assets.JS)),
|
||||
CSSFiles: make([]dtos.EntryPointAsset, 0, len(entryPoints.App.Assets.CSS)),
|
||||
Dark: entryPoints.Dark.Assets.CSS[0],
|
||||
Light: entryPoints.Light.Assets.CSS[0],
|
||||
Swagger: make([]dtos.EntryPointAsset, 0, len(entryPoints.Swagger.Assets.JS)),
|
||||
SwaggerCSSFiles: make([]dtos.EntryPointAsset, 0, len(entryPoints.Swagger.Assets.CSS)),
|
||||
JSFiles: entryPointJSAssets,
|
||||
PreloadJSFiles: preloadJSAssets,
|
||||
Dark: darkCSS,
|
||||
Light: lightCSS,
|
||||
// TODO: Need to figure this out. Ideally the swagger assets are built in a separate build process.
|
||||
// Swagger: make([]dtos.EntryPointAsset, 0, len(entryPoints.Swagger.Assets.JS)),
|
||||
// SwaggerCSSFiles: make([]dtos.EntryPointAsset, 0, len(entryPoints.Swagger.Assets.CSS)),
|
||||
}
|
||||
|
||||
for _, entry := range entryPoints.App.Assets.JS {
|
||||
rsp.JSFiles = append(rsp.JSFiles, dtos.EntryPointAsset{
|
||||
FilePath: entry,
|
||||
Integrity: integrity[entry],
|
||||
})
|
||||
}
|
||||
for _, entry := range entryPoints.App.Assets.CSS {
|
||||
rsp.CSSFiles = append(rsp.CSSFiles, dtos.EntryPointAsset{
|
||||
FilePath: entry,
|
||||
Integrity: integrity[entry],
|
||||
})
|
||||
}
|
||||
for _, entry := range entryPoints.Swagger.Assets.JS {
|
||||
rsp.Swagger = append(rsp.Swagger, dtos.EntryPointAsset{
|
||||
FilePath: entry,
|
||||
Integrity: integrity[entry],
|
||||
})
|
||||
}
|
||||
for _, entry := range entryPoints.Swagger.Assets.CSS {
|
||||
rsp.SwaggerCSSFiles = append(rsp.SwaggerCSSFiles, dtos.EntryPointAsset{
|
||||
FilePath: entry,
|
||||
Integrity: integrity[entry],
|
||||
})
|
||||
}
|
||||
// for _, entry := range entryPoints.App.Assets.JS {
|
||||
// rsp.JSFiles = append(rsp.JSFiles, dtos.EntryPointAsset{
|
||||
// FilePath: entry,
|
||||
// Integrity: integrity[entry],
|
||||
// })
|
||||
// }
|
||||
// for _, entry := range entryPoints.App.Assets.CSS {
|
||||
// rsp.CSSFiles = append(rsp.CSSFiles, dtos.EntryPointAsset{
|
||||
// FilePath: entry,
|
||||
// Integrity: integrity[entry],
|
||||
// })
|
||||
// }
|
||||
// for _, entry := range entryPoints.Swagger.Assets.JS {
|
||||
// rsp.Swagger = append(rsp.Swagger, dtos.EntryPointAsset{
|
||||
// FilePath: entry,
|
||||
// Integrity: integrity[entry],
|
||||
// })
|
||||
// }
|
||||
// for _, entry := range entryPoints.Swagger.Assets.CSS {
|
||||
// rsp.SwaggerCSSFiles = append(rsp.SwaggerCSSFiles, dtos.EntryPointAsset{
|
||||
// FilePath: entry,
|
||||
// Integrity: integrity[entry],
|
||||
// })
|
||||
// }
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
// Create a list of all the chunks that need to be preloaded for a given entrypoint.
|
||||
func getPreloadChunks(manifest Manifest, name string) []dtos.EntryPointAsset {
|
||||
seen := make(map[string]bool)
|
||||
var chunks []dtos.EntryPointAsset
|
||||
|
||||
var getImportedChunks func(chunk ViteManifestEntry)
|
||||
|
||||
getImportedChunks = func(chunk ViteManifestEntry) {
|
||||
for _, file := range chunk.Imports {
|
||||
importee, exists := manifest[file]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
if seen[file] {
|
||||
continue
|
||||
}
|
||||
seen[file] = true
|
||||
|
||||
getImportedChunks(importee)
|
||||
|
||||
if !seen[importee.File] {
|
||||
chunks = append(chunks, dtos.EntryPointAsset{
|
||||
FilePath: importee.File,
|
||||
Integrity: "",
|
||||
})
|
||||
seen[importee.File] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entryChunk, exists := manifest[name]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
getImportedChunks(entryChunk)
|
||||
return chunks
|
||||
}
|
||||
|
||||
@@ -3,107 +3,89 @@ package webassets
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func sortAssets(assets *dtos.EntryPointAssets) {
|
||||
sort.Slice(assets.JSFiles, func(i, j int) bool {
|
||||
return assets.JSFiles[i].FilePath < assets.JSFiles[j].FilePath
|
||||
})
|
||||
sort.Slice(assets.PreloadJSFiles, func(i, j int) bool {
|
||||
return assets.PreloadJSFiles[i].FilePath < assets.PreloadJSFiles[j].FilePath
|
||||
})
|
||||
}
|
||||
|
||||
func TestReadWebassets(t *testing.T) {
|
||||
assets, err := readWebAssetsFromFile("testdata/sample-assets-manifest.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
sortAssets(assets)
|
||||
|
||||
dto, err := json.MarshalIndent(assets, "", " ")
|
||||
require.NoError(t, err)
|
||||
// fmt.Printf("%s\n", string(dto))
|
||||
|
||||
require.JSONEq(t, `{
|
||||
"jsFiles": [
|
||||
{
|
||||
"filePath": "public/build/runtime.js",
|
||||
"integrity": "sha256-tM4AGASn3Cb8139+wp3w6rlo3ELFAuUW7K4Pifx226o= sha384-DfxxsYWb0+RxiXOr+wtCSzAAYGecffq/iHyn6CN9tHmaORv1sS+rsrnlnJo2jPQD sha512-qSxdqrx0mJLY1mdkbKrkCyqOoIEgFqzCoY9+uIuFRIVDPFbb2nJy0NtaKMQvDJnAzIrJFwzwW1e250T4WqQNiQ=="
|
||||
},
|
||||
{
|
||||
"filePath": "public/build/default-packages_grafana-ui_src_components_Layout_Stack_Stack_tsx-packages_grafana-ui_src_com-2a3620.js",
|
||||
"integrity": "sha256-+0bPuBGKFGglkXvW4oPiolrNveozRLZVLUrbCYsbVcM= sha384-EIayAgykdDWmyilAuXo4ad96v3tRqdWZp+BHdeDpSSsbAMeg+eoBBbk2Yh219kDg sha512-+jn7kmQ9Id8aTIe66TD+vM+W19cTIVexEfkxbxgqXdJyJ72qalN6ccWyP1ro1w/E1R/laZGNLz1LBc1I4u2Isw=="
|
||||
},
|
||||
{
|
||||
"filePath": "public/build/app.js",
|
||||
"integrity": "sha256-IOZKp3piC3vddDXP5jy5rIw0vb0KKEOg/k9EGrIxskk= sha384-CBNr5W0pJ23LQMnz5BZI1iVBIExOmF/wpqkEnBtYu9R/yYJIzjpv8KT0a3TBulOi sha512-ockzlzgosuZvLittZrSzh8lexEIZF9iKpy6J9Ii4es3e4D34FpWhHJhDZGpxLVraX4ypLofrDp2Yy0sdUbdi7w=="
|
||||
}
|
||||
],
|
||||
"cssFiles": [
|
||||
{
|
||||
"filePath": "public/build/grafana.app.91aaa9d81398c147a57c.css",
|
||||
"integrity": "sha256-77rfikk+dYkH82TOmcmleVoDOHZQdhzVX9gDLcgPbtQ= sha384-IOTlZ1IvTVq5ekKLoaE3/SoZ12K1eExOAnSw9BzkgQ3+RcyQpb1S5hO2w//IIkRB sha512-0Ct3uJBFQIkyxYTvMxseA1cphe2RivXQ2MCbiV0hEm5NzWPiY9sq2P4ay5dXz5v35c++4W47KaknoWlc83bQJQ=="
|
||||
}
|
||||
],
|
||||
"dark": "public/build/grafana.dark.722d809dba5a31f57d49.css",
|
||||
"light": "public/build/grafana.light.2fbd901d840329c18394.css",
|
||||
"swagger": [
|
||||
{
|
||||
"filePath": "public/build/runtime.js",
|
||||
"integrity": "sha256-tM4AGASn3Cb8139+wp3w6rlo3ELFAuUW7K4Pifx226o= sha384-DfxxsYWb0+RxiXOr+wtCSzAAYGecffq/iHyn6CN9tHmaORv1sS+rsrnlnJo2jPQD sha512-qSxdqrx0mJLY1mdkbKrkCyqOoIEgFqzCoY9+uIuFRIVDPFbb2nJy0NtaKMQvDJnAzIrJFwzwW1e250T4WqQNiQ=="
|
||||
},
|
||||
{
|
||||
"filePath": "public/build/swagger.js",
|
||||
"integrity": "sha256-wLlip7zRYODW/TPcI5JZPRdmWirc1KD+UcNF+8V9RBk= sha384-6VGD+LgCpjMZN/ORSjWcrWa9diUzQO3OfEhP0D2ZluSwP4IT+0kH7KEeD9NVbojd sha512-vZOCFzBZBhd34yGv8z7P4Gw4WLVR9HjpuK0y6Kcw+pCBk5Dv9qHBg3ZVs6s0tOnUmiMWwgL4Ne8f+zgiuJVPqg=="
|
||||
}
|
||||
],
|
||||
"swaggerCssFiles": [
|
||||
{
|
||||
"filePath": "public/build/grafana.swagger.2733d417270d5dd49373.css",
|
||||
"integrity": "sha256-GNcHNgIAT7S+J4X7seFjlvNPC1bRhM15d0cQBm3VFoQ= sha384-ywztCBf8uF0tTFjC1mLth33RI2WuFURN3dRy7Bv2PheGzbWJpwlgo9+mtT2Zm7mO sha512-e4c+VedZGqcwLqwfdqRWonggRPO0gjJ7Z0YbXK5z4bFTsUIc+x8ycIJG+eQaf8cuHlsakG4hkWNkRwLBazcFAg=="
|
||||
}
|
||||
]
|
||||
}`, string(dto))
|
||||
"jsFiles": [
|
||||
{
|
||||
"filePath": "public/build/index-fkCrlmGK.js",
|
||||
"integrity": ""
|
||||
},
|
||||
{
|
||||
"filePath": "public/build/trustedTypePolicies-pOmZ7Wr2.js",
|
||||
"integrity": ""
|
||||
}
|
||||
],
|
||||
"dark": "public/build/grafana-B9F4fUOy.css",
|
||||
"light": "public/build/grafana-Cu0f8nVU.css",
|
||||
"preloadJsFiles": [
|
||||
{
|
||||
"filePath": "public/build/braintree-CW7sY1ng.js",
|
||||
"integrity": ""
|
||||
},
|
||||
{
|
||||
"filePath": "public/build/vendor-r65R1Ntf.js",
|
||||
"integrity": ""
|
||||
}
|
||||
]
|
||||
}`, string(dto))
|
||||
|
||||
assets.SetContentDeliveryURL("https://grafana-assets.grafana.net/grafana/10.3.0-64123/")
|
||||
|
||||
sortAssets(assets)
|
||||
|
||||
dto, err = json.MarshalIndent(assets, "", " ")
|
||||
require.NoError(t, err)
|
||||
fmt.Printf("%s\n", string(dto))
|
||||
// fmt.Printf("%s\n", string(dto))
|
||||
|
||||
require.JSONEq(t, `{
|
||||
"cdn": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/",
|
||||
"jsFiles": [
|
||||
{
|
||||
"filePath": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/runtime.js",
|
||||
"integrity": "sha256-tM4AGASn3Cb8139+wp3w6rlo3ELFAuUW7K4Pifx226o= sha384-DfxxsYWb0+RxiXOr+wtCSzAAYGecffq/iHyn6CN9tHmaORv1sS+rsrnlnJo2jPQD sha512-qSxdqrx0mJLY1mdkbKrkCyqOoIEgFqzCoY9+uIuFRIVDPFbb2nJy0NtaKMQvDJnAzIrJFwzwW1e250T4WqQNiQ=="
|
||||
},
|
||||
{
|
||||
"filePath": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/default-packages_grafana-ui_src_components_Layout_Stack_Stack_tsx-packages_grafana-ui_src_com-2a3620.js",
|
||||
"integrity": "sha256-+0bPuBGKFGglkXvW4oPiolrNveozRLZVLUrbCYsbVcM= sha384-EIayAgykdDWmyilAuXo4ad96v3tRqdWZp+BHdeDpSSsbAMeg+eoBBbk2Yh219kDg sha512-+jn7kmQ9Id8aTIe66TD+vM+W19cTIVexEfkxbxgqXdJyJ72qalN6ccWyP1ro1w/E1R/laZGNLz1LBc1I4u2Isw=="
|
||||
},
|
||||
{
|
||||
"filePath": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/app.js",
|
||||
"integrity": "sha256-IOZKp3piC3vddDXP5jy5rIw0vb0KKEOg/k9EGrIxskk= sha384-CBNr5W0pJ23LQMnz5BZI1iVBIExOmF/wpqkEnBtYu9R/yYJIzjpv8KT0a3TBulOi sha512-ockzlzgosuZvLittZrSzh8lexEIZF9iKpy6J9Ii4es3e4D34FpWhHJhDZGpxLVraX4ypLofrDp2Yy0sdUbdi7w=="
|
||||
}
|
||||
],
|
||||
"cssFiles": [
|
||||
{
|
||||
"filePath": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/grafana.app.91aaa9d81398c147a57c.css",
|
||||
"integrity": "sha256-77rfikk+dYkH82TOmcmleVoDOHZQdhzVX9gDLcgPbtQ= sha384-IOTlZ1IvTVq5ekKLoaE3/SoZ12K1eExOAnSw9BzkgQ3+RcyQpb1S5hO2w//IIkRB sha512-0Ct3uJBFQIkyxYTvMxseA1cphe2RivXQ2MCbiV0hEm5NzWPiY9sq2P4ay5dXz5v35c++4W47KaknoWlc83bQJQ=="
|
||||
}
|
||||
],
|
||||
"dark": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/grafana.dark.722d809dba5a31f57d49.css",
|
||||
"light": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/grafana.light.2fbd901d840329c18394.css",
|
||||
"swagger": [
|
||||
{
|
||||
"filePath": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/runtime.js",
|
||||
"integrity": "sha256-tM4AGASn3Cb8139+wp3w6rlo3ELFAuUW7K4Pifx226o= sha384-DfxxsYWb0+RxiXOr+wtCSzAAYGecffq/iHyn6CN9tHmaORv1sS+rsrnlnJo2jPQD sha512-qSxdqrx0mJLY1mdkbKrkCyqOoIEgFqzCoY9+uIuFRIVDPFbb2nJy0NtaKMQvDJnAzIrJFwzwW1e250T4WqQNiQ=="
|
||||
},
|
||||
{
|
||||
"filePath": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/swagger.js",
|
||||
"integrity": "sha256-wLlip7zRYODW/TPcI5JZPRdmWirc1KD+UcNF+8V9RBk= sha384-6VGD+LgCpjMZN/ORSjWcrWa9diUzQO3OfEhP0D2ZluSwP4IT+0kH7KEeD9NVbojd sha512-vZOCFzBZBhd34yGv8z7P4Gw4WLVR9HjpuK0y6Kcw+pCBk5Dv9qHBg3ZVs6s0tOnUmiMWwgL4Ne8f+zgiuJVPqg=="
|
||||
}
|
||||
],
|
||||
"swaggerCssFiles": [
|
||||
{
|
||||
"filePath": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/grafana.swagger.2733d417270d5dd49373.css",
|
||||
"integrity": "sha256-GNcHNgIAT7S+J4X7seFjlvNPC1bRhM15d0cQBm3VFoQ= sha384-ywztCBf8uF0tTFjC1mLth33RI2WuFURN3dRy7Bv2PheGzbWJpwlgo9+mtT2Zm7mO sha512-e4c+VedZGqcwLqwfdqRWonggRPO0gjJ7Z0YbXK5z4bFTsUIc+x8ycIJG+eQaf8cuHlsakG4hkWNkRwLBazcFAg=="
|
||||
}
|
||||
]
|
||||
"cdn": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/",
|
||||
"jsFiles": [
|
||||
{
|
||||
"filePath": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/index-fkCrlmGK.js",
|
||||
"integrity": ""
|
||||
},
|
||||
{
|
||||
"filePath": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/trustedTypePolicies-pOmZ7Wr2.js",
|
||||
"integrity": ""
|
||||
}
|
||||
],
|
||||
"preloadJsFiles": [
|
||||
{
|
||||
"filePath": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/braintree-CW7sY1ng.js",
|
||||
"integrity": ""
|
||||
},
|
||||
{
|
||||
"filePath": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/vendor-r65R1Ntf.js",
|
||||
"integrity": ""
|
||||
}
|
||||
],
|
||||
"dark": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/grafana-B9F4fUOy.css",
|
||||
"light": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/grafana-Cu0f8nVU.css"
|
||||
}`, string(dto))
|
||||
}
|
||||
|
||||
@@ -113,6 +95,8 @@ func TestReadWebassetsFromCDN(t *testing.T) {
|
||||
assets, err := readWebAssetsFromCDN(context.Background(), "https://grafana-assets.grafana.net/grafana/10.3.0-64123/")
|
||||
require.NoError(t, err)
|
||||
|
||||
sortAssets(assets)
|
||||
|
||||
dto, err := json.MarshalIndent(assets, "", " ")
|
||||
require.NoError(t, err)
|
||||
//fmt.Printf("%s\n", string(dto))
|
||||
@@ -147,5 +131,6 @@ func TestReadWebassetsFromCDN(t *testing.T) {
|
||||
],
|
||||
"dark": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/grafana.dark.b44253d019cd9cb46428.css",
|
||||
"light": "https://grafana-assets.grafana.net/grafana/10.3.0-64123/public/build/grafana.light.e8e11c59b604d62836be.css"
|
||||
"preloadJsFiles": null
|
||||
}`, string(dto))
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ type Cfg struct {
|
||||
AppSubURL string
|
||||
InstanceName string
|
||||
ServeFromSubPath bool
|
||||
FrontendDevServer bool
|
||||
StaticRootPath string
|
||||
Protocol Scheme
|
||||
SocketGid int
|
||||
@@ -1886,6 +1887,8 @@ func (cfg *Cfg) readServerSettings(iniFile *ini.File) error {
|
||||
return fmt.Errorf("TLS version not configured correctly:%v, allowed values are TLS1.2 and TLS1.3", cfg.MinTLSVersion)
|
||||
}
|
||||
|
||||
// TODO: sanitize this for trailing slashes
|
||||
cfg.FrontendDevServer = server.Key("frontend_dev_server").MustBool(false)
|
||||
cfg.Domain = valueAsString(server, "domain", "localhost")
|
||||
cfg.HTTPAddr = valueAsString(server, "http_addr", DefaultHTTPAddr)
|
||||
cfg.HTTPPort = valueAsString(server, "http_port", "3000")
|
||||
|
||||
@@ -205,32 +205,48 @@ func CreateGrafDir(t *testing.T, opts GrafanaOpts) (string, string) {
|
||||
buildDir := filepath.Join(publicDir, "build")
|
||||
err = os.MkdirAll(buildDir, 0750)
|
||||
require.NoError(t, err)
|
||||
err = os.WriteFile(filepath.Join(buildDir, "assets-manifest.json"), []byte(`{
|
||||
"entrypoints": {
|
||||
"app": {
|
||||
"assets": {
|
||||
"js": ["public/build/runtime.XYZ.js"]
|
||||
}
|
||||
},
|
||||
"swagger": {
|
||||
"assets": {
|
||||
"js": ["public/build/runtime.XYZ.js"]
|
||||
}
|
||||
},
|
||||
"dark": {
|
||||
"assets": {
|
||||
"css": ["public/build/dark.css"]
|
||||
}
|
||||
},
|
||||
"light": {
|
||||
"assets": {
|
||||
"css": ["public/build/light.css"]
|
||||
}
|
||||
}
|
||||
manifestDir := filepath.Join(buildDir, ".vite")
|
||||
err = os.MkdirAll(manifestDir, 0750)
|
||||
require.NoError(t, err)
|
||||
err = os.WriteFile(filepath.Join(manifestDir, "manifest.json"), []byte(`{
|
||||
"app/core/trustedTypePolicies.ts": {
|
||||
"file": "public/build/trustedTypePolicies-pOmZ7Wr2.js",
|
||||
"name": "trustedTypePolicies",
|
||||
"src": "app/core/trustedTypePolicies.ts",
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"_braintree-CW7sY1ng.js"
|
||||
]
|
||||
},
|
||||
"_braintree-CW7sY1ng.js": {
|
||||
"file": "public/build/braintree-CW7sY1ng.js",
|
||||
"name": "braintree"
|
||||
},
|
||||
"runtime.50398398ecdeaf58968c.js": {
|
||||
"src": "public/build/runtime.XYZ.js",
|
||||
"integrity": "sha256-k1g7TksMHFQhhQGE"
|
||||
"app/index.ts": {
|
||||
"file": "public/build/index-3exGqndc.js",
|
||||
"src": "app/index.ts",
|
||||
"isEntry": true,
|
||||
"isDynamicEntry": true,
|
||||
"css": ["public/build/index-Dd9uKMaJ.css"],
|
||||
"assets": ["public/build/codicon-SZR-f31-.ttf"]
|
||||
},
|
||||
"swagger/index.ts": {
|
||||
"file": "public/build/swagger-3exGqndc.js",
|
||||
"src": "swagger/index.ts",
|
||||
"isEntry": true,
|
||||
"isDynamicEntry": true,
|
||||
"css": ["public/build/swagger-Dd9uKMaJ.css"],
|
||||
"assets": ["public/build/codicon-SZR-f31-.ttf"]
|
||||
},
|
||||
"sass/grafana.dark.scss": {
|
||||
"file": "public/build/grafana-BwD1GyB5.css",
|
||||
"src": "sass/grafana.dark.scss",
|
||||
"isEntry": true
|
||||
},
|
||||
"sass/grafana.light.scss": {
|
||||
"file": "public/build/grafana-RC8BVZtE.css",
|
||||
"src": "sass/grafana.light.scss",
|
||||
"isEntry": true
|
||||
}
|
||||
}
|
||||
`), 0750)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IRootScopeService, IAngularEvent, auto } from 'angular';
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import _ from 'lodash'; // eslint-disable-line lodash/import-scope
|
||||
|
||||
import { AppEvent } from '@grafana/data';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import angular from 'angular';
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
|
||||
import coreModule from './core_module';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import angular from 'angular';
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { isFunction } from 'lodash';
|
||||
|
||||
import coreModule from './core_module';
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { each } from 'lodash';
|
||||
// @ts-ignore
|
||||
import Drop from 'tether-drop';
|
||||
|
||||
import coreModule from 'app/angular/core_module';
|
||||
|
||||
// @ts-ignore
|
||||
import Drop from '../../../vendor/tether-drop';
|
||||
|
||||
export function infoPopover() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -54,6 +55,7 @@ export function infoPopover() {
|
||||
|
||||
// Create drop in next digest after directive content is rendered.
|
||||
scope.$applyAsync(() => {
|
||||
// @ts-ignore
|
||||
const drop = new Drop(dropOptions);
|
||||
|
||||
const unbind = scope.$on('$destroy', () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { debounce, each, map, partial, escape, unescape } from 'lodash';
|
||||
|
||||
import coreModule from 'app/angular/core_module';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-ignore
|
||||
import baron from 'baron';
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
|
||||
import coreModule from 'app/angular/core_module';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { debounce, each, indexOf, map, partial, escape, unescape } from 'lodash';
|
||||
|
||||
import coreModule from 'app/angular/core_module';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { each, reduce } from 'lodash';
|
||||
|
||||
import coreModule from './core_module';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import angular from 'angular';
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { extend } from 'lodash';
|
||||
|
||||
const $win = $(window);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { debounce, find, indexOf, map, escape, unescape } from 'lodash';
|
||||
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
@@ -1,4 +1,24 @@
|
||||
let templates = (require as any).context('../', true, /\.html$/);
|
||||
templates.keys().forEach((key: string) => {
|
||||
templates(key);
|
||||
});
|
||||
// TODO: Vite has no require.context support so we're importing them individually here.
|
||||
// See the vite.config.ts angularHtmlImport plugin for the code that compiles these templates.
|
||||
|
||||
import 'app/angular/panel/partials/query_editor_row.html?inline';
|
||||
import 'app/angular/partials/http_settings_next.html?inline';
|
||||
import 'app/angular/partials/tls_auth_settings.html?inline';
|
||||
import 'app/features/annotations/partials/event_editor.html?inline';
|
||||
import 'app/partials/confirm_modal.html?inline';
|
||||
import 'app/partials/modal.html?inline';
|
||||
import 'app/partials/reset_password.html?inline';
|
||||
import 'app/partials/signup_invited.html?inline';
|
||||
import 'app/plugins/panel/graph/axes_editor.html?inline';
|
||||
import 'app/plugins/panel/graph/tab_display.html?inline';
|
||||
import 'app/plugins/panel/graph/tab_legend.html?inline';
|
||||
import 'app/plugins/panel/graph/tab_series_overrides.html?inline';
|
||||
import 'app/plugins/panel/graph/tab_thresholds.html?inline';
|
||||
import 'app/plugins/panel/graph/tab_time_regions.html?inline';
|
||||
import 'app/plugins/panel/graph/thresholds_form.html?inline';
|
||||
import 'app/plugins/panel/graph/time_regions_form.html?inline';
|
||||
import 'app/plugins/panel/heatmap/partials/axes_editor.html?inline';
|
||||
import 'app/plugins/panel/heatmap/partials/display_editor.html?inline';
|
||||
import 'app/plugins/panel/table-old/column_options.html?inline';
|
||||
import 'app/plugins/panel/table-old/editor.html?inline';
|
||||
import 'app/plugins/panel/table-old/module.html?inline';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
|
||||
import coreModule from './core_module';
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { extend } from 'lodash';
|
||||
// @ts-ignore
|
||||
import Drop from 'tether-drop';
|
||||
|
||||
import { GrafanaRootScope } from 'app/angular/GrafanaCtrl';
|
||||
import coreModule from 'app/angular/core_module';
|
||||
|
||||
// @ts-ignore
|
||||
import Drop from '../../../vendor/tether-drop';
|
||||
|
||||
coreModule.service('popoverSrv', ['$compile', '$rootScope', '$timeout', popoverSrv]);
|
||||
|
||||
function popoverSrv(this: any, $compile: any, $rootScope: GrafanaRootScope, $timeout: any) {
|
||||
@@ -51,6 +52,7 @@ function popoverSrv(this: any, $compile: any, $rootScope: GrafanaRootScope, $tim
|
||||
$compile(contentElement)(scope);
|
||||
|
||||
$timeout(() => {
|
||||
// @ts-ignore
|
||||
drop = new Drop({
|
||||
target: options.element,
|
||||
content: contentElement,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import angular from 'angular';
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
|
||||
import { getTagColorsFromName } from '@grafana/ui';
|
||||
|
||||
|
||||
@@ -104,11 +104,14 @@ import { createSystemVariableAdapter } from './features/variables/system/adapter
|
||||
import { createTextBoxVariableAdapter } from './features/variables/textbox/adapter';
|
||||
import { configureStore } from './store/configureStore';
|
||||
|
||||
// import symlinked extensions
|
||||
const extensionsIndex = require.context('.', true, /extensions\/index.ts/);
|
||||
const extensionsExports = extensionsIndex.keys().map((key) => {
|
||||
return extensionsIndex(key);
|
||||
});
|
||||
type ExtensionExports = {
|
||||
init: () => void;
|
||||
addExtensionReducers: () => void;
|
||||
};
|
||||
|
||||
// import symlinked extensions (use glob so vite doesn't error if no files are found)
|
||||
const extensions: Record<string, ExtensionExports> = import.meta.glob('./extensions/index.ts', { eager: true });
|
||||
const extensionsExports = Object.values(extensions);
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
initDevFeatures();
|
||||
|
||||
@@ -3,12 +3,14 @@ import { BaseStateReport } from 'crashme/dist/types';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
import { config, createMonitoringLogger } from '@grafana/runtime';
|
||||
import { CorsWorker as Worker } from 'app/core/utils/CorsWorker';
|
||||
import { corsWorker } from 'app/core/utils/CorsWorker';
|
||||
|
||||
import { contextSrv } from '../services/context_srv';
|
||||
import { CorsSharedWorker as SharedWorker, sharedWorkersSupported } from '../utils/CorsSharedWorker';
|
||||
import { corsSharedWorker, sharedWorkersSupported } from '../utils/CorsSharedWorker';
|
||||
|
||||
import clientWorkerUrl from './client.worker?worker&url';
|
||||
import { isChromePerformance, prepareContext } from './crash.utils';
|
||||
import detectorWorkerUrl from './detector.worker?sharedworker&url';
|
||||
|
||||
const logger = createMonitoringLogger('core.crash-detection');
|
||||
|
||||
@@ -48,7 +50,7 @@ export function initializeCrashDetection() {
|
||||
dbName: 'grafana.crashes',
|
||||
|
||||
createClientWorker(): Worker {
|
||||
return new Worker(new URL('./client.worker', import.meta.url));
|
||||
return corsWorker(clientWorkerUrl, { name: 'crash' });
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -61,8 +63,7 @@ export function initializeCrashDetection() {
|
||||
* We guarantee the type assertion is correct by returning a SharedWorker in CorsSharedWorker constructor.
|
||||
*/
|
||||
createDetectorWorker() {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
return new SharedWorker(new URL('./detector.worker', import.meta.url)) as globalThis.SharedWorker;
|
||||
return corsSharedWorker(detectorWorkerUrl, { name: 'crashDetector' });
|
||||
},
|
||||
|
||||
reportCrash: async (report) => {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const localeExtensionImports = {};
|
||||
@@ -1,6 +1,11 @@
|
||||
import { ResourceKey } from 'i18next';
|
||||
import { uniq } from 'lodash';
|
||||
|
||||
// we mock this in jest as import.meta.glob breaks things, so we don't even attempt to load enterprise translations...
|
||||
import { localeExtensionImports, type LocaleFileLoader } from './extensions';
|
||||
|
||||
// TODO: this is just to satisfy extensions which import this type from this file.
|
||||
export type { LocaleFileLoader };
|
||||
|
||||
export const ENGLISH_US = 'en-US';
|
||||
export const FRENCH_FRANCE = 'fr-FR';
|
||||
export const SPANISH_SPAIN = 'es-ES';
|
||||
@@ -11,7 +16,8 @@ export const PSEUDO_LOCALE = 'pseudo';
|
||||
|
||||
export const DEFAULT_LANGUAGE = ENGLISH_US;
|
||||
|
||||
export type LocaleFileLoader = () => Promise<ResourceKey>;
|
||||
const importLanguageFile = (languageCode: string) =>
|
||||
import(`../../../locales/${languageCode}/grafana.json`).then((data) => data.default);
|
||||
|
||||
export interface LanguageDefinition<Namespace extends string = string> {
|
||||
/** IETF language tag for the language e.g. en-US */
|
||||
@@ -29,7 +35,7 @@ export const LANGUAGES: LanguageDefinition[] = [
|
||||
code: ENGLISH_US,
|
||||
name: 'English',
|
||||
loader: {
|
||||
grafana: () => import('../../../locales/en-US/grafana.json'),
|
||||
grafana: () => importLanguageFile('en-US'),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -37,7 +43,7 @@ export const LANGUAGES: LanguageDefinition[] = [
|
||||
code: FRENCH_FRANCE,
|
||||
name: 'Français',
|
||||
loader: {
|
||||
grafana: () => import('../../../locales/fr-FR/grafana.json'),
|
||||
grafana: () => importLanguageFile('fr-FR'),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -45,7 +51,7 @@ export const LANGUAGES: LanguageDefinition[] = [
|
||||
code: SPANISH_SPAIN,
|
||||
name: 'Español',
|
||||
loader: {
|
||||
grafana: () => import('../../../locales/es-ES/grafana.json'),
|
||||
grafana: () => importLanguageFile('es-ES'),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -53,7 +59,7 @@ export const LANGUAGES: LanguageDefinition[] = [
|
||||
code: GERMAN_GERMANY,
|
||||
name: 'Deutsch',
|
||||
loader: {
|
||||
grafana: () => import('../../../locales/de-DE/grafana.json'),
|
||||
grafana: () => importLanguageFile('de-DE'),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -61,7 +67,7 @@ export const LANGUAGES: LanguageDefinition[] = [
|
||||
code: CHINESE_SIMPLIFIED,
|
||||
name: '中文(简体)',
|
||||
loader: {
|
||||
grafana: () => import('../../../locales/zh-Hans/grafana.json'),
|
||||
grafana: () => importLanguageFile('zh-Hans'),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -69,7 +75,7 @@ export const LANGUAGES: LanguageDefinition[] = [
|
||||
code: BRAZILIAN_PORTUGUESE,
|
||||
name: 'Português Brasileiro',
|
||||
loader: {
|
||||
grafana: () => import('../../../locales/pt-BR/grafana.json'),
|
||||
grafana: () => importLanguageFile('pt-BR'),
|
||||
},
|
||||
},
|
||||
] satisfies Array<LanguageDefinition<'grafana'>>;
|
||||
@@ -79,7 +85,7 @@ if (process.env.NODE_ENV === 'development') {
|
||||
code: PSEUDO_LOCALE,
|
||||
name: 'Pseudo-locale',
|
||||
loader: {
|
||||
grafana: () => import('../../../locales/pseudo-LOCALE/grafana.json'),
|
||||
grafana: () => importLanguageFile('pseudo-LOCALE'),
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -87,13 +93,11 @@ if (process.env.NODE_ENV === 'development') {
|
||||
// Optionally load enterprise locale extensions, if they are present.
|
||||
// It is important that this happens before NAMESPACES is defined so it has the correct value
|
||||
//
|
||||
// require.context doesn't work in jest, so we don't even attempt to load enterprise translations...
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
const extensionRequireContext = require.context('../../', true, /app\/extensions\/locales\/localeExtensions/);
|
||||
if (extensionRequireContext.keys().includes('app/extensions/locales/localeExtensions')) {
|
||||
const { LOCALE_EXTENSIONS, ENTERPRISE_I18N_NAMESPACE } = extensionRequireContext(
|
||||
'app/extensions/locales/localeExtensions'
|
||||
);
|
||||
const localeExtensionExports = Object.values(localeExtensionImports);
|
||||
|
||||
if (localeExtensionExports.length > 0) {
|
||||
const { LOCALE_EXTENSIONS, ENTERPRISE_I18N_NAMESPACE } = localeExtensionExports[0];
|
||||
|
||||
for (const language of LANGUAGES) {
|
||||
const localeLoader = LOCALE_EXTENSIONS[language.code];
|
||||
|
||||
14
public/app/core/internationalization/extensions.ts
Normal file
14
public/app/core/internationalization/extensions.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ResourceKey } from 'i18next';
|
||||
export type LocaleFileLoader = () => Promise<ResourceKey>;
|
||||
|
||||
type LocaleExtensionExports = {
|
||||
LOCALE_EXTENSIONS: Record<string, LocaleFileLoader | undefined>;
|
||||
ENTERPRISE_I18N_NAMESPACE: 'string';
|
||||
};
|
||||
|
||||
export const localeExtensionImports: Record<string, LocaleExtensionExports> = import.meta.glob(
|
||||
'../../app/extensions/locales/localeExtensions.ts',
|
||||
{
|
||||
eager: true,
|
||||
}
|
||||
);
|
||||
@@ -1,5 +1,12 @@
|
||||
import editorWorkerUrl from 'monaco-editor/esm/vs/editor/editor.worker.js?worker&url';
|
||||
import cssWorkerUrl from 'monaco-editor/esm/vs/language/css/css.worker?worker&url';
|
||||
import htmlWorkerUrl from 'monaco-editor/esm/vs/language/html/html.worker?worker&url';
|
||||
import jsonWorkerUrl from 'monaco-editor/esm/vs/language/json/json.worker?worker&url';
|
||||
import typescriptWorkerUrl from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker&url';
|
||||
|
||||
import { monacoLanguageRegistry } from '@grafana/data';
|
||||
import { CorsWorker as Worker } from 'app/core/utils/CorsWorker';
|
||||
|
||||
import { corsWorker } from './utils/CorsWorker';
|
||||
|
||||
export function setMonacoEnv() {
|
||||
self.MonacoEnvironment = {
|
||||
@@ -7,26 +14,27 @@ export function setMonacoEnv() {
|
||||
const language = monacoLanguageRegistry.getIfExists(label);
|
||||
|
||||
if (language) {
|
||||
return language.init();
|
||||
const moduleUrl = language.init();
|
||||
return corsWorker(moduleUrl, { name: label });
|
||||
}
|
||||
|
||||
if (label === 'json') {
|
||||
return new Worker(new URL('monaco-editor/esm/vs/language/json/json.worker', import.meta.url));
|
||||
return corsWorker(jsonWorkerUrl, { name: label });
|
||||
}
|
||||
|
||||
if (label === 'css' || label === 'scss' || label === 'less') {
|
||||
return new Worker(new URL('monaco-editor/esm/vs/language/css/css.worker', import.meta.url));
|
||||
return corsWorker(cssWorkerUrl, { name: label });
|
||||
}
|
||||
|
||||
if (label === 'html' || label === 'handlebars' || label === 'razor') {
|
||||
return new Worker(new URL('monaco-editor/esm/vs/language/html/html.worker', import.meta.url));
|
||||
return corsWorker(htmlWorkerUrl, { name: label });
|
||||
}
|
||||
|
||||
if (label === 'typescript' || label === 'javascript') {
|
||||
return new Worker(new URL('monaco-editor/esm/vs/language/typescript/ts.worker', import.meta.url));
|
||||
return corsWorker(typescriptWorkerUrl, { name: label });
|
||||
}
|
||||
|
||||
return new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker', import.meta.url));
|
||||
return corsWorker(editorWorkerUrl, { name: label });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Suspense, useEffect, useLayoutEffect } from 'react';
|
||||
import { Navigate, useLocation } from 'react-router-dom-v5-compat';
|
||||
// @ts-ignore
|
||||
import Drop from 'tether-drop';
|
||||
|
||||
import { locationSearchToObject, navigationLogger, reportPageview } from '@grafana/runtime';
|
||||
import { ErrorBoundary } from '@grafana/ui';
|
||||
|
||||
// @ts-ignore
|
||||
import Drop from '../../../vendor/tether-drop';
|
||||
import { useGrafana } from '../context/GrafanaContext';
|
||||
import { contextSrv } from '../services/context_srv';
|
||||
|
||||
@@ -101,6 +101,8 @@ function cleanupDOM() {
|
||||
}
|
||||
|
||||
// cleanup tether-drop
|
||||
// TODO: Vite doesn't like this. Pretty sure the lib is incompatible with Vite.
|
||||
// @ts-ignore
|
||||
for (const drop of Drop.drops) {
|
||||
drop.destroy();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { textUtil } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
|
||||
const CSP_REPORT_ONLY_ENABLED = config.bootData.settings.cspReportOnlyEnabled;
|
||||
const CSP_REPORT_ONLY_ENABLED = window.grafanaBootData!.settings.cspReportOnlyEnabled;
|
||||
|
||||
export const defaultTrustedTypesPolicy = {
|
||||
createHTML: (string: string, source: string, sink: string) => {
|
||||
@@ -14,7 +13,7 @@ export const defaultTrustedTypesPolicy = {
|
||||
createScript: (string: string) => string,
|
||||
createScriptURL: (string: string, source: string, sink: string) => {
|
||||
if (!CSP_REPORT_ONLY_ENABLED) {
|
||||
return textUtil.sanitizeUrl(string);
|
||||
return sanitizeUrl(string);
|
||||
}
|
||||
console.error('[ScriptURL not sanitized with Trusted Types]', string, source, sink);
|
||||
return string;
|
||||
@@ -22,7 +21,7 @@ export const defaultTrustedTypesPolicy = {
|
||||
};
|
||||
|
||||
if (
|
||||
config.bootData.settings.trustedTypesDefaultPolicyEnabled &&
|
||||
window.grafanaBootData!.settings.trustedTypesDefaultPolicyEnabled &&
|
||||
window.trustedTypes &&
|
||||
window.trustedTypes.createPolicy
|
||||
) {
|
||||
|
||||
@@ -1,33 +1,15 @@
|
||||
// Almost identical to CorsWorker.ts. Main difference being it allows loading a SharedWorker if browser supports it
|
||||
|
||||
export function sharedWorkersSupported() {
|
||||
return typeof window.SharedWorker !== 'undefined';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating CorsSharedWorker should be called only if sharedWorkersSupported() is truthy
|
||||
*/
|
||||
export class CorsSharedWorker {
|
||||
constructor(url: URL, options?: WorkerOptions) {
|
||||
if (!sharedWorkersSupported()) {
|
||||
throw new Error('SharedWorker is not supported');
|
||||
}
|
||||
// by default, worker inherits HTML document's location and pathname which leads to wrong public path value
|
||||
// the CorsWorkerPlugin will override it with the value based on the initial worker chunk, ie.
|
||||
// initial worker chunk: http://host.com/cdn/scripts/worker-123.js
|
||||
// resulting public path: http://host.com/cdn/scripts
|
||||
|
||||
const scriptUrl = url.toString();
|
||||
const scriptsBasePathUrl = new URL('.', url).toString();
|
||||
|
||||
const importScripts = `importScripts('${scriptUrl}');`;
|
||||
const objectURL = URL.createObjectURL(
|
||||
new Blob([`__webpack_worker_public_path__ = '${scriptsBasePathUrl}'; ${importScripts}`], {
|
||||
type: 'application/javascript',
|
||||
})
|
||||
);
|
||||
const worker = new SharedWorker(objectURL, options);
|
||||
URL.revokeObjectURL(objectURL);
|
||||
return worker;
|
||||
}
|
||||
// This function is used to create a sharedworker that can load across domains.
|
||||
export function corsSharedWorker(workerUrl: string, options: WorkerOptions) {
|
||||
const js = `import ${JSON.stringify(new URL(workerUrl, import.meta.url))}`;
|
||||
const blob = new Blob([js], { type: 'application/javascript' });
|
||||
const objURL = URL.createObjectURL(blob);
|
||||
const worker = new SharedWorker(objURL, { type: 'module', name: options?.name });
|
||||
worker.addEventListener('error', (e) => {
|
||||
URL.revokeObjectURL(objURL);
|
||||
});
|
||||
return worker;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
// works with webpack plugin: scripts/webpack/plugins/CorsWorkerPlugin.js
|
||||
export class CorsWorker extends window.Worker {
|
||||
constructor(url: URL, options?: WorkerOptions) {
|
||||
// by default, worker inherits HTML document's location and pathname which leads to wrong public path value
|
||||
// the CorsWorkerPlugin will override it with the value based on the initial worker chunk, ie.
|
||||
// initial worker chunk: http://host.com/cdn/scripts/worker-123.js
|
||||
// resulting public path: http://host.com/cdn/scripts
|
||||
|
||||
const scriptUrl = url.toString();
|
||||
const scriptsBasePathUrl = new URL('.', url).toString();
|
||||
|
||||
const importScripts = `importScripts('${scriptUrl}');`;
|
||||
const objectURL = URL.createObjectURL(
|
||||
new Blob([`__webpack_worker_public_path__ = '${scriptsBasePathUrl}'; ${importScripts}`], {
|
||||
type: 'application/javascript',
|
||||
})
|
||||
);
|
||||
super(objectURL, options);
|
||||
URL.revokeObjectURL(objectURL);
|
||||
}
|
||||
// This function is used to create a worker that can load across domains.
|
||||
// To use it, you need to import the workerUrl and pass it as the first argument.
|
||||
//
|
||||
// import clientWorkerUrl from './client.worker?worker&url';
|
||||
//
|
||||
// corsWorker(clientWorkerUrl, { name: 'myWorker' });
|
||||
//
|
||||
export function corsWorker(workerUrl: string, options: WorkerOptions) {
|
||||
const js = `import ${JSON.stringify(new URL(workerUrl, import.meta.url))}`;
|
||||
const blob = new Blob([js], { type: 'application/javascript' });
|
||||
const objURL = URL.createObjectURL(blob);
|
||||
const worker = new Worker(objURL, { type: 'module', name: options?.name });
|
||||
worker.addEventListener('error', (e) => {
|
||||
URL.revokeObjectURL(objURL);
|
||||
});
|
||||
return worker;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { CorsWorker as Worker } from 'app/core/utils/CorsWorker';
|
||||
import { corsWorker } from 'app/core/utils/CorsWorker';
|
||||
|
||||
import matcherWorkerUrl from './routeGroupsMatcher.worker?worker&url';
|
||||
|
||||
// CorsWorker is needed as a workaround for CORS issue caused
|
||||
// by static assets served from an url different from origin
|
||||
export const createWorker = () => new Worker(new URL('./routeGroupsMatcher.worker.ts', import.meta.url));
|
||||
export const createWorker = () => corsWorker(matcherWorkerUrl, { name: 'routeGroupsMatcher' });
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { SceneObjectStateChangedEvent } from '@grafana/scenes';
|
||||
import { Dashboard } from '@grafana/schema';
|
||||
import { CorsWorker } from 'app/core/utils/CorsWorker';
|
||||
import * as createDetectChangesWorker from 'app/features/dashboard-scene/saving/createDetectChangesWorker';
|
||||
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
@@ -25,7 +24,7 @@ describe('DashboardSceneChangeTracker', () => {
|
||||
() =>
|
||||
({
|
||||
terminate,
|
||||
}) as unknown as CorsWorker
|
||||
}) as unknown as Worker
|
||||
);
|
||||
const changeTracker = new DashboardSceneChangeTracker({
|
||||
subscribeToEvent: jest.fn().mockReturnValue({ unsubscribe: jest.fn() }),
|
||||
@@ -44,7 +43,7 @@ describe('DashboardSceneChangeTracker', () => {
|
||||
jest.spyOn(createDetectChangesWorker, 'createWorker').mockImplementation(() => {
|
||||
return {
|
||||
postMessage,
|
||||
} as unknown as CorsWorker;
|
||||
} as unknown as Worker;
|
||||
});
|
||||
jest.spyOn(DashboardSceneChangeTracker, 'isUpdatingPersistedState').mockImplementation(() => {
|
||||
return true;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { CorsWorker as Worker } from 'app/core/utils/CorsWorker';
|
||||
import { corsWorker } from 'app/core/utils/CorsWorker';
|
||||
|
||||
export const createWorker = () => new Worker(new URL('./DetectChangesWorker.ts', import.meta.url));
|
||||
import detectChangesWorkerUrl from './DetectChangesWorker?worker&url';
|
||||
|
||||
export const createWorker = () => corsWorker(detectChangesWorkerUrl, { name: 'detectChanges' });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import _, { isFunction } from 'lodash'; // eslint-disable-line lodash/import-scope
|
||||
import moment from 'moment'; // eslint-disable-line no-restricted-imports
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { CorsWorker as Worker } from 'app/core/utils/CorsWorker';
|
||||
import { corsWorker } from 'app/core/utils/CorsWorker';
|
||||
|
||||
export const createWorker = () => new Worker(new URL('./service.worker.ts', import.meta.url));
|
||||
import centrifugeWorkerUrl from './service.worker?worker&url';
|
||||
|
||||
export const createWorker = () => corsWorker(centrifugeWorkerUrl, { name: 'centrifuge' });
|
||||
|
||||
@@ -26,7 +26,7 @@ import * as ticks from 'app/core/utils/ticks';
|
||||
// Help the 6.4 to 6.5 migration
|
||||
// The base classes were moved from @grafana/ui to @grafana/data
|
||||
// This exposes the same classes on both import paths
|
||||
const grafanaUI: Record<string, unknown> = grafanaUIraw;
|
||||
const grafanaUI: System.Module = { ...grafanaUIraw };
|
||||
grafanaUI.PanelPlugin = grafanaData.PanelPlugin;
|
||||
grafanaUI.DataSourcePlugin = grafanaData.DataSourcePlugin;
|
||||
grafanaUI.AppPlugin = grafanaData.AppPlugin;
|
||||
|
||||
@@ -22,7 +22,8 @@ import { logError, logInfo } from './utils';
|
||||
|
||||
// Loads near membrane custom formatter for near membrane proxy objects.
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
require('@locker/near-membrane-dom/custom-devtools-formatter');
|
||||
// @ts-ignore - this is aliased in vite config but ts complains because it cannot resolve it.
|
||||
import('@locker/near-membrane-dom/custom-devtools-formatter');
|
||||
}
|
||||
|
||||
const pluginImportCache = new Map<string, Promise<System.Module>>();
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import './core/trustedTypePolicies';
|
||||
declare let __webpack_public_path__: string;
|
||||
declare let __webpack_nonce__: string;
|
||||
import './viteGlobals';
|
||||
import 'vite/modulepreload-polyfill';
|
||||
|
||||
// Check if we are hosting files on cdn and set webpack public path
|
||||
if (window.public_cdn_path) {
|
||||
__webpack_public_path__ = window.public_cdn_path;
|
||||
}
|
||||
// TODO: Vite does this differently...
|
||||
// declare let __webpack_public_path__: string;
|
||||
// declare let __webpack_nonce__: string;
|
||||
|
||||
// This is a path to the public folder without '/build'
|
||||
window.__grafana_public_path__ =
|
||||
__webpack_public_path__.substring(0, __webpack_public_path__.lastIndexOf('build/')) || __webpack_public_path__;
|
||||
// // Check if we are hosting files on cdn and set webpack public path
|
||||
// if (window.public_cdn_path) {
|
||||
// __webpack_public_path__ = window.public_cdn_path;
|
||||
// }
|
||||
|
||||
if (window.nonce) {
|
||||
__webpack_nonce__ = window.nonce;
|
||||
}
|
||||
// // This is a path to the public folder without '/build'
|
||||
// window.__grafana_public_path__ =
|
||||
// __webpack_public_path__.substring(0, __webpack_public_path__.lastIndexOf('build/')) || __webpack_public_path__;
|
||||
|
||||
// if (window.nonce) {
|
||||
// __webpack_nonce__ = window.nonce;
|
||||
// }
|
||||
|
||||
// This is an indication to the window.onLoad failure check that the app bundle has loaded.
|
||||
window.__grafana_app_bundle_loaded = true;
|
||||
@@ -22,7 +25,7 @@ import app from './app';
|
||||
|
||||
const prepareInit = async () => {
|
||||
if (process.env.frontend_dev_mock_api) {
|
||||
return import('test/mock-api/worker').then((workerModule) => {
|
||||
return import('../test/mock-api/worker').then((workerModule) => {
|
||||
workerModule.default.start({ onUnhandledRequest: 'bypass' });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -37,7 +37,9 @@
|
||||
"@types/prismjs": "1.26.5",
|
||||
"@types/react": "18.3.18",
|
||||
"@types/react-dom": "18.3.5",
|
||||
"css-loader": "7.1.2",
|
||||
"react-select-event": "5.5.1",
|
||||
"style-loader": "4.0.0",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.7.3",
|
||||
"webpack": "5.97.1"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
const JSURL = require('jsurl');
|
||||
|
||||
export interface AwsUrl {
|
||||
end: string;
|
||||
start: string;
|
||||
@@ -29,5 +27,64 @@ export function getLogsEndpoint(region: string): string {
|
||||
export function encodeUrl(obj: AwsUrl, region: string): string {
|
||||
return `https://${getLogsEndpoint(
|
||||
region
|
||||
)}/cloudwatch/home?region=${region}#logs-insights:queryDetail=${JSURL.stringify(obj)}`;
|
||||
)}/cloudwatch/home?region=${region}#logs-insights:queryDetail=${stringify(obj)}`;
|
||||
}
|
||||
|
||||
// Lifted from https://github.com/Sage/jsurl
|
||||
function stringify<T>(v: T): string {
|
||||
function encode(s: string): string {
|
||||
return !/[^\w-.]/.test(s)
|
||||
? s
|
||||
: s.replace(/[^\w-.]/g, (ch: string): string => {
|
||||
if (ch === '$') {
|
||||
return '!';
|
||||
}
|
||||
const charCode = ch.charCodeAt(0);
|
||||
// Thanks to Douglas Crockford for the negative slice trick
|
||||
return charCode < 0x100
|
||||
? '*' + ('00' + charCode.toString(16)).slice(-2)
|
||||
: '**' + ('0000' + charCode.toString(16)).slice(-4);
|
||||
});
|
||||
}
|
||||
|
||||
let tmpAry;
|
||||
|
||||
switch (typeof v) {
|
||||
case 'number':
|
||||
return isFinite(v) ? '~' + v : '~null';
|
||||
case 'boolean':
|
||||
return '~' + v;
|
||||
case 'string':
|
||||
return "~'" + encode(v);
|
||||
case 'object':
|
||||
if (!v) {
|
||||
return '~null';
|
||||
}
|
||||
|
||||
tmpAry = [];
|
||||
|
||||
if (Array.isArray(v)) {
|
||||
for (let i = 0; i < v.length; i++) {
|
||||
tmpAry[i] = stringify(v[i]) || '~null';
|
||||
}
|
||||
|
||||
return '~(' + (tmpAry.join('') || '~') + ')';
|
||||
} else {
|
||||
for (const key in v) {
|
||||
if (v.hasOwnProperty(key)) {
|
||||
const val = stringify(v[key]);
|
||||
|
||||
// skip undefined and functions
|
||||
if (val) {
|
||||
tmpAry.push(encode(key) + val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '~(' + tmpAry.join('~') + ')';
|
||||
}
|
||||
default:
|
||||
// function, undefined
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +52,10 @@
|
||||
"@types/react-dom": "18.3.5",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/uuid": "10.0.0",
|
||||
"css-loader": "7.1.2",
|
||||
"glob": "11.0.1",
|
||||
"react-select-event": "5.5.1",
|
||||
"style-loader": "4.0.0",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.7.3",
|
||||
"webpack": "5.97.1"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { isString, escape } from 'lodash';
|
||||
|
||||
import coreModule from 'app/angular/core_module';
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'vendor/flot/jquery.flot.crosshair';
|
||||
import 'vendor/flot/jquery.flot.dashes';
|
||||
import './jquery.flot.events';
|
||||
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { clone, find, flatten, isUndefined, map, max as _max, min as _min, sortBy as _sortBy, toNumber } from 'lodash';
|
||||
import { createElement } from 'react';
|
||||
import { createRoot, Root } from 'react-dom/client';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
|
||||
import {
|
||||
textUtil,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { partition, each } from 'lodash';
|
||||
//@ts-ignore
|
||||
import Drop from 'tether-drop';
|
||||
|
||||
import { CreatePlotOverlay } from '@grafana/data';
|
||||
import { getLegacyAngularInjector } from '@grafana/runtime';
|
||||
|
||||
// @ts-ignore
|
||||
import Drop from '../../../../vendor/tether-drop';
|
||||
|
||||
const createAnnotationToolip: CreatePlotOverlay = (element, event, plot) => {
|
||||
const injector = getLegacyAngularInjector();
|
||||
const content = document.createElement('div');
|
||||
@@ -26,6 +27,7 @@ const createAnnotationToolip: CreatePlotOverlay = (element, event, plot) => {
|
||||
tmpScope.$digest();
|
||||
tmpScope.$destroy();
|
||||
|
||||
// @ts-ignore
|
||||
const drop = new Drop({
|
||||
target: element[0],
|
||||
content: content,
|
||||
@@ -86,7 +88,7 @@ const createEditPopover: CreatePlotOverlay = (element, event, plot) => {
|
||||
|
||||
$compile(content)(scope);
|
||||
scope.$digest();
|
||||
|
||||
// @ts-ignore
|
||||
drop = new Drop({
|
||||
target: markerElementToAttachTo[0],
|
||||
content: content,
|
||||
|
||||
@@ -5,10 +5,13 @@ exports[`Graph DataProcessor getTimeSeries from LegacyResponseData Should return
|
||||
TimeSeries {
|
||||
"alias": "Value",
|
||||
"aliasEscaped": "Value",
|
||||
"allIsNull": undefined,
|
||||
"allIsZero": undefined,
|
||||
"bars": {
|
||||
"fillColor": "#7EB26D",
|
||||
},
|
||||
"color": "#7EB26D",
|
||||
"dashes": undefined,
|
||||
"dataFrameIndex": 0,
|
||||
"datapoints": [
|
||||
[
|
||||
@@ -24,22 +27,38 @@ exports[`Graph DataProcessor getTimeSeries from LegacyResponseData Should return
|
||||
1003,
|
||||
],
|
||||
],
|
||||
"decimals": undefined,
|
||||
"fieldIndex": 1,
|
||||
"fillBelowTo": undefined,
|
||||
"flotpairs": undefined,
|
||||
"hasMsResolution": false,
|
||||
"hiddenSeries": undefined,
|
||||
"hideTooltip": undefined,
|
||||
"id": "Value",
|
||||
"isOutsideRange": undefined,
|
||||
"label": "Value",
|
||||
"legend": true,
|
||||
"lines": undefined,
|
||||
"nullPointMode": undefined,
|
||||
"points": undefined,
|
||||
"stack": undefined,
|
||||
"stats": {},
|
||||
"transform": undefined,
|
||||
"unit": "watt",
|
||||
"valueFormater": [Function],
|
||||
"yaxis": undefined,
|
||||
"zindex": undefined,
|
||||
},
|
||||
TimeSeries {
|
||||
"alias": "table_data v1",
|
||||
"aliasEscaped": "table_data v1",
|
||||
"allIsNull": undefined,
|
||||
"allIsZero": undefined,
|
||||
"bars": {
|
||||
"fillColor": "#EAB839",
|
||||
},
|
||||
"color": "#EAB839",
|
||||
"dashes": undefined,
|
||||
"dataFrameIndex": 1,
|
||||
"datapoints": [
|
||||
[
|
||||
@@ -55,22 +74,38 @@ exports[`Graph DataProcessor getTimeSeries from LegacyResponseData Should return
|
||||
1003,
|
||||
],
|
||||
],
|
||||
"decimals": undefined,
|
||||
"fieldIndex": 1,
|
||||
"fillBelowTo": undefined,
|
||||
"flotpairs": undefined,
|
||||
"hasMsResolution": false,
|
||||
"hiddenSeries": undefined,
|
||||
"hideTooltip": undefined,
|
||||
"id": "table_data v1",
|
||||
"isOutsideRange": undefined,
|
||||
"label": "table_data v1",
|
||||
"legend": true,
|
||||
"lines": undefined,
|
||||
"nullPointMode": undefined,
|
||||
"points": undefined,
|
||||
"stack": undefined,
|
||||
"stats": {},
|
||||
"transform": undefined,
|
||||
"unit": "ohm",
|
||||
"valueFormater": [Function],
|
||||
"yaxis": undefined,
|
||||
"zindex": undefined,
|
||||
},
|
||||
TimeSeries {
|
||||
"alias": "table_data v2",
|
||||
"aliasEscaped": "table_data v2",
|
||||
"allIsNull": undefined,
|
||||
"allIsZero": undefined,
|
||||
"bars": {
|
||||
"fillColor": "#6ED0E0",
|
||||
},
|
||||
"color": "#6ED0E0",
|
||||
"dashes": undefined,
|
||||
"dataFrameIndex": 1,
|
||||
"datapoints": [
|
||||
[
|
||||
@@ -86,22 +121,38 @@ exports[`Graph DataProcessor getTimeSeries from LegacyResponseData Should return
|
||||
1003,
|
||||
],
|
||||
],
|
||||
"decimals": undefined,
|
||||
"fieldIndex": 2,
|
||||
"fillBelowTo": undefined,
|
||||
"flotpairs": undefined,
|
||||
"hasMsResolution": false,
|
||||
"hiddenSeries": undefined,
|
||||
"hideTooltip": undefined,
|
||||
"id": "table_data v2",
|
||||
"isOutsideRange": undefined,
|
||||
"label": "table_data v2",
|
||||
"legend": true,
|
||||
"lines": undefined,
|
||||
"nullPointMode": undefined,
|
||||
"points": undefined,
|
||||
"stack": undefined,
|
||||
"stats": {},
|
||||
"transform": undefined,
|
||||
"unit": undefined,
|
||||
"valueFormater": [Function],
|
||||
"yaxis": undefined,
|
||||
"zindex": undefined,
|
||||
},
|
||||
TimeSeries {
|
||||
"alias": "series v1",
|
||||
"aliasEscaped": "series v1",
|
||||
"allIsNull": undefined,
|
||||
"allIsZero": undefined,
|
||||
"bars": {
|
||||
"fillColor": "#EF843C",
|
||||
},
|
||||
"color": "#EF843C",
|
||||
"dashes": undefined,
|
||||
"dataFrameIndex": 2,
|
||||
"datapoints": [
|
||||
[
|
||||
@@ -117,22 +168,38 @@ exports[`Graph DataProcessor getTimeSeries from LegacyResponseData Should return
|
||||
1003,
|
||||
],
|
||||
],
|
||||
"decimals": undefined,
|
||||
"fieldIndex": 0,
|
||||
"fillBelowTo": undefined,
|
||||
"flotpairs": undefined,
|
||||
"hasMsResolution": false,
|
||||
"hiddenSeries": undefined,
|
||||
"hideTooltip": undefined,
|
||||
"id": "series v1",
|
||||
"isOutsideRange": undefined,
|
||||
"label": "series v1",
|
||||
"legend": true,
|
||||
"lines": undefined,
|
||||
"nullPointMode": undefined,
|
||||
"points": undefined,
|
||||
"stack": undefined,
|
||||
"stats": {},
|
||||
"transform": undefined,
|
||||
"unit": undefined,
|
||||
"valueFormater": [Function],
|
||||
"yaxis": undefined,
|
||||
"zindex": undefined,
|
||||
},
|
||||
TimeSeries {
|
||||
"alias": "series v2",
|
||||
"aliasEscaped": "series v2",
|
||||
"allIsNull": undefined,
|
||||
"allIsZero": undefined,
|
||||
"bars": {
|
||||
"fillColor": "#E24D42",
|
||||
},
|
||||
"color": "#E24D42",
|
||||
"dashes": undefined,
|
||||
"dataFrameIndex": 2,
|
||||
"datapoints": [
|
||||
[
|
||||
@@ -148,22 +215,38 @@ exports[`Graph DataProcessor getTimeSeries from LegacyResponseData Should return
|
||||
1003,
|
||||
],
|
||||
],
|
||||
"decimals": undefined,
|
||||
"fieldIndex": 1,
|
||||
"fillBelowTo": undefined,
|
||||
"flotpairs": undefined,
|
||||
"hasMsResolution": false,
|
||||
"hiddenSeries": undefined,
|
||||
"hideTooltip": undefined,
|
||||
"id": "series v2",
|
||||
"isOutsideRange": undefined,
|
||||
"label": "series v2",
|
||||
"legend": true,
|
||||
"lines": undefined,
|
||||
"nullPointMode": undefined,
|
||||
"points": undefined,
|
||||
"stack": undefined,
|
||||
"stats": {},
|
||||
"transform": undefined,
|
||||
"unit": undefined,
|
||||
"valueFormater": [Function],
|
||||
"yaxis": undefined,
|
||||
"zindex": undefined,
|
||||
},
|
||||
TimeSeries {
|
||||
"alias": "series with time as strings v1",
|
||||
"aliasEscaped": "series with time as strings v1",
|
||||
"allIsNull": undefined,
|
||||
"allIsZero": undefined,
|
||||
"bars": {
|
||||
"fillColor": "#1F78C1",
|
||||
},
|
||||
"color": "#1F78C1",
|
||||
"dashes": undefined,
|
||||
"dataFrameIndex": 3,
|
||||
"datapoints": [
|
||||
[
|
||||
@@ -179,14 +262,27 @@ exports[`Graph DataProcessor getTimeSeries from LegacyResponseData Should return
|
||||
1609466400000,
|
||||
],
|
||||
],
|
||||
"decimals": undefined,
|
||||
"fieldIndex": 0,
|
||||
"fillBelowTo": undefined,
|
||||
"flotpairs": undefined,
|
||||
"hasMsResolution": false,
|
||||
"hiddenSeries": undefined,
|
||||
"hideTooltip": undefined,
|
||||
"id": "series with time as strings v1",
|
||||
"isOutsideRange": undefined,
|
||||
"label": "series with time as strings v1",
|
||||
"legend": true,
|
||||
"lines": undefined,
|
||||
"nullPointMode": undefined,
|
||||
"points": undefined,
|
||||
"stack": undefined,
|
||||
"stats": {},
|
||||
"transform": undefined,
|
||||
"unit": undefined,
|
||||
"valueFormater": [Function],
|
||||
"yaxis": undefined,
|
||||
"zindex": undefined,
|
||||
},
|
||||
]
|
||||
`;
|
||||
@@ -196,10 +292,13 @@ exports[`Graph DataProcessor getTimeSeries from LegacyResponseData Should return
|
||||
TimeSeries {
|
||||
"alias": "Count",
|
||||
"aliasEscaped": "Count",
|
||||
"allIsNull": undefined,
|
||||
"allIsZero": undefined,
|
||||
"bars": {
|
||||
"fillColor": "#7EB26D",
|
||||
},
|
||||
"color": "#7EB26D",
|
||||
"dashes": undefined,
|
||||
"dataFrameIndex": 0,
|
||||
"datapoints": [
|
||||
[
|
||||
@@ -275,14 +374,27 @@ exports[`Graph DataProcessor getTimeSeries from LegacyResponseData Should return
|
||||
1609466400000,
|
||||
],
|
||||
],
|
||||
"decimals": undefined,
|
||||
"fieldIndex": 1,
|
||||
"fillBelowTo": undefined,
|
||||
"flotpairs": undefined,
|
||||
"hasMsResolution": false,
|
||||
"hiddenSeries": undefined,
|
||||
"hideTooltip": undefined,
|
||||
"id": "Value",
|
||||
"isOutsideRange": undefined,
|
||||
"label": "Value",
|
||||
"legend": true,
|
||||
"lines": undefined,
|
||||
"nullPointMode": undefined,
|
||||
"points": undefined,
|
||||
"stack": undefined,
|
||||
"stats": {},
|
||||
"transform": undefined,
|
||||
"unit": "watt",
|
||||
"valueFormater": [Function],
|
||||
"yaxis": undefined,
|
||||
"zindex": undefined,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'vendor/flot/jquery.flot';
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { isNumber } from 'lodash';
|
||||
|
||||
import { PanelCtrl } from 'app/angular/panel/panel_ctrl';
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { CorsWorker as Worker } from 'app/core/utils/CorsWorker';
|
||||
import { corsWorker } from 'app/core/utils/CorsWorker';
|
||||
|
||||
export const createWorker = () => new Worker(new URL('./layout.worker.js', import.meta.url));
|
||||
export const createMsaglWorker = () => new Worker(new URL('./layeredLayout.worker.js', import.meta.url));
|
||||
import layeredLayoutWorkerUrl from './layeredLayout.worker?worker&url';
|
||||
import layoutWorkerUrl from './layout.worker?worker&url';
|
||||
|
||||
export const createWorker = () => corsWorker(layoutWorkerUrl, { name: 'nodeGraphLayout' });
|
||||
export const createMsaglWorker = () => corsWorker(layeredLayoutWorkerUrl, { name: 'nodeGraphLayeredLayout' });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IScope, IAngularStatic } from 'angular';
|
||||
import $ from 'jquery';
|
||||
import 'jquery';
|
||||
import { defaults } from 'lodash';
|
||||
|
||||
import { isTableData, PanelEvents, PanelPlugin } from '@grafana/data';
|
||||
|
||||
2
public/app/types/vite.d.ts
vendored
Normal file
2
public/app/types/vite.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="vite/types/importMeta" />
|
||||
5
public/app/viteGlobals.ts
Normal file
5
public/app/viteGlobals.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// Vite: hacky approach to making jQuery globally available
|
||||
import jQuery from 'jquery';
|
||||
Object.assign(window, { $: jQuery, jQuery });
|
||||
|
||||
window.global ||= window;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CorsWorker as Worker } from 'app/core/utils/CorsWorker';
|
||||
import kustoWorkerUrl from '@kusto/monaco-kusto/release/esm/kusto.worker?worker&url';
|
||||
|
||||
export default function loadKusto() {
|
||||
return new Worker(new URL('@kusto/monaco-kusto/release/esm/kusto.worker', import.meta.url));
|
||||
return kustoWorkerUrl;
|
||||
}
|
||||
|
||||
@@ -7,11 +7,11 @@ import i18next from 'i18next';
|
||||
import failOnConsole from 'jest-fail-on-console';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
import getEnvConfig from '../../scripts/webpack/env-util';
|
||||
import getEnvConfig from '../../scripts/webpack/env-util.cjs';
|
||||
|
||||
import { matchers } from './matchers';
|
||||
|
||||
const config = getEnvConfig() as Record<string, string | boolean>;
|
||||
const config = getEnvConfig() as Record<`frontend_dev_${string}`, unknown>;
|
||||
|
||||
if (config.frontend_dev_fail_tests_on_console || process.env.CI) {
|
||||
failOnConsole({
|
||||
@@ -34,6 +34,9 @@ i18next.use(initReactI18next).init({
|
||||
// the factory uses import.meta.url so we can't use it in CommonJS modules.
|
||||
jest.mock('app/features/dashboard-scene/saving/createDetectChangesWorker.ts');
|
||||
|
||||
// mock extension internationalization as it uses import.meta.glob which we can't use in CommonJS modules.
|
||||
jest.mock('app/core/internationalization/extensions.ts');
|
||||
|
||||
// our tests are heavy in CI due to parallelisation and monaco and kusto
|
||||
// so we increase the default timeout to 2secs to avoid flakiness
|
||||
configure({ asyncUtilTimeout: 2000 });
|
||||
|
||||
495
public/vendor/tether-drop/index.js
vendored
Normal file
495
public/vendor/tether-drop/index.js
vendored
Normal file
@@ -0,0 +1,495 @@
|
||||
import {
|
||||
extend,
|
||||
addClass,
|
||||
removeClass,
|
||||
hasClass,
|
||||
Evented,
|
||||
} from "./tetherUtils";
|
||||
|
||||
function sortAttach(str) {
|
||||
let [first, second] = str.split(" ");
|
||||
if (["left", "right"].indexOf(first) >= 0) {
|
||||
[first, second] = [second, first];
|
||||
}
|
||||
return [first, second].join(" ");
|
||||
}
|
||||
|
||||
function removeFromArray(arr, item) {
|
||||
let index;
|
||||
let results = [];
|
||||
while ((index = arr.indexOf(item)) !== -1) {
|
||||
results.push(arr.splice(index, 1));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
let clickEvents = ["click"];
|
||||
if ("ontouchstart" in document.documentElement) {
|
||||
clickEvents.push("touchstart");
|
||||
}
|
||||
|
||||
const transitionEndEvents = {
|
||||
WebkitTransition: "webkitTransitionEnd",
|
||||
MozTransition: "transitionend",
|
||||
OTransition: "otransitionend",
|
||||
transition: "transitionend",
|
||||
};
|
||||
|
||||
let transitionEndEvent = "";
|
||||
for (let name in transitionEndEvents) {
|
||||
if ({}.hasOwnProperty.call(transitionEndEvents, name)) {
|
||||
let tempEl = document.createElement("p");
|
||||
if (typeof tempEl.style[name] !== "undefined") {
|
||||
transitionEndEvent = transitionEndEvents[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MIRROR_ATTACH = {
|
||||
left: "right",
|
||||
right: "left",
|
||||
top: "bottom",
|
||||
bottom: "top",
|
||||
middle: "middle",
|
||||
center: "center",
|
||||
};
|
||||
|
||||
let allDrops = {};
|
||||
|
||||
// Drop can be included in external libraries. Calling createContext gives you a fresh
|
||||
// copy of drop which won't interact with other copies on the page (beyond calling the document events).
|
||||
|
||||
function createContext(options = {}) {
|
||||
let drop = (...args) => new DropInstance(...args);
|
||||
|
||||
extend(drop, {
|
||||
createContext: createContext,
|
||||
drops: [],
|
||||
defaults: {},
|
||||
});
|
||||
|
||||
const defaultOptions = {
|
||||
classPrefix: "drop",
|
||||
defaults: {
|
||||
position: "bottom left",
|
||||
openOn: "click",
|
||||
beforeClose: null,
|
||||
constrainToScrollParent: true,
|
||||
constrainToWindow: true,
|
||||
classes: "",
|
||||
remove: false,
|
||||
openDelay: 0,
|
||||
closeDelay: 50,
|
||||
// inherited from openDelay and closeDelay if not explicitly defined
|
||||
focusDelay: null,
|
||||
blurDelay: null,
|
||||
hoverOpenDelay: null,
|
||||
hoverCloseDelay: null,
|
||||
tetherOptions: {},
|
||||
},
|
||||
};
|
||||
|
||||
extend(drop, defaultOptions, options);
|
||||
extend(drop.defaults, defaultOptions.defaults, options.defaults);
|
||||
|
||||
if (typeof allDrops[drop.classPrefix] === "undefined") {
|
||||
allDrops[drop.classPrefix] = [];
|
||||
}
|
||||
|
||||
drop.updateBodyClasses = () => {
|
||||
// There is only one body, so despite the context concept, we still iterate through all
|
||||
// drops which share our classPrefix.
|
||||
|
||||
let anyOpen = false;
|
||||
const drops = allDrops[drop.classPrefix];
|
||||
const len = drops.length;
|
||||
for (let i = 0; i < len; ++i) {
|
||||
if (drops[i].isOpened()) {
|
||||
anyOpen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (anyOpen) {
|
||||
addClass(document.body, `${drop.classPrefix}-open`);
|
||||
} else {
|
||||
removeClass(document.body, `${drop.classPrefix}-open`);
|
||||
}
|
||||
};
|
||||
|
||||
class DropInstance extends Evented {
|
||||
constructor(opts) {
|
||||
super();
|
||||
this.options = extend({}, drop.defaults, opts);
|
||||
this.target = this.options.target;
|
||||
|
||||
if (typeof this.target === "undefined") {
|
||||
throw new Error("Drop Error: You must provide a target.");
|
||||
}
|
||||
|
||||
const dataPrefix = `data-${drop.classPrefix}`;
|
||||
|
||||
const contentAttr = this.target.getAttribute(dataPrefix);
|
||||
if (contentAttr && this.options.content == null) {
|
||||
this.options.content = contentAttr;
|
||||
}
|
||||
|
||||
const attrsOverride = ["position", "openOn"];
|
||||
for (let i = 0; i < attrsOverride.length; ++i) {
|
||||
const override = this.target.getAttribute(
|
||||
`${dataPrefix}-${attrsOverride[i]}`
|
||||
);
|
||||
if (override && this.options[attrsOverride[i]] == null) {
|
||||
this.options[attrsOverride[i]] = override;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.classes && this.options.addTargetClasses !== false) {
|
||||
addClass(this.target, this.options.classes);
|
||||
}
|
||||
|
||||
drop.drops.push(this);
|
||||
allDrops[drop.classPrefix].push(this);
|
||||
|
||||
this._boundEvents = [];
|
||||
this.bindMethods();
|
||||
this.setupElements();
|
||||
this.setupEvents();
|
||||
this.setupTether();
|
||||
}
|
||||
|
||||
_on(element, event, handler) {
|
||||
this._boundEvents.push({ element, event, handler });
|
||||
element.addEventListener(event, handler);
|
||||
}
|
||||
|
||||
bindMethods() {
|
||||
this.transitionEndHandler = this._transitionEndHandler.bind(this);
|
||||
}
|
||||
|
||||
setupElements() {
|
||||
this.drop = document.createElement("div");
|
||||
addClass(this.drop, drop.classPrefix);
|
||||
|
||||
if (this.options.classes) {
|
||||
addClass(this.drop, this.options.classes);
|
||||
}
|
||||
|
||||
this.content = document.createElement("div");
|
||||
addClass(this.content, `${drop.classPrefix}-content`);
|
||||
|
||||
if (typeof this.options.content === "function") {
|
||||
const generateAndSetContent = () => {
|
||||
// content function might return a string or an element
|
||||
const contentElementOrHTML = this.options.content.call(this, this);
|
||||
|
||||
if (typeof contentElementOrHTML === "string") {
|
||||
this.content.innerHTML = contentElementOrHTML;
|
||||
} else if (typeof contentElementOrHTML === "object") {
|
||||
this.content.innerHTML = "";
|
||||
this.content.appendChild(contentElementOrHTML);
|
||||
} else {
|
||||
throw new Error(
|
||||
"Drop Error: Content function should return a string or HTMLElement."
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
this.on("beforeOpen", generateAndSetContent.bind(this));
|
||||
} else if (typeof this.options.content === "object") {
|
||||
this.content.appendChild(this.options.content);
|
||||
} else {
|
||||
this.content.innerHTML = this.options.content;
|
||||
}
|
||||
|
||||
this.drop.appendChild(this.content);
|
||||
}
|
||||
|
||||
setupTether() {
|
||||
// Tether expects two attachment points, one in the target element, one in the
|
||||
// drop. We use a single one, and use the order as well, to allow us to put
|
||||
// the drop on either side of any of the four corners. This magic converts between
|
||||
// the two:
|
||||
let dropAttach = this.options.position.split(" ");
|
||||
dropAttach[0] = MIRROR_ATTACH[dropAttach[0]];
|
||||
dropAttach = dropAttach.join(" ");
|
||||
|
||||
let constraints = [];
|
||||
if (this.options.constrainToScrollParent) {
|
||||
constraints.push({
|
||||
to: "scrollParent",
|
||||
pin: "top, bottom",
|
||||
attachment: "together none",
|
||||
});
|
||||
} else {
|
||||
// To get 'out of bounds' classes
|
||||
constraints.push({
|
||||
to: "scrollParent",
|
||||
});
|
||||
}
|
||||
|
||||
if (this.options.constrainToWindow !== false) {
|
||||
constraints.push({
|
||||
to: "window",
|
||||
attachment: "together",
|
||||
});
|
||||
} else {
|
||||
// To get 'out of bounds' classes
|
||||
constraints.push({
|
||||
to: "window",
|
||||
});
|
||||
}
|
||||
|
||||
const opts = {
|
||||
element: this.drop,
|
||||
target: this.target,
|
||||
attachment: sortAttach(dropAttach),
|
||||
targetAttachment: sortAttach(this.options.position),
|
||||
classPrefix: drop.classPrefix,
|
||||
offset: "0 0",
|
||||
targetOffset: "0 0",
|
||||
enabled: false,
|
||||
constraints: constraints,
|
||||
addTargetClasses: this.options.addTargetClasses,
|
||||
};
|
||||
|
||||
if (this.options.tetherOptions !== false) {
|
||||
this.tether = new Tether(extend({}, opts, this.options.tetherOptions));
|
||||
}
|
||||
}
|
||||
|
||||
setupEvents() {
|
||||
if (!this.options.openOn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.options.openOn === "always") {
|
||||
setTimeout(this.open.bind(this));
|
||||
return;
|
||||
}
|
||||
|
||||
const events = this.options.openOn.split(" ");
|
||||
|
||||
if (events.indexOf("click") >= 0) {
|
||||
const openHandler = (event) => {
|
||||
this.toggle(event);
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const closeHandler = (event) => {
|
||||
if (!this.isOpened()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clicking inside dropdown
|
||||
if (event.target === this.drop || this.drop.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clicking target
|
||||
if (
|
||||
event.target === this.target ||
|
||||
this.target.contains(event.target)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.close(event);
|
||||
};
|
||||
|
||||
for (let i = 0; i < clickEvents.length; ++i) {
|
||||
const clickEvent = clickEvents[i];
|
||||
this._on(this.target, clickEvent, openHandler);
|
||||
this._on(document, clickEvent, closeHandler);
|
||||
}
|
||||
}
|
||||
|
||||
let inTimeout = null;
|
||||
let outTimeout = null;
|
||||
|
||||
const inHandler = (event) => {
|
||||
if (outTimeout !== null) {
|
||||
clearTimeout(outTimeout);
|
||||
} else {
|
||||
inTimeout = setTimeout(() => {
|
||||
this.open(event);
|
||||
inTimeout = null;
|
||||
}, (event.type === "focus" ? this.options.focusDelay : this.options.hoverOpenDelay) || this.options.openDelay);
|
||||
}
|
||||
};
|
||||
|
||||
const outHandler = (event) => {
|
||||
if (inTimeout !== null) {
|
||||
clearTimeout(inTimeout);
|
||||
} else {
|
||||
outTimeout = setTimeout(() => {
|
||||
this.close(event);
|
||||
outTimeout = null;
|
||||
}, (event.type === "blur" ? this.options.blurDelay : this.options.hoverCloseDelay) || this.options.closeDelay);
|
||||
}
|
||||
};
|
||||
|
||||
if (events.indexOf("hover") >= 0) {
|
||||
this._on(this.target, "mouseover", inHandler);
|
||||
this._on(this.drop, "mouseover", inHandler);
|
||||
this._on(this.target, "mouseout", outHandler);
|
||||
this._on(this.drop, "mouseout", outHandler);
|
||||
}
|
||||
|
||||
if (events.indexOf("focus") >= 0) {
|
||||
this._on(this.target, "focus", inHandler);
|
||||
this._on(this.drop, "focus", inHandler);
|
||||
this._on(this.target, "blur", outHandler);
|
||||
this._on(this.drop, "blur", outHandler);
|
||||
}
|
||||
}
|
||||
|
||||
isOpened() {
|
||||
if (this.drop) {
|
||||
return hasClass(this.drop, `${drop.classPrefix}-open`);
|
||||
}
|
||||
}
|
||||
|
||||
toggle(event) {
|
||||
if (this.isOpened()) {
|
||||
this.close(event);
|
||||
} else {
|
||||
this.open(event);
|
||||
}
|
||||
}
|
||||
|
||||
open(event) {
|
||||
/* eslint no-unused-vars: 0 */
|
||||
if (this.isOpened()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.drop.parentNode) {
|
||||
document.body.appendChild(this.drop);
|
||||
}
|
||||
|
||||
if (typeof this.tether !== "undefined") {
|
||||
this.tether.enable();
|
||||
}
|
||||
|
||||
addClass(this.drop, `${drop.classPrefix}-open`);
|
||||
addClass(this.drop, `${drop.classPrefix}-open-transitionend`);
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.drop) {
|
||||
addClass(this.drop, `${drop.classPrefix}-after-open`);
|
||||
}
|
||||
});
|
||||
|
||||
this.trigger("beforeOpen");
|
||||
|
||||
if (typeof this.tether !== "undefined") {
|
||||
this.tether.position();
|
||||
}
|
||||
|
||||
this.trigger("open");
|
||||
|
||||
drop.updateBodyClasses();
|
||||
}
|
||||
|
||||
_transitionEndHandler(e) {
|
||||
if (e.target !== e.currentTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasClass(this.drop, `${drop.classPrefix}-open`)) {
|
||||
removeClass(this.drop, `${drop.classPrefix}-open-transitionend`);
|
||||
}
|
||||
this.drop.removeEventListener(
|
||||
transitionEndEvent,
|
||||
this.transitionEndHandler
|
||||
);
|
||||
}
|
||||
|
||||
beforeCloseHandler(event) {
|
||||
let shouldClose = true;
|
||||
|
||||
if (!this.isClosing && typeof this.options.beforeClose === "function") {
|
||||
this.isClosing = true;
|
||||
shouldClose = this.options.beforeClose(event, this) !== false;
|
||||
}
|
||||
|
||||
this.isClosing = false;
|
||||
|
||||
return shouldClose;
|
||||
}
|
||||
|
||||
close(event) {
|
||||
if (!this.isOpened()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.beforeCloseHandler(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeClass(this.drop, `${drop.classPrefix}-open`);
|
||||
removeClass(this.drop, `${drop.classPrefix}-after-open`);
|
||||
|
||||
this.drop.addEventListener(transitionEndEvent, this.transitionEndHandler);
|
||||
|
||||
this.trigger("close");
|
||||
|
||||
if (typeof this.tether !== "undefined") {
|
||||
this.tether.disable();
|
||||
}
|
||||
|
||||
drop.updateBodyClasses();
|
||||
|
||||
if (this.options.remove) {
|
||||
this.remove(event);
|
||||
}
|
||||
}
|
||||
|
||||
remove(event) {
|
||||
this.close(event);
|
||||
if (this.drop.parentNode) {
|
||||
this.drop.parentNode.removeChild(this.drop);
|
||||
}
|
||||
}
|
||||
|
||||
position() {
|
||||
if (this.isOpened() && typeof this.tether !== "undefined") {
|
||||
this.tether.position();
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.remove();
|
||||
|
||||
if (typeof this.tether !== "undefined") {
|
||||
this.tether.destroy();
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._boundEvents.length; ++i) {
|
||||
const { element, event, handler } = this._boundEvents[i];
|
||||
element.removeEventListener(event, handler);
|
||||
}
|
||||
|
||||
this._boundEvents = [];
|
||||
|
||||
this.tether = null;
|
||||
this.drop = null;
|
||||
this.content = null;
|
||||
this.target = null;
|
||||
|
||||
removeFromArray(allDrops[drop.classPrefix], this);
|
||||
removeFromArray(drop.drops, this);
|
||||
}
|
||||
}
|
||||
|
||||
return drop;
|
||||
}
|
||||
|
||||
const Drop = createContext();
|
||||
|
||||
export default Drop;
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
Drop.updateBodyClasses();
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user