-
- {tabs.map((t) => t.renderTab({ active: t.tabId === tab, onChangeTab: () => model.onChangeTab(t) }))}
-
-
-
-
- {currentTab && }
-
-
+
+
+
+
+
+
{renderDetailPanel()}
+
+
setTransformDrawerOpen(false)}
+ onTransformationAdd={handleTransformationAdd}
+ series={series}
+ />
);
}
@@ -118,22 +384,67 @@ function getStyles(theme: GrafanaTheme2) {
height: '100%',
width: '100%',
}),
- tabBorder: css({
+ unifiedLayout: css({
+ flex: 1,
+ minHeight: 0,
background: theme.colors.background.primary,
border: `1px solid ${theme.colors.border.weak}`,
borderLeft: 'none',
borderBottom: 'none',
borderTopRightRadius: theme.shape.radius.default,
- flexGrow: 1,
overflow: 'hidden',
}),
- tabContent: css({
- padding: theme.spacing(2),
+ splitLayout: css({
+ display: 'flex',
height: '100%',
+ width: '100%',
}),
- tabsBar: css({
+ leftPanel: css({
+ width: '25%',
+ minWidth: '200px',
+ maxWidth: '400px',
flexShrink: 0,
- paddingLeft: theme.spacing(2),
+ borderRight: `1px solid ${theme.colors.border.weak}`,
+ }),
+ rightPanel: css({
+ flex: 1,
+ minWidth: 0,
+ overflow: 'hidden',
+ }),
+ detailContent: css({
+ display: 'flex',
+ flexDirection: 'column',
+ height: '100%',
+ background: theme.colors.background.primary,
+ }),
+ detailHeader: css({
+ padding: theme.spacing(2),
+ borderBottom: `1px solid ${theme.colors.border.weak}`,
+ background: theme.colors.background.secondary,
+ '& h3': {
+ margin: 0,
+ marginBottom: theme.spacing(0.5),
+ fontSize: theme.typography.h4.fontSize,
+ fontWeight: theme.typography.fontWeightMedium,
+ },
+ }),
+ detailHeaderSubtext: css({
+ margin: 0,
+ fontSize: theme.typography.bodySmall.fontSize,
+ color: theme.colors.text.secondary,
+ }),
+ detailBody: css({
+ flex: 1,
+ overflow: 'auto',
+ minHeight: 0,
+ }),
+ emptyState: css({
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ height: '100%',
+ color: theme.colors.text.secondary,
+ fontSize: theme.typography.h5.fontSize,
}),
};
}
diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformationsTab.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformationsTab.tsx
index af68e86adc4..d58eedcd9d6 100644
--- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformationsTab.tsx
+++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataTransformationsTab.tsx
@@ -3,8 +3,7 @@ import { DragDropContext, DropResult, Droppable } from '@hello-pangea/dnd';
import { useCallback, useMemo, useState } from 'react';
import { DataTransformerConfig, GrafanaTheme2, PanelData } from '@grafana/data';
-import { selectors } from '@grafana/e2e-selectors';
-import { Trans, t } from '@grafana/i18n';
+import { t } from '@grafana/i18n';
import {
SceneObjectBase,
SceneComponentProps,
@@ -14,7 +13,7 @@ import {
VizPanel,
SceneObjectState,
} from '@grafana/scenes';
-import { Button, ButtonGroup, ConfirmModal, Tab, useStyles2 } from '@grafana/ui';
+import { Tab, useStyles2 } from '@grafana/ui';
import { TransformationOperationRows } from 'app/features/dashboard/components/TransformationsEditor/TransformationOperationRows';
import { ExpressionQueryType } from 'app/features/expressions/types';
@@ -69,7 +68,6 @@ export class PanelDataTransformationsTab
}
export function PanelDataTransformationsTabRendered({ model }: SceneComponentProps
) {
- const styles = useStyles2(getStyles);
const sourceData = model.getQueryRunner().useState();
const { data, transformations: transformsWrongType } = model.getDataTransformer().useState();
@@ -84,7 +82,6 @@ export function PanelDataTransformationsTabRendered({ model }: SceneComponentPro
}, [transformsWrongType]);
const [drawerOpen, setDrawerOpen] = useState(false);
- const [confirmModalOpen, setConfirmModalOpen] = useState(false);
const openDrawer = () => setDrawerOpen(true);
const closeDrawer = () => setDrawerOpen(false);
@@ -163,46 +160,6 @@ export function PanelDataTransformationsTabRendered({ model }: SceneComponentPro
return (
<>
-
-
-
-
- {
- model.onChangeTransformations([]);
- setConfirmModalOpen(false);
- }}
- onDismiss={() => setConfirmModalOpen(false)}
- />
{transformationsDrawer}
>
);
@@ -216,6 +173,7 @@ interface TransformationEditorProps {
function TransformationsEditor({ transformations, model, data }: TransformationEditorProps) {
const transformationEditorRows = transformations.map((t, i) => ({ id: `${i} - ${t.id}`, transformation: t }));
+ const styles = useStyles2(getStyles);
const onDragEnd = (result: DropResult) => {
if (!result || !result.destination) {
@@ -234,37 +192,39 @@ function TransformationsEditor({ transformations, model, data }: TransformationE
};
return (
-
-
- {(provided) => {
- return (
-
- {
- const newTransformations = transformations.slice();
- newTransformations[index] = transformation;
- model.onChangeTransformations(newTransformations);
- }}
- onRemove={(index) => {
- const newTransformations = transformations.slice();
- newTransformations.splice(index, 1);
- model.onChangeTransformations(newTransformations);
- }}
- configs={transformationEditorRows}
- data={data}
- >
- {provided.placeholder}
-
- );
- }}
-
-
+
+
+
+ {(provided) => {
+ return (
+
+ {
+ const newTransformations = transformations.slice();
+ newTransformations[index] = transformation;
+ model.onChangeTransformations(newTransformations);
+ }}
+ onRemove={(index) => {
+ const newTransformations = transformations.slice();
+ newTransformations.splice(index, 1);
+ model.onChangeTransformations(newTransformations);
+ }}
+ configs={transformationEditorRows}
+ data={data}
+ >
+ {provided.placeholder}
+
+ );
+ }}
+
+
+
);
}
const getStyles = (theme: GrafanaTheme2) => ({
- removeAll: css({
- marginLeft: theme.spacing(2),
+ container: css({
+ padding: theme.spacing(2),
}),
});
diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/QueryTransformCard.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/QueryTransformCard.tsx
new file mode 100644
index 00000000000..e0924b14f97
--- /dev/null
+++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/QueryTransformCard.tsx
@@ -0,0 +1,217 @@
+import { css } from '@emotion/css';
+import { memo } from 'react';
+
+import { DataTransformerConfig, GrafanaTheme2 } from '@grafana/data';
+import { t } from '@grafana/i18n';
+import { getDataSourceSrv } from '@grafana/runtime';
+import { SceneDataQuery } from '@grafana/scenes';
+import { Icon, IconButton, Stack, useStyles2 } from '@grafana/ui';
+
+interface QueryTransformCardProps {
+ item: SceneDataQuery | DataTransformerConfig;
+ type: 'query' | 'transform';
+ index: number;
+ isSelected: boolean;
+ onClick: () => void;
+ onDuplicate?: () => void;
+ onRemove?: () => void;
+ onToggleVisibility?: () => void;
+}
+
+export const QueryTransformCard = memo(
+ ({ item, type, index, isSelected, onClick, onDuplicate, onRemove, onToggleVisibility }: QueryTransformCardProps) => {
+ const styles = useStyles2(getStyles);
+
+ const getName = (): string => {
+ if (type === 'query' && 'refId' in item) {
+ return item.refId || `Query ${index + 1}`;
+ } else if ('id' in item) {
+ return item.id.replace(/-/g, ' ').replace(/\b\w/g, (l: string) => l.toUpperCase());
+ }
+ return '';
+ };
+
+ const getDatasourceName = (): string => {
+ if (type === 'query' && 'datasource' in item && item.datasource) {
+ try {
+ const dsSettings = getDataSourceSrv().getInstanceSettings(item.datasource);
+ return dsSettings?.name || 'Unknown datasource';
+ } catch {
+ return 'Unknown datasource';
+ }
+ }
+ return '';
+ };
+
+ const isHidden = type === 'query' && 'hide' in item && item.hide;
+ const icon = type === 'query' ? 'database' : 'process';
+ const typeLabel = type === 'query' ? 'Query' : 'Transformation';
+ const name = getName();
+ const datasourceName = getDatasourceName();
+
+ const handleAction = (e: React.MouseEvent, action: () => void) => {
+ e.stopPropagation();
+ action();
+ };
+
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ onClick();
+ }
+ };
+
+ return (
+
+ {/* Header with type and action icons */}
+
+
+
+ {typeLabel}
+
+
+
+ {type === 'query' && onToggleVisibility && (
+ handleAction(e, onToggleVisibility)}
+ className={styles.actionButton}
+ />
+ )}
+ {type === 'query' && onDuplicate && (
+ handleAction(e, onDuplicate)}
+ className={styles.actionButton}
+ />
+ )}
+ {onRemove && (
+ handleAction(e, onRemove)}
+ className={styles.actionButton}
+ />
+ )}
+
+
+
+
+ {/* Content: Name and datasource */}
+
+
{name}
+ {datasourceName &&
{datasourceName}
}
+
+
+ );
+ }
+);
+
+QueryTransformCard.displayName = 'QueryTransformCard';
+
+const getStyles = (theme: GrafanaTheme2) => {
+ const actionsClass = 'actions-container';
+
+ return {
+ card: css({
+ cursor: 'pointer',
+ marginBottom: theme.spacing(1),
+ border: `1px solid ${theme.colors.border.weak}`,
+ borderRadius: theme.shape.radius.default,
+ overflow: 'hidden',
+ background: theme.colors.background.primary,
+ [`&:hover .${actionsClass}`]: {
+ opacity: 1,
+ },
+ '&:hover': {
+ borderColor: theme.colors.border.strong,
+ },
+ }),
+ cardSelected: css({
+ borderColor: theme.colors.primary.border,
+ boxShadow: `0 0 0 1px ${theme.colors.primary.border}`,
+ }),
+ headerQuery: css({
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ padding: theme.spacing(1, 1.5),
+ background: theme.colors.primary.transparent,
+ borderBottom: `1px solid ${theme.colors.border.weak}`,
+ }),
+ headerTransform: css({
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ padding: theme.spacing(1, 1.5),
+ background: theme.isDark
+ ? `${theme.visualization.getColorByName('orange')}20`
+ : `${theme.visualization.getColorByName('orange')}15`,
+ borderBottom: `1px solid ${theme.colors.border.weak}`,
+ }),
+ headerLeft: css({
+ display: 'flex',
+ alignItems: 'center',
+ gap: theme.spacing(1),
+ flex: 1,
+ minWidth: 0,
+ }),
+ headerIcon: css({
+ color: theme.colors.text.secondary,
+ flexShrink: 0,
+ }),
+ typeLabel: css({
+ fontSize: theme.typography.bodySmall.fontSize,
+ fontWeight: theme.typography.fontWeightMedium,
+ color: theme.colors.text.secondary,
+ textTransform: 'uppercase',
+ letterSpacing: '0.05em',
+ }),
+ actions: css({
+ opacity: 0,
+ flexShrink: 0,
+ }),
+ actionsClass,
+ actionButton: css({
+ '&:hover': {
+ background: theme.colors.action.hover,
+ },
+ }),
+ content: css({
+ padding: theme.spacing(1.5),
+ }),
+ name: css({
+ fontSize: theme.typography.h5.fontSize,
+ fontWeight: theme.typography.fontWeightMedium,
+ color: theme.colors.text.primary,
+ marginBottom: theme.spacing(0.5),
+ }),
+ datasource: css({
+ fontSize: theme.typography.bodySmall.fontSize,
+ color: theme.colors.text.secondary,
+ }),
+ };
+};
diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/QueryTransformList.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/QueryTransformList.tsx
new file mode 100644
index 00000000000..26ea518417b
--- /dev/null
+++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/QueryTransformList.tsx
@@ -0,0 +1,138 @@
+import { css } from '@emotion/css';
+import { memo } from 'react';
+
+import { DataTransformerConfig, GrafanaTheme2 } from '@grafana/data';
+import { Trans } from '@grafana/i18n';
+import { SceneDataQuery } from '@grafana/scenes';
+import { Button, ScrollContainer, Stack, useStyles2 } from '@grafana/ui';
+
+import { QueryTransformCard } from './QueryTransformCard';
+
+export interface QueryTransformItem {
+ id: string;
+ type: 'query' | 'transform';
+ data: SceneDataQuery | DataTransformerConfig;
+ index: number;
+}
+
+interface QueryTransformListProps {
+ items: QueryTransformItem[];
+ selectedId: string | null;
+ onSelect: (id: string) => void;
+ onAddQuery: () => void;
+ onAddTransform: () => void;
+ onDuplicateQuery?: (index: number) => void;
+ onRemoveQuery?: (index: number) => void;
+ onToggleQueryVisibility?: (index: number) => void;
+ onRemoveTransform?: (index: number) => void;
+}
+
+export const QueryTransformList = memo(
+ ({
+ items,
+ selectedId,
+ onSelect,
+ onAddQuery,
+ onAddTransform,
+ onDuplicateQuery,
+ onRemoveQuery,
+ onToggleQueryVisibility,
+ onRemoveTransform,
+ }: QueryTransformListProps) => {
+ const styles = useStyles2(getStyles);
+
+ const queries = items.filter((item) => item.type === 'query');
+ const transforms = items.filter((item) => item.type === 'transform');
+
+ return (
+
+
+
+
+ {queries.length > 0 && (
+
+ {queries.map((item) => (
+ onSelect(item.id)}
+ onDuplicate={onDuplicateQuery ? () => onDuplicateQuery(item.index) : undefined}
+ onRemove={onRemoveQuery ? () => onRemoveQuery(item.index) : undefined}
+ onToggleVisibility={
+ onToggleQueryVisibility ? () => onToggleQueryVisibility(item.index) : undefined
+ }
+ />
+ ))}
+
+ )}
+
+ {transforms.length > 0 && (
+
+ {transforms.map((item) => (
+ onSelect(item.id)}
+ onRemove={onRemoveTransform ? () => onRemoveTransform(item.index) : undefined}
+ />
+ ))}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+);
+
+QueryTransformList.displayName = 'QueryTransformList';
+
+const getStyles = (theme: GrafanaTheme2) => {
+ return {
+ container: css({
+ display: 'flex',
+ flexDirection: 'column',
+ height: '100%',
+ background: theme.colors.background.primary,
+ borderRight: `1px solid ${theme.colors.border.weak}`,
+ overflow: 'hidden',
+ }),
+ scrollContainer: css({
+ flex: 1,
+ minHeight: 0,
+ overflow: 'auto',
+ }),
+ content: css({
+ padding: theme.spacing(2),
+ }),
+ section: css({
+ '&:not(:first-child)': {
+ marginTop: theme.spacing(2),
+ },
+ }),
+ actions: css({
+ padding: theme.spacing(2),
+ borderTop: `1px solid ${theme.colors.border.weak}`,
+ background: theme.colors.background.canvas,
+ flexShrink: 0,
+ }),
+ };
+};
diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/SceneQueryDetailView.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/SceneQueryDetailView.tsx
new file mode 100644
index 00000000000..430cbe57c2a
--- /dev/null
+++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/SceneQueryDetailView.tsx
@@ -0,0 +1,216 @@
+import { css } from '@emotion/css';
+import { useCallback, useEffect, useState } from 'react';
+
+import {
+ CoreApp,
+ DataQuery,
+ DataSourceApi,
+ DataSourceInstanceSettings,
+ DataSourcePluginContextProvider,
+ GrafanaTheme2,
+ PanelData,
+ TimeRange,
+} from '@grafana/data';
+import { Trans, t } from '@grafana/i18n';
+import { getDataSourceSrv } from '@grafana/runtime';
+import { SceneDataQuery, VizPanel } from '@grafana/scenes';
+import { ErrorBoundaryAlert, useStyles2 } from '@grafana/ui';
+import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOperationRow';
+import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker';
+import { QueryErrorAlert } from 'app/features/query/components/QueryErrorAlert';
+
+import { getQueryRunnerFor } from '../../utils/utils';
+
+interface SceneQueryDetailViewProps {
+ panel: VizPanel;
+ query: SceneDataQuery;
+ queryIndex: number;
+}
+
+export function SceneQueryDetailView({ panel, query, queryIndex }: SceneQueryDetailViewProps) {
+ const styles = useStyles2(getStyles);
+ const [datasource, setDatasource] = useState(null);
+ const [dsSettings, setDsSettings] = useState(null);
+ const [data, setData] = useState();
+
+ const queryRunner = getQueryRunnerFor(panel);
+ const queryRunnerState = queryRunner?.useState();
+ const timeRange: TimeRange | undefined = queryRunner?.state.$timeRange?.state.value;
+
+ // Load datasource
+ useEffect(() => {
+ const loadDatasource = async () => {
+ try {
+ const ds = await getDataSourceSrv().get(query.datasource);
+ const settings = getDataSourceSrv().getInstanceSettings(query.datasource);
+ setDatasource(ds);
+ setDsSettings(settings || null);
+ } catch (error) {
+ console.error('Failed to load datasource:', error);
+ // Fallback to default datasource
+ const defaultDs = await getDataSourceSrv().get();
+ const defaultSettings = getDataSourceSrv().getInstanceSettings(null);
+ setDatasource(defaultDs);
+ setDsSettings(defaultSettings || null);
+ }
+ };
+
+ loadDatasource();
+ }, [query.datasource]);
+
+ // Subscribe to panel data
+ useEffect(() => {
+ if (!queryRunnerState?.data) {
+ return;
+ }
+ // Filter data for this specific query
+ const panelData = queryRunnerState.data;
+ const filteredSeries = panelData.series.filter((s) => s.refId === query.refId);
+ const filteredData = {
+ ...panelData,
+ series: filteredSeries,
+ error: panelData.errors?.find((e) => e.refId === query.refId),
+ };
+ setData(filteredData);
+ }, [queryRunnerState?.data, query.refId]);
+
+ const handleDataSourceChange = useCallback(
+ async (newDsSettings: DataSourceInstanceSettings) => {
+ try {
+ const newDs = await getDataSourceSrv().get(newDsSettings.uid);
+ setDatasource(newDs);
+ setDsSettings(newDsSettings);
+
+ // Update the query with the new datasource
+ if (queryRunner) {
+ const queries = queryRunner.state.queries || [];
+ const newQueries = queries.map((q, idx) => {
+ if (idx === queryIndex) {
+ // Get default query for new datasource
+ const defaultQuery = newDs.getDefaultQuery?.(CoreApp.PanelEditor) || {};
+ return {
+ ...defaultQuery,
+ ...q,
+ datasource: { uid: newDsSettings.uid, type: newDsSettings.type },
+ refId: q.refId,
+ };
+ }
+ return q;
+ });
+
+ queryRunner.setState({ queries: newQueries });
+ queryRunner.runQueries();
+ }
+ } catch (error) {
+ console.error('Failed to change datasource:', error);
+ }
+ },
+ [queryRunner, queryIndex]
+ );
+
+ const handleQueryChange = useCallback(
+ (updatedQuery: DataQuery) => {
+ if (queryRunner) {
+ const queries = queryRunner.state.queries || [];
+ const newQueries = queries.map((q, idx) => (idx === queryIndex ? updatedQuery : q));
+ queryRunner.setState({ queries: newQueries });
+ }
+ },
+ [queryRunner, queryIndex]
+ );
+
+ const handleRunQuery = useCallback(() => {
+ if (queryRunner) {
+ queryRunner.runQueries();
+ }
+ }, [queryRunner]);
+
+ const renderQueryEditor = () => {
+ if (!datasource || !dsSettings) {
+ return (
+
+ Loading data source...
+
+ );
+ }
+
+ const QueryEditor = datasource.components?.QueryEditor;
+ if (!QueryEditor) {
+ return (
+
+
+ This data source does not have a query editor
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ );
+ };
+
+ const error = data?.error || data?.errors?.find((e) => e.refId === query.refId);
+
+ return (
+
+
+
+
+
+
+
+ {renderQueryEditor()}
+ {error && }
+
+
+
+ );
+}
+
+const getStyles = (theme: GrafanaTheme2) => {
+ return {
+ container: css({
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(2),
+ padding: theme.spacing(2),
+ }),
+ datasourceSection: css({
+ paddingBottom: theme.spacing(2),
+ }),
+ queryContent: css({
+ display: 'flex',
+ flexDirection: 'column',
+ gap: theme.spacing(1),
+ }),
+ noEditor: css({
+ padding: theme.spacing(2),
+ textAlign: 'center',
+ color: theme.colors.text.secondary,
+ }),
+ };
+};
diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx
index 18a2954d77e..0563bf3df3c 100644
--- a/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx
+++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx
@@ -227,6 +227,7 @@ export const TransformationOperationRow = ({
draggable
actions={renderActions}
disabled={disabled}
+ hideHeader={true}
expanderMessages={{
close: 'Collapse transformation row',
open: 'Expand transformation row',
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index d9efaadc4d6..6ceb8e3cead 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -6180,6 +6180,9 @@
"text-loading-rules": "Loading rules...",
"title-dashboard-not-saved": "Dashboard not saved"
},
+ "panel-data-pane": {
+ "empty-state": "Select a query or transformation to edit"
+ },
"panel-data-queries-tab": {
"tab-label": "Queries"
},
@@ -6190,13 +6193,6 @@
"panel-data-transformations-tab": {
"tab-label": "Transformations"
},
- "panel-data-transformations-tab-rendered": {
- "add-another-transformation": "Add another transformation",
- "body-delete-all-transformations": "By deleting all transformations, you will go back to the main selection screen.",
- "confirmText-delete-all": "Delete all",
- "delete-all-transformations": "Delete all transformations",
- "title-delete-all-transformations": "Delete all transformations?"
- },
"panel-edit-controls": {
"table-view-aria-label-toggletableview": "Toggle table view",
"table-view-label-table-view": "Table view"
@@ -6242,6 +6238,17 @@
"query-editor": {
"query": "Query"
},
+ "query-transform-card": {
+ "duplicate": "Duplicate query",
+ "hide-response": "Hide response",
+ "remove-query": "Remove query",
+ "remove-transform": "Remove transformation",
+ "show-response": "Show response"
+ },
+ "query-transform-list": {
+ "add-query": "Add query",
+ "add-transformation": "Add transformation"
+ },
"query-variable-editor-form": {
"description-examples": "Named capture groups can be used to separate the display text and value (<1>see examples1> ).",
"description-optional": "Optional, if you want to extract part of a series name or metric node segment.",
@@ -6352,6 +6359,11 @@
"title-same-as-folder": "Dashboard name cannot be the same as the folder name",
"title-validation-failed": "Dashboard title validation failed."
},
+ "scene-query-detail-view": {
+ "loading": "Loading data source...",
+ "no-editor": "This data source does not have a query editor",
+ "select-datasource": "Select data source"
+ },
"scenes-new-rule-from-panel-button": {
"body-no-alerting-capable-query-found": "Cannot create alerts from this panel because no query to an alerting capable datasource is found.",
"new-alert-rule": "New alert rule",