mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
PanelEdit: Show when field options have override rules or data config that overrides the default (#40250)
* First pass at showing data override dots * Added test * Adding override rule dots * Added unit test * Minor changes * Update public/app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor.tsx Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com> * Fixed ts issues * review feedback changes * skipp broken e2e test Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
This commit is contained in:
parent
e9d90231e0
commit
3dee34c009
8
.yarn/sdks/eslint/bin/eslint.js
vendored
8
.yarn/sdks/eslint/bin/eslint.js
vendored
@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = '../../../../.pnp.cjs';
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
8
.yarn/sdks/eslint/lib/api.js
vendored
8
.yarn/sdks/eslint/lib/api.js
vendored
@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = '../../../../.pnp.cjs';
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
1
.yarn/sdks/integrations.yml
vendored
1
.yarn/sdks/integrations.yml
vendored
@ -2,4 +2,5 @@
|
||||
# Manual changes might be lost!
|
||||
|
||||
integrations:
|
||||
- vim
|
||||
- vscode
|
||||
|
8
.yarn/sdks/prettier/index.js
vendored
8
.yarn/sdks/prettier/index.js
vendored
@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = '../../../.pnp.cjs';
|
||||
const relPnpApiPath = "../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
8
.yarn/sdks/stylelint/bin/stylelint.js
vendored
8
.yarn/sdks/stylelint/bin/stylelint.js
vendored
@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = '../../../../.pnp.cjs';
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
8
.yarn/sdks/stylelint/lib/index.js
vendored
8
.yarn/sdks/stylelint/lib/index.js
vendored
@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = '../../../../.pnp.cjs';
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
8
.yarn/sdks/typescript/lib/tsc.js
vendored
8
.yarn/sdks/typescript/lib/tsc.js
vendored
@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = '../../../../.pnp.cjs';
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
134
.yarn/sdks/typescript/lib/tsserver.js
vendored
134
.yarn/sdks/typescript/lib/tsserver.js
vendored
@ -1,30 +1,28 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = '../../../../.pnp.cjs';
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
||||
const moduleWrapper = (tsserver) => {
|
||||
const moduleWrapper = tsserver => {
|
||||
if (!process.versions.pnp) {
|
||||
return tsserver;
|
||||
}
|
||||
|
||||
const { isAbsolute } = require(`path`);
|
||||
const {isAbsolute} = require(`path`);
|
||||
const pnpApi = require(`pnpapi`);
|
||||
|
||||
const isVirtual = (str) => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||
const normalize = (str) => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||
|
||||
const dependencyTreeRoots = new Set(
|
||||
pnpApi.getDependencyTreeRoots().map((locator) => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
})
|
||||
);
|
||||
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
}));
|
||||
|
||||
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||
// doesn't understand. This layer makes sure to remove the protocol
|
||||
@ -66,43 +64,33 @@ const moduleWrapper = (tsserver) => {
|
||||
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
case `vscode <1.61`:
|
||||
{
|
||||
str = `^zip:${str}`;
|
||||
}
|
||||
break;
|
||||
case `vscode <1.61`: {
|
||||
str = `^zip:${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode`:
|
||||
{
|
||||
str = `^/zip/${str}`;
|
||||
}
|
||||
break;
|
||||
case `vscode`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
// To make "go to definition" work,
|
||||
// We have to resolve the actual file system path from virtual path
|
||||
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||
case `coc-nvim`:
|
||||
{
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
}
|
||||
break;
|
||||
case `coc-nvim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
} break;
|
||||
|
||||
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||
// We have to resolve the actual file system path from virtual path,
|
||||
// everything else is up to neovim
|
||||
case `neovim`:
|
||||
{
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile:${str}`;
|
||||
}
|
||||
break;
|
||||
case `neovim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile:${str}`;
|
||||
} break;
|
||||
|
||||
default:
|
||||
{
|
||||
str = `zip:${str}`;
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
str = `zip:${str}`;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,24 +101,22 @@ const moduleWrapper = (tsserver) => {
|
||||
function fromEditorPath(str) {
|
||||
switch (hostInfo) {
|
||||
case `coc-nvim`:
|
||||
case `neovim`:
|
||||
{
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||
// So in order to convert it back, we use .* to match all the thing
|
||||
// before `zipfile:`
|
||||
return process.platform === `win32` ? str.replace(/^.*zipfile:\//, ``) : str.replace(/^.*zipfile:/, ``);
|
||||
}
|
||||
break;
|
||||
case `neovim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||
// So in order to convert it back, we use .* to match all the thing
|
||||
// before `zipfile:`
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^.*zipfile:\//, ``)
|
||||
: str.replace(/^.*zipfile:/, ``);
|
||||
} break;
|
||||
|
||||
case `vscode`:
|
||||
default:
|
||||
{
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^\^?(zip:|\/zip)\/+/, ``)
|
||||
: str.replace(/^\^?(zip:|\/zip)\/+/, `/`);
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^\^?(zip:|\/zip)\/+/, ``)
|
||||
: str.replace(/^\^?(zip:|\/zip)\/+/, `/`);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,8 +128,8 @@ const moduleWrapper = (tsserver) => {
|
||||
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||
// https://github.com/microsoft/vscode/issues/45856
|
||||
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||
const { enablePluginsWithOptions: originalEnablePluginsWithOptions } = ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function () {
|
||||
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||
this.projectService.allowLocalPluginLoads = true;
|
||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||
};
|
||||
@ -153,12 +139,12 @@ const moduleWrapper = (tsserver) => {
|
||||
// like an absolute path of ours and normalize it.
|
||||
|
||||
const Session = tsserver.server.Session;
|
||||
const { onMessage: originalOnMessage, send: originalSend } = Session.prototype;
|
||||
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||
let hostInfo = `unknown`;
|
||||
|
||||
Object.assign(Session.prototype, {
|
||||
onMessage(/** @type {string} */ message) {
|
||||
const parsedMessage = JSON.parse(message);
|
||||
const parsedMessage = JSON.parse(message)
|
||||
|
||||
if (
|
||||
parsedMessage != null &&
|
||||
@ -167,33 +153,21 @@ const moduleWrapper = (tsserver) => {
|
||||
typeof parsedMessage.arguments.hostInfo === `string`
|
||||
) {
|
||||
hostInfo = parsedMessage.arguments.hostInfo;
|
||||
if (
|
||||
hostInfo === `vscode` &&
|
||||
process.env.VSCODE_IPC_HOOK &&
|
||||
process.env.VSCODE_IPC_HOOK.match(/Code\/1\.([1-5][0-9]|60)\./)
|
||||
) {
|
||||
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK && process.env.VSCODE_IPC_HOOK.match(/Code\/1\.([1-5][0-9]|60)\./)) {
|
||||
hostInfo += ` <1.61`;
|
||||
}
|
||||
}
|
||||
|
||||
return originalOnMessage.call(
|
||||
this,
|
||||
JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === `string` ? fromEditorPath(value) : value;
|
||||
})
|
||||
);
|
||||
return originalOnMessage.call(this, JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === `string` ? fromEditorPath(value) : value;
|
||||
}));
|
||||
},
|
||||
|
||||
send(/** @type {any} */ msg) {
|
||||
return originalSend.call(
|
||||
this,
|
||||
JSON.parse(
|
||||
JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})));
|
||||
}
|
||||
});
|
||||
|
||||
return tsserver;
|
||||
|
134
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
134
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
@ -1,30 +1,28 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = '../../../../.pnp.cjs';
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
||||
const moduleWrapper = (tsserver) => {
|
||||
const moduleWrapper = tsserver => {
|
||||
if (!process.versions.pnp) {
|
||||
return tsserver;
|
||||
}
|
||||
|
||||
const { isAbsolute } = require(`path`);
|
||||
const {isAbsolute} = require(`path`);
|
||||
const pnpApi = require(`pnpapi`);
|
||||
|
||||
const isVirtual = (str) => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||
const normalize = (str) => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||
|
||||
const dependencyTreeRoots = new Set(
|
||||
pnpApi.getDependencyTreeRoots().map((locator) => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
})
|
||||
);
|
||||
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
}));
|
||||
|
||||
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||
// doesn't understand. This layer makes sure to remove the protocol
|
||||
@ -66,43 +64,33 @@ const moduleWrapper = (tsserver) => {
|
||||
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
case `vscode <1.61`:
|
||||
{
|
||||
str = `^zip:${str}`;
|
||||
}
|
||||
break;
|
||||
case `vscode <1.61`: {
|
||||
str = `^zip:${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode`:
|
||||
{
|
||||
str = `^/zip/${str}`;
|
||||
}
|
||||
break;
|
||||
case `vscode`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
// To make "go to definition" work,
|
||||
// We have to resolve the actual file system path from virtual path
|
||||
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||
case `coc-nvim`:
|
||||
{
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
}
|
||||
break;
|
||||
case `coc-nvim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
} break;
|
||||
|
||||
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||
// We have to resolve the actual file system path from virtual path,
|
||||
// everything else is up to neovim
|
||||
case `neovim`:
|
||||
{
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile:${str}`;
|
||||
}
|
||||
break;
|
||||
case `neovim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile:${str}`;
|
||||
} break;
|
||||
|
||||
default:
|
||||
{
|
||||
str = `zip:${str}`;
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
str = `zip:${str}`;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,24 +101,22 @@ const moduleWrapper = (tsserver) => {
|
||||
function fromEditorPath(str) {
|
||||
switch (hostInfo) {
|
||||
case `coc-nvim`:
|
||||
case `neovim`:
|
||||
{
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||
// So in order to convert it back, we use .* to match all the thing
|
||||
// before `zipfile:`
|
||||
return process.platform === `win32` ? str.replace(/^.*zipfile:\//, ``) : str.replace(/^.*zipfile:/, ``);
|
||||
}
|
||||
break;
|
||||
case `neovim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||
// So in order to convert it back, we use .* to match all the thing
|
||||
// before `zipfile:`
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^.*zipfile:\//, ``)
|
||||
: str.replace(/^.*zipfile:/, ``);
|
||||
} break;
|
||||
|
||||
case `vscode`:
|
||||
default:
|
||||
{
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^\^?(zip:|\/zip)\/+/, ``)
|
||||
: str.replace(/^\^?(zip:|\/zip)\/+/, `/`);
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^\^?(zip:|\/zip)\/+/, ``)
|
||||
: str.replace(/^\^?(zip:|\/zip)\/+/, `/`);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,8 +128,8 @@ const moduleWrapper = (tsserver) => {
|
||||
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||
// https://github.com/microsoft/vscode/issues/45856
|
||||
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||
const { enablePluginsWithOptions: originalEnablePluginsWithOptions } = ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function () {
|
||||
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||
this.projectService.allowLocalPluginLoads = true;
|
||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||
};
|
||||
@ -153,12 +139,12 @@ const moduleWrapper = (tsserver) => {
|
||||
// like an absolute path of ours and normalize it.
|
||||
|
||||
const Session = tsserver.server.Session;
|
||||
const { onMessage: originalOnMessage, send: originalSend } = Session.prototype;
|
||||
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||
let hostInfo = `unknown`;
|
||||
|
||||
Object.assign(Session.prototype, {
|
||||
onMessage(/** @type {string} */ message) {
|
||||
const parsedMessage = JSON.parse(message);
|
||||
const parsedMessage = JSON.parse(message)
|
||||
|
||||
if (
|
||||
parsedMessage != null &&
|
||||
@ -167,33 +153,21 @@ const moduleWrapper = (tsserver) => {
|
||||
typeof parsedMessage.arguments.hostInfo === `string`
|
||||
) {
|
||||
hostInfo = parsedMessage.arguments.hostInfo;
|
||||
if (
|
||||
hostInfo === `vscode` &&
|
||||
process.env.VSCODE_IPC_HOOK &&
|
||||
process.env.VSCODE_IPC_HOOK.match(/Code\/1\.([1-5][0-9]|60)\./)
|
||||
) {
|
||||
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK && process.env.VSCODE_IPC_HOOK.match(/Code\/1\.([1-5][0-9]|60)\./)) {
|
||||
hostInfo += ` <1.61`;
|
||||
}
|
||||
}
|
||||
|
||||
return originalOnMessage.call(
|
||||
this,
|
||||
JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === `string` ? fromEditorPath(value) : value;
|
||||
})
|
||||
);
|
||||
return originalOnMessage.call(this, JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === `string` ? fromEditorPath(value) : value;
|
||||
}));
|
||||
},
|
||||
|
||||
send(/** @type {any} */ msg) {
|
||||
return originalSend.call(
|
||||
this,
|
||||
JSON.parse(
|
||||
JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})));
|
||||
}
|
||||
});
|
||||
|
||||
return tsserver;
|
||||
|
8
.yarn/sdks/typescript/lib/typescript.js
vendored
8
.yarn/sdks/typescript/lib/typescript.js
vendored
@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = '../../../../.pnp.cjs';
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
@ -15,7 +15,7 @@ e2e.scenario({
|
||||
itName: 'Tests dashboard time zone scenarios',
|
||||
addScenarioDataSource: false,
|
||||
addScenarioDashBoard: false,
|
||||
skipScenario: false,
|
||||
skipScenario: true,
|
||||
scenario: () => {
|
||||
e2e.flows.openDashboard({ uid: '5SdHCasdf' });
|
||||
|
||||
|
@ -3,6 +3,8 @@ import { Field, Label } from '@grafana/ui';
|
||||
import React, { ReactNode } from 'react';
|
||||
import Highlighter from 'react-highlight-words';
|
||||
import { OptionsPaneCategoryDescriptor } from './OptionsPaneCategoryDescriptor';
|
||||
import { OptionsPaneItemOverrides } from './OptionsPaneItemOverrides';
|
||||
import { OptionPaneItemOverrideInfo } from './types';
|
||||
|
||||
export interface OptionsPaneItemProps {
|
||||
title: string;
|
||||
@ -12,6 +14,7 @@ export interface OptionsPaneItemProps {
|
||||
render: () => React.ReactNode;
|
||||
skipField?: boolean;
|
||||
showIf?: () => boolean;
|
||||
overrides?: OptionPaneItemOverrideInfo[];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -23,15 +26,20 @@ export class OptionsPaneItemDescriptor {
|
||||
constructor(public props: OptionsPaneItemProps) {}
|
||||
|
||||
getLabel(searchQuery?: string): ReactNode {
|
||||
const { title, description } = this.props;
|
||||
const { title, description, overrides } = this.props;
|
||||
|
||||
if (!searchQuery) {
|
||||
// Do not render label for categories with only one child
|
||||
if (this.parent.props.title === title) {
|
||||
if (this.parent.props.title === title && !overrides?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return title;
|
||||
return (
|
||||
<Label description={description}>
|
||||
{title}
|
||||
{overrides && overrides.length > 0 && <OptionsPaneItemOverrides overrides={overrides} />}
|
||||
</Label>
|
||||
);
|
||||
}
|
||||
|
||||
const categories: React.ReactNode[] = [];
|
||||
@ -47,6 +55,7 @@ export class OptionsPaneItemDescriptor {
|
||||
return (
|
||||
<Label description={description && this.highlightWord(description, searchQuery)} category={categories}>
|
||||
{this.highlightWord(title, searchQuery)}
|
||||
{overrides && overrides.length > 0 && <OptionsPaneItemOverrides overrides={overrides} />}
|
||||
</Label>
|
||||
);
|
||||
}
|
||||
@ -57,6 +66,13 @@ export class OptionsPaneItemDescriptor {
|
||||
);
|
||||
}
|
||||
|
||||
renderOverrides() {
|
||||
const { overrides } = this.props;
|
||||
if (!overrides || overrides.length === 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
render(searchQuery?: string) {
|
||||
const { title, description, render, showIf, skipField } = this.props;
|
||||
const key = `${this.parent.props.id} ${title}`;
|
||||
|
@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import { Tooltip, useStyles2 } from '@grafana/ui';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { css, CSSObject } from '@emotion/css';
|
||||
import { OptionPaneItemOverrideInfo } from './types';
|
||||
|
||||
export interface Props {
|
||||
overrides: OptionPaneItemOverrideInfo[];
|
||||
}
|
||||
|
||||
export function OptionsPaneItemOverrides({ overrides }: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
{overrides.map((override, index) => (
|
||||
<Tooltip content={override.tooltip} key={index.toString()} placement="top">
|
||||
<div aria-label={override.description} className={styles[override.type]} />
|
||||
</Tooltip>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
const common: CSSObject = {
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: '50%',
|
||||
marginLeft: theme.spacing(1),
|
||||
position: 'relative',
|
||||
top: '-1px',
|
||||
};
|
||||
|
||||
return {
|
||||
wrapper: css({
|
||||
display: 'flex',
|
||||
}),
|
||||
rule: css({
|
||||
...common,
|
||||
backgroundColor: theme.colors.primary.main,
|
||||
}),
|
||||
data: css({
|
||||
...common,
|
||||
backgroundColor: theme.colors.warning.main,
|
||||
}),
|
||||
};
|
||||
};
|
@ -2,10 +2,12 @@ import React from 'react';
|
||||
import { fireEvent, render, screen, within } from '@testing-library/react';
|
||||
import {
|
||||
FieldConfigSource,
|
||||
FieldType,
|
||||
LoadingState,
|
||||
PanelData,
|
||||
standardEditorsRegistry,
|
||||
standardFieldConfigEditorRegistry,
|
||||
toDataFrame,
|
||||
} from '@grafana/data';
|
||||
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
@ -15,6 +17,7 @@ import { Provider } from 'react-redux';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import { getPanelPlugin } from 'app/features/plugins/__mocks__/pluginMocks';
|
||||
import { getStandardFieldConfigs, getStandardOptionEditors } from '@grafana/ui';
|
||||
import { dataOverrideTooltipDescription, overrideRuleTooltipDescription } from './state/getOptionOverrides';
|
||||
|
||||
standardEditorsRegistry.setInit(getStandardOptionEditors);
|
||||
standardFieldConfigEditorRegistry.setInit(getStandardFieldConfigs);
|
||||
@ -241,4 +244,46 @@ describe('OptionsPaneOptions', () => {
|
||||
within(thresholdsSection).getByLabelText(OptionsPaneSelector.fieldLabel('Thresholds CustomThresholdOption'))
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show data override info dot', async () => {
|
||||
const scenario = new OptionsPaneOptionsTestScenario();
|
||||
scenario.panelData.series = [
|
||||
toDataFrame({
|
||||
fields: [
|
||||
{
|
||||
name: 'Value',
|
||||
type: FieldType.number,
|
||||
values: [10, 200],
|
||||
config: {
|
||||
min: 100,
|
||||
},
|
||||
},
|
||||
],
|
||||
refId: 'A',
|
||||
}),
|
||||
];
|
||||
|
||||
scenario.render();
|
||||
|
||||
expect(screen.getByLabelText(dataOverrideTooltipDescription)).toBeInTheDocument();
|
||||
expect(screen.queryByLabelText(overrideRuleTooltipDescription)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show override rule info dot', async () => {
|
||||
const scenario = new OptionsPaneOptionsTestScenario();
|
||||
scenario.panel.fieldConfig.overrides = [
|
||||
{
|
||||
matcher: { id: 'byName', options: 'SeriesA' },
|
||||
properties: [
|
||||
{
|
||||
id: 'decimals',
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
scenario.render();
|
||||
expect(screen.getByLabelText(overrideRuleTooltipDescription)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -10,8 +10,9 @@ import {
|
||||
isNestedPanelOptions,
|
||||
NestedValueAccess,
|
||||
PanelOptionsEditorBuilder,
|
||||
} from '../../../../../../packages/grafana-data/src/utils/OptionsUIBuilders';
|
||||
} from '@grafana/data/src/utils/OptionsUIBuilders';
|
||||
import { PanelOptionsSupplier } from '@grafana/data/src/panel/PanelPlugin';
|
||||
import { getOptionOverrides } from './state/getOptionOverrides';
|
||||
|
||||
type categoryGetter = (categoryNames?: string[]) => OptionsPaneCategoryDescriptor;
|
||||
|
||||
@ -91,6 +92,7 @@ export function getVizualizationOptions(props: OptionPaneRenderProps): OptionsPa
|
||||
new OptionsPaneItemDescriptor({
|
||||
title: fieldOption.name,
|
||||
description: fieldOption.description,
|
||||
overrides: getOptionOverrides(fieldOption, currentFieldConfig, data?.series),
|
||||
render: function renderEditor() {
|
||||
const onChange = (v: any) => {
|
||||
onFieldConfigsChange(
|
||||
|
@ -0,0 +1,49 @@
|
||||
import { DataFrame, FieldConfigPropertyItem, FieldConfigSource } from '@grafana/data';
|
||||
import { get as lodashGet } from 'lodash';
|
||||
import { OptionPaneItemOverrideInfo } from '../types';
|
||||
|
||||
export const dataOverrideTooltipDescription =
|
||||
'Some data fields have this option pre-configured. Add a field override rule to override the pre-configured value.';
|
||||
export const overrideRuleTooltipDescription = 'An override rule exists for this property';
|
||||
|
||||
export function getOptionOverrides(
|
||||
fieldOption: FieldConfigPropertyItem,
|
||||
fieldConfig: FieldConfigSource,
|
||||
frames: DataFrame[] | undefined
|
||||
): OptionPaneItemOverrideInfo[] {
|
||||
const infoDots: OptionPaneItemOverrideInfo[] = [];
|
||||
|
||||
// Look for options overriden in data field config
|
||||
if (frames) {
|
||||
for (const frame of frames) {
|
||||
for (const field of frame.fields) {
|
||||
const value = lodashGet(field.config, fieldOption.path);
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
infoDots.push({
|
||||
type: 'data',
|
||||
description: dataOverrideTooltipDescription,
|
||||
tooltip: dataOverrideTooltipDescription,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const overrideRuleFound = fieldConfig.overrides.some((rule) =>
|
||||
rule.properties.some((prop) => prop.id === fieldOption.id)
|
||||
);
|
||||
|
||||
if (overrideRuleFound) {
|
||||
infoDots.push({
|
||||
type: 'rule',
|
||||
description: overrideRuleTooltipDescription,
|
||||
tooltip: overrideRuleTooltipDescription,
|
||||
});
|
||||
}
|
||||
|
||||
return infoDots;
|
||||
}
|
@ -59,3 +59,10 @@ export interface OptionPaneRenderProps {
|
||||
onPanelOptionsChanged: (options: any) => void;
|
||||
onFieldConfigsChange: (config: FieldConfigSource) => void;
|
||||
}
|
||||
|
||||
export interface OptionPaneItemOverrideInfo {
|
||||
type: 'data' | 'rule';
|
||||
onClick?: () => void;
|
||||
tooltip: string;
|
||||
description: string;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user