chore: some refactor + cleanup
This commit is contained in:
@@ -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({
|
||||
|
||||
+47
-41
@@ -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 </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 </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)}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user