chore: some refactor + cleanup

This commit is contained in:
Alex Spencer
2025-11-17 13:55:11 -08:00
parent e2f99e10b2
commit d912dbfc8f
5 changed files with 112 additions and 105 deletions
@@ -24,7 +24,7 @@ export interface QueryOperationRowProps {
collapsable?: boolean;
disabled?: boolean;
expanderMessages?: ExpanderMessages;
isFocused?: boolean;
highlight?: boolean;
}
export type QueryOperationRowRenderProp = ((props: QueryOperationRowRenderProps) => React.ReactNode) | React.ReactNode;
@@ -49,7 +49,7 @@ export function QueryOperationRow({
index,
id,
expanderMessages,
isFocused = false,
highlight = false,
}: QueryOperationRowProps) {
const [isContentVisible, setIsContentVisible] = useState(isOpen !== undefined ? isOpen : true);
const styles = useStyles2(getQueryOperationRowStyles);
@@ -129,7 +129,7 @@ export function QueryOperationRow({
reportDragMousePosition={reportDragMousePosition}
title={title}
expanderMessages={expanderMessages}
isFocused={isFocused}
highlight={highlight}
/>
</div>
{isContentVisible && <div className={styles.content}>{children}</div>}
@@ -155,7 +155,7 @@ export function QueryOperationRow({
reportDragMousePosition={reportDragMousePosition}
title={title}
expanderMessages={expanderMessages}
isFocused={isFocused}
highlight={highlight}
/>
{isContentVisible && <div className={styles.content}>{children}</div>}
</div>
@@ -20,7 +20,7 @@ export interface QueryOperationRowHeaderProps {
title?: string;
id: string;
expanderMessages?: ExpanderMessages;
isFocused?: boolean;
highlight?: boolean;
}
export interface ExpanderMessages {
@@ -41,7 +41,7 @@ export const QueryOperationRowHeader = ({
title,
id,
expanderMessages,
isFocused = false,
highlight = false,
}: QueryOperationRowHeaderProps) => {
const styles = useStyles2(getStyles);
@@ -57,7 +57,7 @@ export const QueryOperationRowHeader = ({
const dragAndDropLabel = t('query-operation.header.drag-and-drop', 'Drag and drop to reorder');
return (
<div className={cx(styles.header, isFocused && styles.focused)}>
<div className={cx(styles.header, { [styles.highlighted]: highlight })}>
<div className={styles.column}>
{collapsable && (
<IconButton
@@ -109,7 +109,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
outline: 'none',
},
}),
focused: css({
highlighted: css({
border: `2px solid ${theme.colors.primary.border}`,
}),
column: css({
@@ -1,5 +1,4 @@
import { css, cx } from '@emotion/css';
import { useState } from 'react';
import { CoreApp, DataSourceApi, DataSourceInstanceSettings, getDataSourceRef } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
@@ -47,6 +46,7 @@ interface PanelDataQueriesTabState extends SceneObjectState {
datasource?: DataSourceApi;
dsSettings?: DataSourceInstanceSettings;
panelRef: SceneObjectRef<VizPanel>;
hasFocusedQuery?: boolean;
}
export class PanelDataQueriesTab extends SceneObjectBase<PanelDataQueriesTabState> implements PanelDataPaneTab {
static Component = PanelDataQueriesTabRendered;
@@ -251,6 +251,10 @@ export class PanelDataQueriesTab extends SceneObjectBase<PanelDataQueriesTabStat
this.queryRunner.runQueries();
};
public onFocusQuery = (hasFocusedQuery: boolean) => {
this.setState({ hasFocusedQuery });
};
public getQueries() {
return this.queryRunner.state.queries;
}
@@ -336,19 +340,15 @@ export class PanelDataQueriesTab extends SceneObjectBase<PanelDataQueriesTabStat
}
export function PanelDataQueriesTabRendered({ model }: SceneComponentProps<PanelDataQueriesTab>) {
const { datasource, dsSettings } = model.useState();
const { datasource, dsSettings, hasFocusedQuery } = model.useState();
const { data, queries } = model.queryRunner.useState();
const { openDrawer: openQueryLibraryDrawer, queryLibraryEnabled } = useQueryLibraryContext();
const [hasFocusedQuery, setHasFocusedQuery] = useState(false);
const styles = useStyles2(getStyles);
if (!datasource || !dsSettings || !data) {
return null;
}
const showAddButton = !isSharedDashboardQuery(dsSettings.name) && !hasFocusedQuery;
const showExpressionButton =
config.expressionsEnabled && model.isExpressionsSupported(dsSettings) && !hasFocusedQuery;
const onSelectQueryFromLibrary = async (query: DataQuery) => {
// ensure all queries explicitly define a datasource
const enrichedQueries = queries.map((q) =>
@@ -374,6 +374,10 @@ export function PanelDataQueriesTabRendered({ model }: SceneComponentProps<Panel
}
};
const canAddQueries = !isSharedDashboardQuery(dsSettings.name);
const canAddExpressions = config.expressionsEnabled && model.isExpressionsSupported(dsSettings);
const showActionButtons = !hasFocusedQuery;
return (
<div
data-testid={selectors.components.QueryTab.content}
@@ -396,52 +400,54 @@ export function PanelDataQueriesTabRendered({ model }: SceneComponentProps<Panel
queries={queries}
dsSettings={dsSettings}
onAddQuery={model.onAddQuery}
onFocusQuery={setHasFocusedQuery}
onFocusQuery={model.onFocusQuery}
onQueriesChange={model.onQueriesChange}
onRunQueries={model.onRunQueries}
onUpdateDatasources={queryLibraryEnabled ? model.updateDatasourceIfNeeded : undefined}
app={CoreApp.PanelEditor}
/>
<Stack gap={2}>
{showAddButton && (
<>
<Button
icon="plus"
onClick={model.addQueryClick}
variant="secondary"
data-testid={selectors.components.QueryTab.addQuery}
>
<Trans i18nKey="dashboard-scene.panel-data-queries-tab-rendered.add-query">Add query</Trans>
</Button>
{queryLibraryEnabled && (
{showActionButtons && (
<Stack gap={2}>
{canAddQueries && (
<>
<Button
icon="plus"
onClick={() =>
openQueryLibraryDrawer({
onSelectQuery: onSelectQueryFromLibrary,
options: {
context: CoreApp.PanelEditor,
},
})
}
onClick={model.addQueryClick}
variant="secondary"
data-testid={selectors.components.QueryTab.addQueryFromLibrary}
data-testid={selectors.components.QueryTab.addQuery}
>
<Trans i18nKey={'dashboards.panel-queries.add-from-saved-queries'}>Add from saved queries</Trans>
<Trans i18nKey="dashboard-scene.panel-data-queries-tab-rendered.add-query">Add query</Trans>
</Button>
)}
</>
)}
{showExpressionButton && (
<ExpressionTypeDropdown handleOnSelect={model.onAddExpressionOfType}>
<Button icon="plus" variant="secondary" data-testid={selectors.components.QueryTab.addExpression}>
<Trans i18nKey="dashboard-scene.panel-data-queries-tab-rendered.expression">Expression&nbsp;</Trans>
</Button>
</ExpressionTypeDropdown>
)}
{model.renderExtraActions()}
</Stack>
{queryLibraryEnabled && (
<Button
icon="plus"
onClick={() =>
openQueryLibraryDrawer({
onSelectQuery: onSelectQueryFromLibrary,
options: {
context: CoreApp.PanelEditor,
},
})
}
variant="secondary"
data-testid={selectors.components.QueryTab.addQueryFromLibrary}
>
<Trans i18nKey={'dashboards.panel-queries.add-from-saved-queries'}>Add from saved queries</Trans>
</Button>
)}
</>
)}
{canAddExpressions && (
<ExpressionTypeDropdown handleOnSelect={model.onAddExpressionOfType}>
<Button icon="plus" variant="secondary" data-testid={selectors.components.QueryTab.addExpression}>
<Trans i18nKey="dashboard-scene.panel-data-queries-tab-rendered.expression">Expression&nbsp;</Trans>
</Button>
</ExpressionTypeDropdown>
)}
{model.renderExtraActions()}
</Stack>
)}
</div>
);
}
@@ -23,7 +23,7 @@ import { selectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n';
import { getDataSourceSrv, renderLimitedComponents, reportInteraction, usePluginComponents } from '@grafana/runtime';
import { DataQuery } from '@grafana/schema';
import { Badge, Button, Dropdown, ErrorBoundaryAlert, Icon, List, Menu, Stack, Text, TextLink } from '@grafana/ui';
import { Badge, Button, Dropdown, ErrorBoundaryAlert, Icon, List, Menu, Stack, Text } from '@grafana/ui';
import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp';
import {
QueryOperationAction,
@@ -393,44 +393,15 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
return extraActions;
};
renderActions = (props: QueryOperationRowRenderProps) => {
const {
query,
hideHideQueryButton: hideHideQueryButton = false,
queryLibraryRef,
app,
isFocused,
onFocusQuery,
} = this.props;
buildMenuItems = (): ReactNode[] => {
const { isFocused, onFocusQuery } = this.props;
const { datasource, showingHelp } = this.state;
const isHidden = !!query.hide;
const hasEditorHelp = datasource?.components?.QueryEditorHelp;
const isEditingQueryLibrary = queryLibraryRef !== undefined;
const isUnifiedAlerting = app === CoreApp.UnifiedAlerting;
const isExpressionQuery = query.datasource?.uid === ExpressionDatasourceUID;
const isEditingQueryLibrary = this.props.queryLibraryRef !== undefined;
// Build menu items for the actions dropdown
const menuItems: ReactNode[] = [];
// Saved query buttons (if applicable) - keep separate as it returns complex ReactNode
const savedQueryButtons =
!isEditingQueryLibrary && !isUnifiedAlerting && !isExpressionQuery ? (
<SavedQueryButtons
query={{
...query,
datasource: datasource ? { uid: datasource.uid, type: datasource.type } : query.datasource,
}}
app={app}
onUpdateSuccess={this.onExitQueryLibraryEditingMode}
onSelectQuery={this.onSelectQueryFromLibrary}
datasourceFilters={datasource?.name ? [datasource.name] : []}
/>
) : null;
// Data source help (toggle action)
if (hasEditorHelp) {
menuItems.push(
return [
// Data source help
hasEditorHelp && (
<Menu.Item
key="datasource-help"
label={
@@ -442,24 +413,18 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
onClick={this.onToggleHelp}
active={showingHelp}
/>
);
}
// Duplicate query
if (!isEditingQueryLibrary) {
menuItems.push(
),
// Duplicate query
!isEditingQueryLibrary && (
<Menu.Item
key="duplicate-query"
label={t('query-operation.header.duplicate-query', 'Duplicate query')}
icon="copy"
onClick={this.onCopyQuery}
/>
);
}
// Focus query
if (onFocusQuery) {
menuItems.push(
),
// Focus query
onFocusQuery && (
<Menu.Item
key="focus-query"
label={
@@ -472,11 +437,44 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
active={Boolean(isFocused)}
testId={selectors.components.QueryEditorRow.actionButton('Focus query')}
/>
);
),
].filter((item): item is JSX.Element => Boolean(item));
};
renderSavedQueryButtons = (): ReactNode => {
const { query, app, queryLibraryRef } = this.props;
const { datasource } = this.state;
const isUnifiedAlerting = app === CoreApp.UnifiedAlerting;
const isExpressionQuery = query.datasource?.uid === ExpressionDatasourceUID;
const isEditingQueryLibrary = queryLibraryRef !== undefined;
if (isEditingQueryLibrary || isUnifiedAlerting || isExpressionQuery) {
return null;
}
// Extra actions (warnings, badges, etc.) - keep separate as they return complex ReactNodes
return (
<SavedQueryButtons
query={{
...query,
datasource: datasource ? { uid: datasource.uid, type: datasource.type } : query.datasource,
}}
app={app}
onUpdateSuccess={this.onExitQueryLibraryEditingMode}
onSelectQuery={this.onSelectQueryFromLibrary}
datasourceFilters={datasource?.name ? [datasource.name] : []}
/>
);
};
renderActions = () => {
const { query, hideHideQueryButton = false, queryLibraryRef } = this.props;
const isHidden = !!query.hide;
const isEditingQueryLibrary = queryLibraryRef !== undefined;
// Build all action components
const savedQueryButtons = this.renderSavedQueryButtons();
const extraActions = this.renderExtraActions();
const menuItems = this.buildMenuItems();
// Only render dropdown if there are menu items
const actionsDropdown =
@@ -496,7 +494,7 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
<>
{savedQueryButtons}
{extraActions}
{!hideHideQueryButton ? (
{!hideHideQueryButton && (
<QueryOperationToggleAction
dataTestId={selectors.components.QueryEditorRow.actionButton('Hide response')}
title={
@@ -508,7 +506,7 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
active={isHidden}
onClick={this.onHideQuery}
/>
) : null}
)}
{!isEditingQueryLibrary && (
<QueryOperationAction
title={t('query-operation.header.remove-query', 'Remove query')}
@@ -586,7 +584,7 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
actions={hideActionButtons ? undefined : this.renderActions}
isOpen={isOpen}
onOpen={onQueryOpenChanged}
isFocused={isFocused}
highlight={isFocused}
>
<div className={rowClasses} id={this.id}>
<ErrorBoundaryAlert boundaryName="query-editor-operation-row">
@@ -179,6 +179,11 @@ export class QueryEditorRows extends PureComponent<Props, { focusedQueryRefId: s
this.props.onFocusQuery?.(Boolean(refId));
};
toggleFocusedQuery = (refId: string) => {
const newFocusedRefId = this.state.focusedQueryRefId === refId ? null : refId;
this.setFocusedQueryRefId(newFocusedRefId);
};
render() {
const {
dsSettings,
@@ -252,9 +257,7 @@ export class QueryEditorRows extends PureComponent<Props, { focusedQueryRefId: s
onCancelQueryLibraryEdit={onCancelQueryLibraryEdit}
isOpen={isOpen}
isFocused={focusedQueryRefId === query.refId}
onFocusQuery={() =>
this.setFocusedQueryRefId(focusedQueryRefId === query.refId ? null : query.refId)
}
onFocusQuery={() => this.toggleFocusedQuery(query.refId)}
/>
);