From 96e0f4718bca2eaf58c0158201f2e3baa6884915 Mon Sep 17 00:00:00 2001 From: Alex Spencer <52186778+alexjonspencer1@users.noreply.github.com> Date: Tue, 2 Dec 2025 15:14:25 -0800 Subject: [PATCH] chore: alex - fix transformation enabled/disabled, sections! --- .../PanelDataPane/PanelDataPane.tsx | 61 +++++++--- .../PanelDataPane/QueryTransformCard.tsx | 16 ++- .../PanelDataPane/QueryTransformList.tsx | 106 +++++++++++++----- 3 files changed, 134 insertions(+), 49 deletions(-) diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx index ad56c0e2696..ba611463ed3 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx @@ -137,13 +137,16 @@ function PanelDataPaneRendered({ model }: SceneComponentProps) { const dataTransformer = panel.state.$data instanceof SceneDataTransformer ? panel.state.$data : null; const transformerState = dataTransformer?.useState(); - // Build combined items list - const items: QueryTransformItem[] = useMemo(() => { - const result: QueryTransformItem[] = []; + // Build separate lists for queries/expressions and transformations + const { dataSourceItems, transformItems, allItems } = useMemo(() => { + const dataSourceItems: QueryTransformItem[] = []; + const transformItems: QueryTransformItem[] = []; + + // Add queries and expressions const queries = queryRunnerState?.queries; for (let i = 0; i < (queries?.length ?? 0); i++) { const query = queries![i]; - result.push({ + dataSourceItems.push({ id: queryItemId(query), type: isExpressionQuery(query) ? 'expression' : 'query', data: query, @@ -151,11 +154,12 @@ function PanelDataPaneRendered({ model }: SceneComponentProps) { }); } + // Add transformations const transformations = transformerState?.transformations; for (let i = 0; i < (transformations?.length ?? 0); i++) { const transform = transformations![i]; if (isDataTransformerConfig(transform)) { - result.push({ + transformItems.push({ id: transformItemId(i), type: 'transform', data: transform, @@ -164,20 +168,24 @@ function PanelDataPaneRendered({ model }: SceneComponentProps) { } } - return result; + return { + dataSourceItems, + transformItems, + allItems: [...dataSourceItems, ...transformItems], + }; }, [queryRunnerState?.queries, transformerState?.transformations]); // Auto-select first item if nothing is selected const effectiveSelectedId = useMemo(() => { - if (selectedId === null && items.length > 0) { - return items[0].id; + if (selectedId === null && allItems.length > 0) { + return allItems[0].id; } return selectedId; - }, [selectedId, items]); + }, [selectedId, allItems]); const selectedItem = useMemo(() => { - return items.find((item) => item.id === effectiveSelectedId); - }, [items, effectiveSelectedId]); + return allItems.find((item) => item.id === effectiveSelectedId); + }, [allItems, effectiveSelectedId]); const handleSelect = useCallback((id: string) => { setSelectedId(id); @@ -406,6 +414,22 @@ function PanelDataPaneRendered({ model }: SceneComponentProps) { [tabs, selectedId] ); + const handleToggleTransformVisibility = useCallback( + (index: number) => { + const transformsTab = tabs.find((t): t is PanelDataTransformationsTab => t.tabId === TabId.Transformations); + if (transformsTab) { + const transformations = (transformsTab.getDataTransformer().state.transformations || []).filter( + isDataTransformerConfig + ); + const newTransformations = transformations.map((t, i) => + i === index ? { ...t, disabled: t.disabled ? undefined : true } : t + ); + transformsTab.onChangeTransformations(newTransformations); + } + }, + [tabs] + ); + // Get data for transformations drawer const sourceData = queryRunner?.useState(); const series = sourceData?.data?.series || []; @@ -421,7 +445,9 @@ function PanelDataPaneRendered({ model }: SceneComponentProps) {
) { onRemoveExpression={handleRemoveExpression} onToggleExpressionVisibility={handleToggleExpressionVisibility} onRemoveTransform={handleRemoveTransform} + onToggleTransformVisibility={handleToggleTransformVisibility} />
{typeLabel}
- {(type === 'query' || type === 'expression') && onToggleVisibility && ( + {onToggleVisibility && ( handleAction(e, onToggleVisibility)} className={styles.actionButton} @@ -161,7 +167,7 @@ const getStyles = (theme: GrafanaTheme2, colors: ReturnType void; onAddQuery: () => void; @@ -32,11 +34,14 @@ interface QueryTransformListProps { onRemoveExpression?: (index: number) => void; onToggleExpressionVisibility?: (index: number) => void; onRemoveTransform?: (index: number) => void; + onToggleTransformVisibility?: (index: number) => void; } export const QueryTransformList = memo( ({ - items, + dataSourceItems, + transformItems, + allItems, selectedId, onSelect, onAddQuery, @@ -49,13 +54,14 @@ export const QueryTransformList = memo( onRemoveExpression, onToggleExpressionVisibility, onRemoveTransform, + onToggleTransformVisibility, }: QueryTransformListProps) => { const styles = useStyles2(getStyles); const allConnections = useMemo(() => { const conns: Array<{ from: string; to: string }> = []; - items.forEach((item) => { + allItems.forEach((item) => { if (item.type === 'expression' && 'expression' in item.data && 'refId' in item.data) { const expr = item.data; @@ -78,7 +84,7 @@ export const QueryTransformList = memo( }); return conns; - }, [items]); + }, [allItems]); // Filter connections to only show for selected card const visibleConnections = useMemo(() => { @@ -87,7 +93,7 @@ export const QueryTransformList = memo( } // Find the item to get its refId - const activeItem = items.find((item) => item.id === selectedId); + const activeItem = allItems.find((item) => item.id === selectedId); if (!activeItem || !('refId' in activeItem.data)) { return []; } @@ -96,7 +102,7 @@ export const QueryTransformList = memo( // Show connections where this card is involved (either as source or destination) return allConnections.filter((conn) => conn.from === activeRefId || conn.to === activeRefId); - }, [allConnections, selectedId, items]); + }, [allConnections, selectedId, allItems]); const getHandlers = (item: QueryTransformItem) => { switch (item.type) { @@ -118,23 +124,24 @@ export const QueryTransformList = memo( return { onDuplicate: undefined, onRemove: onRemoveTransform ? () => onRemoveTransform(item.index) : undefined, - onToggleVisibility: undefined, + onToggleVisibility: onToggleTransformVisibility ? () => onToggleTransformVisibility(item.index) : undefined, }; } }; const stats = useMemo(() => { - const totalCards = items.length; - const queries = items.filter((item) => item.type === 'query' || item.type === 'expression'); - const hiddenQueries = queries.filter((item) => 'hide' in item.data && item.data.hide); - const visibleQueries = queries.length - hiddenQueries.length; + const totalCards = allItems.length; + const hiddenQueries = dataSourceItems.filter((item) => 'hide' in item.data && item.data.hide); + const disabledTransforms = transformItems.filter((item) => 'disabled' in item.data && item.data.disabled); + const hiddenTotal = hiddenQueries.length + disabledTransforms.length; + const visibleTotal = totalCards - hiddenTotal; return { totalCards, - visibleQueries, - hiddenQueries: hiddenQueries.length, + visibleQueries: visibleTotal, + hiddenQueries: hiddenTotal, }; - }, [items]); + }, [allItems, dataSourceItems, transformItems]); return (
@@ -149,18 +156,46 @@ export const QueryTransformList = memo(
- - {items.map((item) => ( - onSelect(item.id)} - {...getHandlers(item)} - /> - ))} + + {/* Data Sources Section (Queries + Expressions) */} + {dataSourceItems.length > 0 && ( + +
+ {t('dashboard-scene.query-transform-list.queries-expressions', 'Queries & Expressions')} +
+ {dataSourceItems.map((item) => ( + onSelect(item.id)} + {...getHandlers(item)} + /> + ))} +
+ )} + + {/* Transformations Section */} + {transformItems.length > 0 && ( + +
+ {t('dashboard-scene.query-transform-list.transformations', 'Transformations')} +
+ {transformItems.map((item) => ( + onSelect(item.id)} + {...getHandlers(item)} + /> + ))} +
+ )} { textTransform: 'uppercase', color: theme.colors.text.primary, }), + sectionLabel: css({ + fontFamily: "'CommitMono', monospace", + fontSize: theme.typography.bodySmall.fontSize, + color: theme.colors.text.maxContrast, + textTransform: 'uppercase', + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1), + marginLeft: theme.spacing(-2), + marginRight: theme.spacing(-2), + '&::before, &::after': { + content: '""', + flex: 1, + height: '1px', + background: theme.colors.border.weak, + }, + }), scrollWrapper: css({ flex: 1, minHeight: 0, position: 'relative', }), content: css({ - padding: theme.spacing(2), + padding: theme.spacing(4), paddingRight: theme.spacing(6), }), footer: css({