Compare commits
9 Commits
ash/react-
...
grafakus/q
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f584d143f | ||
|
|
e38240369b | ||
|
|
26ac64cf1a | ||
|
|
d8777012cb | ||
|
|
dfa07d8e10 | ||
|
|
dc63eb3314 | ||
|
|
1e3c6fed55 | ||
|
|
d02201f564 | ||
|
|
2a9377ac02 |
@@ -800,6 +800,8 @@ VariableOption: {
|
|||||||
text: string | [...string]
|
text: string | [...string]
|
||||||
// Value of the option
|
// Value of the option
|
||||||
value: string | [...string]
|
value: string | [...string]
|
||||||
|
// Additional properties for multi-props variables
|
||||||
|
properties?: {[string]: string}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query variable specification
|
// Query variable specification
|
||||||
|
|||||||
@@ -804,6 +804,8 @@ VariableOption: {
|
|||||||
text: string | [...string]
|
text: string | [...string]
|
||||||
// Value of the option
|
// Value of the option
|
||||||
value: string | [...string]
|
value: string | [...string]
|
||||||
|
// Additional properties for multi-props variables
|
||||||
|
properties?: {[string]: string}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query variable specification
|
// Query variable specification
|
||||||
|
|||||||
@@ -241,6 +241,8 @@ lineage: schemas: [{
|
|||||||
text: string | [...string]
|
text: string | [...string]
|
||||||
// Value of the option
|
// Value of the option
|
||||||
value: string | [...string]
|
value: string | [...string]
|
||||||
|
// Additional properties for multi-props variables
|
||||||
|
properties?: {[string]: string}
|
||||||
} @cuetsy(kind="interface")
|
} @cuetsy(kind="interface")
|
||||||
|
|
||||||
// Options to config when to refresh a variable
|
// Options to config when to refresh a variable
|
||||||
|
|||||||
@@ -241,6 +241,8 @@ lineage: schemas: [{
|
|||||||
text: string | [...string]
|
text: string | [...string]
|
||||||
// Value of the option
|
// Value of the option
|
||||||
value: string | [...string]
|
value: string | [...string]
|
||||||
|
// Additional properties for multi-props variables
|
||||||
|
properties?: {[string]: string}
|
||||||
} @cuetsy(kind="interface")
|
} @cuetsy(kind="interface")
|
||||||
|
|
||||||
// Options to config when to refresh a variable
|
// Options to config when to refresh a variable
|
||||||
|
|||||||
@@ -804,6 +804,8 @@ VariableOption: {
|
|||||||
text: string | [...string]
|
text: string | [...string]
|
||||||
// Value of the option
|
// Value of the option
|
||||||
value: string | [...string]
|
value: string | [...string]
|
||||||
|
// Additional properties for multi-props variables
|
||||||
|
properties?: {[string]: string}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query variable specification
|
// Query variable specification
|
||||||
|
|||||||
@@ -1426,6 +1426,8 @@ type DashboardVariableOption struct {
|
|||||||
Text DashboardStringOrArrayOfString `json:"text"`
|
Text DashboardStringOrArrayOfString `json:"text"`
|
||||||
// Value of the option
|
// Value of the option
|
||||||
Value DashboardStringOrArrayOfString `json:"value"`
|
Value DashboardStringOrArrayOfString `json:"value"`
|
||||||
|
// Additional properties for multi-props variables
|
||||||
|
Properties map[string]string `json:"properties,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDashboardVariableOption creates a new DashboardVariableOption object.
|
// NewDashboardVariableOption creates a new DashboardVariableOption object.
|
||||||
|
|||||||
@@ -5133,6 +5133,22 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardVariableOption(ref common.Refer
|
|||||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardStringOrArrayOfString"),
|
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardStringOrArrayOfString"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"properties": {
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Description: "Additional properties for multi-props variables",
|
||||||
|
Type: []string{"object"},
|
||||||
|
AdditionalProperties: &spec.SchemaOrBool{
|
||||||
|
Allows: true,
|
||||||
|
Schema: &spec.Schema{
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Default: "",
|
||||||
|
Type: []string{"string"},
|
||||||
|
Format: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Required: []string{"text", "value"},
|
Required: []string{"text", "value"},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -808,6 +808,8 @@ VariableOption: {
|
|||||||
text: string | [...string]
|
text: string | [...string]
|
||||||
// Value of the option
|
// Value of the option
|
||||||
value: string | [...string]
|
value: string | [...string]
|
||||||
|
// Additional properties for multi-props variables
|
||||||
|
properties?: {[string]: string}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query variable specification
|
// Query variable specification
|
||||||
|
|||||||
@@ -1429,6 +1429,8 @@ type DashboardVariableOption struct {
|
|||||||
Text DashboardStringOrArrayOfString `json:"text"`
|
Text DashboardStringOrArrayOfString `json:"text"`
|
||||||
// Value of the option
|
// Value of the option
|
||||||
Value DashboardStringOrArrayOfString `json:"value"`
|
Value DashboardStringOrArrayOfString `json:"value"`
|
||||||
|
// Additional properties for multi-props variables
|
||||||
|
Properties map[string]string `json:"properties,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDashboardVariableOption creates a new DashboardVariableOption object.
|
// NewDashboardVariableOption creates a new DashboardVariableOption object.
|
||||||
|
|||||||
@@ -5196,6 +5196,22 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardVariableOption(ref common.Refere
|
|||||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardStringOrArrayOfString"),
|
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardStringOrArrayOfString"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"properties": {
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Description: "Additional properties for multi-props variables",
|
||||||
|
Type: []string{"object"},
|
||||||
|
AdditionalProperties: &spec.SchemaOrBool{
|
||||||
|
Allows: true,
|
||||||
|
Schema: &spec.Schema{
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Default: "",
|
||||||
|
Type: []string{"string"},
|
||||||
|
Format: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Required: []string{"text", "value"},
|
Required: []string{"text", "value"},
|
||||||
},
|
},
|
||||||
|
|||||||
4
apps/dashboard/pkg/apis/dashboard_manifest.go
generated
4
apps/dashboard/pkg/apis/dashboard_manifest.go
generated
File diff suppressed because one or more lines are too long
@@ -237,6 +237,8 @@ lineage: schemas: [{
|
|||||||
text: string | [...string]
|
text: string | [...string]
|
||||||
// Value of the option
|
// Value of the option
|
||||||
value: string | [...string]
|
value: string | [...string]
|
||||||
|
// Additional properties for multi-props variables
|
||||||
|
properties?: {[string]: string}
|
||||||
} @cuetsy(kind="interface")
|
} @cuetsy(kind="interface")
|
||||||
|
|
||||||
// Options to config when to refresh a variable
|
// Options to config when to refresh a variable
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ export interface VariableOption {
|
|||||||
text: string | string[];
|
text: string | string[];
|
||||||
value: string | string[];
|
value: string | string[];
|
||||||
isNone?: boolean;
|
isNone?: boolean;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
properties?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IntervalVariableModel extends VariableWithOptions {
|
export interface IntervalVariableModel extends VariableWithOptions {
|
||||||
@@ -118,6 +120,7 @@ export interface QueryVariableModel extends VariableWithMultiSupport {
|
|||||||
definition: string;
|
definition: string;
|
||||||
sort: VariableSort;
|
sort: VariableSort;
|
||||||
queryValue?: string;
|
queryValue?: string;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
query: any;
|
query: any;
|
||||||
regex: string;
|
regex: string;
|
||||||
regexApplyTo?: VariableRegexApplyTo;
|
regexApplyTo?: VariableRegexApplyTo;
|
||||||
@@ -193,6 +196,7 @@ export interface BaseVariableModel {
|
|||||||
skipUrlSync: boolean;
|
skipUrlSync: boolean;
|
||||||
index: number;
|
index: number;
|
||||||
state: LoadingState;
|
state: LoadingState;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
error: any | null;
|
error: any | null;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
usedInRepeat?: boolean;
|
usedInRepeat?: boolean;
|
||||||
|
|||||||
@@ -231,6 +231,10 @@ export const defaultVariableModel: Partial<VariableModel> = {
|
|||||||
* Option to be selected in a variable.
|
* Option to be selected in a variable.
|
||||||
*/
|
*/
|
||||||
export interface VariableOption {
|
export interface VariableOption {
|
||||||
|
/**
|
||||||
|
* Additional properties for multi-props variables
|
||||||
|
*/
|
||||||
|
properties?: Record<string, string>;
|
||||||
/**
|
/**
|
||||||
* Whether the option is selected or not
|
* Whether the option is selected or not
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -715,7 +715,9 @@ VariableOption: {
|
|||||||
// Text to be displayed for the option
|
// Text to be displayed for the option
|
||||||
text: string | [...string]
|
text: string | [...string]
|
||||||
// Value of the option
|
// Value of the option
|
||||||
value: string | [...string]
|
value: string | [...string]
|
||||||
|
// Additional properties for multi-props variables
|
||||||
|
properties?: {[string]: string}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query variable specification
|
// Query variable specification
|
||||||
|
|||||||
2
pkg/kinds/dashboard/dashboard_spec_gen.go
generated
2
pkg/kinds/dashboard/dashboard_spec_gen.go
generated
@@ -903,6 +903,8 @@ type VariableOption struct {
|
|||||||
Text StringOrArrayOfString `json:"text"`
|
Text StringOrArrayOfString `json:"text"`
|
||||||
// Value of the option
|
// Value of the option
|
||||||
Value StringOrArrayOfString `json:"value"`
|
Value StringOrArrayOfString `json:"value"`
|
||||||
|
// Additional properties for multi-props variables
|
||||||
|
Properties map[string]string `json:"properties,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVariableOption creates a new VariableOption object.
|
// NewVariableOption creates a new VariableOption object.
|
||||||
|
|||||||
@@ -3912,6 +3912,13 @@
|
|||||||
"value"
|
"value"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"properties": {
|
||||||
|
"description": "Additional properties for multi-props variables",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
"selected": {
|
"selected": {
|
||||||
"description": "Whether the option is selected or not",
|
"description": "Whether the option is selected or not",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
|||||||
@@ -3939,6 +3939,13 @@
|
|||||||
"value"
|
"value"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"properties": {
|
||||||
|
"description": "Additional properties for multi-props variables",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
"selected": {
|
"selected": {
|
||||||
"description": "Whether the option is selected or not",
|
"description": "Whether the option is selected or not",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ function variableValueOptionsToVariableOptions(varState: MultiValueVariable['sta
|
|||||||
value: String(o.value),
|
value: String(o.value),
|
||||||
text: o.label,
|
text: o.label,
|
||||||
selected: Array.isArray(varState.value) ? varState.value.includes(o.value) : varState.value === o.value,
|
selected: Array.isArray(varState.value) ? varState.value.includes(o.value) : varState.value === o.value,
|
||||||
|
...(o.properties && { properties: o.properties }),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDelete
|
|||||||
|
|
||||||
const isHasVariableOptions = hasVariableOptions(variable);
|
const isHasVariableOptions = hasVariableOptions(variable);
|
||||||
const optionsForSelect = isHasVariableOptions ? variable.getOptionsForSelect(false) : [];
|
const optionsForSelect = isHasVariableOptions ? variable.getOptionsForSelect(false) : [];
|
||||||
const hasMultiProps = 'valuesFormat' in variable.state && variable.state.valuesFormat === 'json';
|
|
||||||
|
|
||||||
const onDeleteVariable = (hideModal: () => void) => () => {
|
const onDeleteVariable = (hideModal: () => void) => () => {
|
||||||
reportInteraction('Delete variable');
|
reportInteraction('Delete variable');
|
||||||
@@ -125,7 +124,7 @@ export function VariableEditorForm({ variable, onTypeChange, onGoBack, onDelete
|
|||||||
|
|
||||||
{EditorToRender && <EditorToRender variable={variable} onRunQuery={onRunQuery} />}
|
{EditorToRender && <EditorToRender variable={variable} onRunQuery={onRunQuery} />}
|
||||||
|
|
||||||
{isHasVariableOptions && <VariableValuesPreview options={optionsForSelect} hasMultiProps={hasMultiProps} />}
|
{isHasVariableOptions && <VariableValuesPreview options={optionsForSelect} />}
|
||||||
|
|
||||||
<div className={styles.buttonContainer}>
|
<div className={styles.buttonContainer}>
|
||||||
<Stack gap={2}>
|
<Stack gap={2}>
|
||||||
|
|||||||
@@ -10,13 +10,16 @@ import { Button, InlineFieldRow, InlineLabel, InteractiveTable, Text, useStyles2
|
|||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
options: VariableValueOption[];
|
options: VariableValueOption[];
|
||||||
hasMultiProps?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VariableValuesPreview = ({ options, hasMultiProps }: Props) => {
|
const hasMultiProps = (options: Props['options']) => {
|
||||||
|
return Object.keys(options[1]?.properties ?? options[0]?.properties ?? {}).length > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const VariableValuesPreview = ({ options }: Props) => {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const hasOptions = options.length > 0;
|
const hasOptions = options.length > 0;
|
||||||
const displayMultiPropsPreview = config.featureToggles.multiPropsVariables && hasMultiProps;
|
const displayMultiPropsPreview = config.featureToggles.multiPropsVariables && hasOptions && hasMultiProps(options);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.previewContainer} style={{ gap: '8px' }}>
|
<div className={styles.previewContainer} style={{ gap: '8px' }}>
|
||||||
@@ -43,7 +46,8 @@ function VariableValuesWithPropsPreview({ options }: { options: VariableValueOpt
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
columns: Object.keys(data[0] ?? {}).map((id) => ({
|
// the option at index 0 can be "All" so we try to grab the column names from the 2nd option
|
||||||
|
columns: Object.keys(data[1] ?? data[0] ?? {}).map((id) => ({
|
||||||
id,
|
id,
|
||||||
// see https://github.com/TanStack/table/issues/1671
|
// see https://github.com/TanStack/table/issues/1671
|
||||||
header: unsanitizeKey(id),
|
header: unsanitizeKey(id),
|
||||||
@@ -62,7 +66,6 @@ function VariableValuesWithPropsPreview({ options }: { options: VariableValueOpt
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sanitizeKey = (key: string) => key.replace(/\./g, '__dot__');
|
const sanitizeKey = (key: string) => key.replace(/\./g, '__dot__');
|
||||||
const unsanitizeKey = (key: string) => key.replace(/__dot__/g, '.');
|
const unsanitizeKey = (key: string) => key.replace(/__dot__/g, '.');
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ function ModalEditorMultiProps(props: ModalEditorProps) {
|
|||||||
{queryValidationError && <FieldValidationMessage>{queryValidationError.message}</FieldValidationMessage>}
|
{queryValidationError && <FieldValidationMessage>{queryValidationError.message}</FieldValidationMessage>}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<VariableValuesPreview options={options} hasMultiProps={valuesFormat === 'json'} />
|
<VariableValuesPreview options={options} />
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Modal.ButtonRow>
|
<Modal.ButtonRow>
|
||||||
|
|||||||
@@ -81,16 +81,17 @@ const buildLabelPath = (label: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getVariableValueProperties = (variable: TypedVariableModel): string[] => {
|
const getVariableValueProperties = (variable: TypedVariableModel): string[] => {
|
||||||
if (!('valuesFormat' in variable) || variable.valuesFormat !== 'json') {
|
if (!('options' in variable) || !variable.options[0].properties) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectFieldPaths(option: Record<string, string>, currentPath: string) {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
function collectFieldPaths(properties: Record<string, any>, currentPath: string) {
|
||||||
let paths: string[] = [];
|
let paths: string[] = [];
|
||||||
for (const field in option) {
|
for (const field in properties) {
|
||||||
if (option.hasOwnProperty(field)) {
|
if (properties.hasOwnProperty(field)) {
|
||||||
const newPath = `${currentPath}.${field}`;
|
const newPath = `${currentPath}.${field}`;
|
||||||
const value = option[field];
|
const value = properties[field];
|
||||||
if (typeof value === 'object' && value !== null) {
|
if (typeof value === 'object' && value !== null) {
|
||||||
paths = [...paths, ...collectFieldPaths(value, newPath)];
|
paths = [...paths, ...collectFieldPaths(value, newPath)];
|
||||||
}
|
}
|
||||||
@@ -100,11 +101,7 @@ const getVariableValueProperties = (variable: TypedVariableModel): string[] => {
|
|||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return collectFieldPaths(variable.options[0].properties, variable.name);
|
||||||
return collectFieldPaths(JSON.parse(variable.query)[0], variable.name);
|
|
||||||
} catch {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPanelLinksVariableSuggestions = (): VariableSuggestion[] => [
|
export const getPanelLinksVariableSuggestions = (): VariableSuggestion[] => [
|
||||||
|
|||||||
@@ -503,13 +503,16 @@ describe('linkSrv', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getPanelLinksVariableSuggestions', () => {
|
describe('getPanelLinksVariableSuggestions', () => {
|
||||||
it('then it should return template variables, json properties and built-ins', () => {
|
it('then it should return template variables, options properties and built-ins', () => {
|
||||||
const templateSrvWithJsonValues = initTemplateSrv('key', [
|
const templateSrvWithJsonValues = initTemplateSrv('key', [
|
||||||
{
|
{
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
name: 'customServers',
|
name: 'customServers',
|
||||||
valuesFormat: 'json',
|
valuesFormat: 'json',
|
||||||
query: '[{"name":"web","ip":"192.168.0.100"},{"name":"ads","ip":"192.168.0.142"}]',
|
options: [
|
||||||
|
{ text: 'web', value: 'web', properties: { name: 'web', ip: '192.168.0.100' } },
|
||||||
|
{ text: 'ads', value: 'ads', properties: { name: 'ads', ip: '192.168.0.142' } },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
setTemplateSrv(templateSrvWithJsonValues);
|
setTemplateSrv(templateSrvWithJsonValues);
|
||||||
|
|||||||
Reference in New Issue
Block a user