diff --git a/public/app/core/components/QueryOperationRow/QueryOperationRow.tsx b/public/app/core/components/QueryOperationRow/QueryOperationRow.tsx index 83c38c9a0bc..5d6b3edb8d3 100644 --- a/public/app/core/components/QueryOperationRow/QueryOperationRow.tsx +++ b/public/app/core/components/QueryOperationRow/QueryOperationRow.tsx @@ -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} /> {isContentVisible &&
{children}
} @@ -155,7 +155,7 @@ export function QueryOperationRow({ reportDragMousePosition={reportDragMousePosition} title={title} expanderMessages={expanderMessages} - isFocused={isFocused} + highlight={highlight} /> {isContentVisible &&
{children}
} diff --git a/public/app/core/components/QueryOperationRow/QueryOperationRowHeader.tsx b/public/app/core/components/QueryOperationRow/QueryOperationRowHeader.tsx index 9e05979f8a2..7ace50b64a3 100644 --- a/public/app/core/components/QueryOperationRow/QueryOperationRowHeader.tsx +++ b/public/app/core/components/QueryOperationRow/QueryOperationRowHeader.tsx @@ -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 ( -
+
{collapsable && ( ({ outline: 'none', }, }), - focused: css({ + highlighted: css({ border: `2px solid ${theme.colors.primary.border}`, }), column: css({ diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx index 484bfda39d3..8881c23d3af 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx @@ -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; + hasFocusedQuery?: boolean; } export class PanelDataQueriesTab extends SceneObjectBase implements PanelDataPaneTab { static Component = PanelDataQueriesTabRendered; @@ -251,6 +251,10 @@ export class PanelDataQueriesTab extends SceneObjectBase { + this.setState({ hasFocusedQuery }); + }; + public getQueries() { return this.queryRunner.state.queries; } @@ -336,19 +340,15 @@ export class PanelDataQueriesTab extends SceneObjectBase) { - 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 - - {showAddButton && ( - <> - - {queryLibraryEnabled && ( + {showActionButtons && ( + + {canAddQueries && ( + <> - )} - - )} - {showExpressionButton && ( - - - - )} - {model.renderExtraActions()} - + {queryLibraryEnabled && ( + + )} + + )} + {canAddExpressions && ( + + + + )} + {model.renderExtraActions()} + + )}
); } diff --git a/public/app/features/query/components/QueryEditorRow.tsx b/public/app/features/query/components/QueryEditorRow.tsx index dda45418910..a9859853bc5 100644 --- a/public/app/features/query/components/QueryEditorRow.tsx +++ b/public/app/features/query/components/QueryEditorRow.tsx @@ -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 extends PureComponent { - 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 ? ( - - ) : null; - - // Data source help (toggle action) - if (hasEditorHelp) { - menuItems.push( + return [ + // Data source help + hasEditorHelp && ( extends PureComponent - ); - } - - // Duplicate query - if (!isEditingQueryLibrary) { - menuItems.push( + ), + // Duplicate query + !isEditingQueryLibrary && ( - ); - } - - // Focus query - if (onFocusQuery) { - menuItems.push( + ), + // Focus query + onFocusQuery && ( extends PureComponent - ); + ), + ].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 ( + + ); + }; + + 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 extends PureComponent {savedQueryButtons} {extraActions} - {!hideHideQueryButton ? ( + {!hideHideQueryButton && ( extends PureComponent - ) : null} + )} {!isEditingQueryLibrary && ( extends PureComponent
diff --git a/public/app/features/query/components/QueryEditorRows.tsx b/public/app/features/query/components/QueryEditorRows.tsx index 6fbb651e64c..30f639ff1b1 100644 --- a/public/app/features/query/components/QueryEditorRows.tsx +++ b/public/app/features/query/components/QueryEditorRows.tsx @@ -179,6 +179,11 @@ export class QueryEditorRows extends PureComponent { + const newFocusedRefId = this.state.focusedQueryRefId === refId ? null : refId; + this.setFocusedQueryRefId(newFocusedRefId); + }; + render() { const { dsSettings, @@ -252,9 +257,7 @@ export class QueryEditorRows extends PureComponent - this.setFocusedQueryRefId(focusedQueryRefId === query.refId ? null : query.refId) - } + onFocusQuery={() => this.toggleFocusedQuery(query.refId)} /> );