Explore: Compact mode (#107351)
* feat(explore): implement compact mode feature This commit implements compact mode for Grafana Explore to address issue #102867. Features implemented: - URL parameter support: ?compact=true or ?compact=1 activates compact mode - Query rows auto-collapse: Query editors start collapsed showing summary line - Content outline auto-collapse: Sections start collapsed to save space - Single DrilldownAlertBox: Shows single banner in split view (no duplicates) Changes: - Added compactMode to ExploreState interface and selectors - Added URL parameter parsing in useStateSync hook - Modified QueryRows and QueryEditorRow components to support compact mode - Updated DrilldownAlertBox logic to prevent duplicates in split view - Fixed test files to use proper mock state objects Fixes #102867 * Move compact mode to pane state and sync with URL * Clean up * Show Drilldown alert box only when not splitted * Rename and clean up * Clean up * Add missing props * Add missing props * Add missing translations --------- Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com>
This commit is contained in:
co-authored by
Piotr Jamróz
parent
1f025fe1a3
commit
af066d2312
@@ -49,6 +49,7 @@ export interface ExploreUrlState<T extends DataQuery = AnyQuery> {
|
||||
queries: T[];
|
||||
range: URLRange;
|
||||
panelsState?: ExplorePanelsState;
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
export interface ExplorePanelsState extends Partial<Record<PreferredVisualisationType, {}>> {
|
||||
@@ -89,6 +90,7 @@ export interface SplitOpenOptions<T extends AnyQuery = AnyQuery> {
|
||||
range?: TimeRange;
|
||||
panelsState?: ExplorePanelsState;
|
||||
correlationHelperData?: ExploreCorrelationHelperData;
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -108,6 +108,8 @@ const dummyProps: Props = {
|
||||
dsToExplore: [],
|
||||
},
|
||||
changeDatasource: jest.fn(),
|
||||
compact: false,
|
||||
changeCompactMode: jest.fn(),
|
||||
};
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
|
||||
@@ -58,7 +58,7 @@ import { SecondaryActions } from './SecondaryActions';
|
||||
import TableContainer from './Table/TableContainer';
|
||||
import { TraceViewContainer } from './TraceView/TraceViewContainer';
|
||||
import { changeDatasource } from './state/datasource';
|
||||
import { changeSize } from './state/explorePane';
|
||||
import { changeSize, changeCompactMode } from './state/explorePane';
|
||||
import { splitOpen } from './state/main';
|
||||
import {
|
||||
addQueryRow,
|
||||
@@ -182,14 +182,16 @@ export class Explore extends PureComponent<Props, ExploreState> {
|
||||
onContentOutlineToogle = () => {
|
||||
store.set(CONTENT_OUTLINE_LOCAL_STORAGE_KEYS.visible, !this.state.contentOutlineVisible);
|
||||
this.setState((state) => {
|
||||
const newContentOutlineVisible = this.props.compact ? true : !state.contentOutlineVisible;
|
||||
reportInteraction('explore_toolbar_contentoutline_clicked', {
|
||||
item: 'outline',
|
||||
type: state.contentOutlineVisible ? 'close' : 'open',
|
||||
type: newContentOutlineVisible ? 'open' : 'close',
|
||||
});
|
||||
return {
|
||||
contentOutlineVisible: !state.contentOutlineVisible,
|
||||
contentOutlineVisible: newContentOutlineVisible,
|
||||
};
|
||||
});
|
||||
this.props.changeCompactMode(this.props.exploreId, false);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -306,9 +308,12 @@ export class Explore extends PureComponent<Props, ExploreState> {
|
||||
updateTimeRange({ exploreId, absoluteRange });
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for interaction from the visualizations. Will open split view in compact mode.
|
||||
*/
|
||||
onSplitOpen = (panelType: string) => {
|
||||
return async (options?: SplitOpenOptions) => {
|
||||
this.props.splitOpen(options);
|
||||
this.props.splitOpen(options ? { ...options, compact: true } : options);
|
||||
if (options && this.props.datasourceInstance) {
|
||||
const target = (await getDataSourceSrv().get(options.datasourceUid)).type;
|
||||
const source =
|
||||
@@ -573,6 +578,8 @@ export class Explore extends PureComponent<Props, ExploreState> {
|
||||
correlationEditorHelperData,
|
||||
showQueryInspector,
|
||||
setShowQueryInspector,
|
||||
splitted,
|
||||
compact,
|
||||
} = this.props;
|
||||
const { contentOutlineVisible } = this.state;
|
||||
const styles = getStyles(theme);
|
||||
@@ -614,7 +621,7 @@ export class Explore extends PureComponent<Props, ExploreState> {
|
||||
}}
|
||||
>
|
||||
<div className={styles.wrapper}>
|
||||
{contentOutlineVisible && (
|
||||
{contentOutlineVisible && !compact && (
|
||||
<ContentOutline scroller={this.scrollElement} panelId={`content-outline-container-${exploreId}`} />
|
||||
)}
|
||||
<ScrollContainer
|
||||
@@ -631,9 +638,19 @@ export class Explore extends PureComponent<Props, ExploreState> {
|
||||
mergeSingleChild={true}
|
||||
>
|
||||
<PanelContainer className={styles.queryContainer}>
|
||||
<DrilldownAlertBox datasourceType={datasourceInstance?.type || ''} />
|
||||
{!splitted && <DrilldownAlertBox datasourceType={datasourceInstance?.type || ''} />}
|
||||
{correlationsBox}
|
||||
<QueryRows exploreId={exploreId} />
|
||||
<QueryRows
|
||||
exploreId={exploreId}
|
||||
// Don't simply pass isOpen here to avoid opening the row when content outline is openend and
|
||||
// triggers exiting from compact mode. If it's confusing we can change the behavior to exit
|
||||
// compact mode explicitly with a button in the UI instead of exiting when row is opened or
|
||||
// content outline is opened.
|
||||
isOpen={compact ? false : undefined}
|
||||
changeCompactMode={(compact: boolean) =>
|
||||
this.props.changeCompactMode(this.props.exploreId, false)
|
||||
}
|
||||
/>
|
||||
<SecondaryActions
|
||||
// do not allow people to add queries with potentially different datasources in correlations editor mode
|
||||
addQueryRowButtonDisabled={
|
||||
@@ -748,6 +765,7 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
||||
showRawPrometheus,
|
||||
supplementaryQueries,
|
||||
correlationEditorHelperData,
|
||||
compact,
|
||||
} = item;
|
||||
|
||||
const loading = selectIsWaitingForData(exploreId)(state);
|
||||
@@ -774,6 +792,7 @@ function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
||||
showRawPrometheus,
|
||||
showFlameGraph,
|
||||
splitted: isSplit(state),
|
||||
compact,
|
||||
loading,
|
||||
logsSample,
|
||||
showLogsSample,
|
||||
@@ -794,6 +813,7 @@ const mapDispatchToProps = {
|
||||
addQueryRow,
|
||||
splitOpen,
|
||||
setSupplementaryQueryEnabled,
|
||||
changeCompactMode,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
||||
@@ -2,9 +2,9 @@ import { css, cx } from '@emotion/css';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans } from '@grafana/i18n';
|
||||
import { t, Trans } from '@grafana/i18n';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { ErrorBoundaryAlert, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { ErrorBoundaryAlert, LoadingPlaceholder, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { SplitPaneWrapper } from 'app/core/components/SplitPaneWrapper/SplitPaneWrapper';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
import { useNavModel } from 'app/core/hooks/useNavModel';
|
||||
@@ -84,10 +84,14 @@ function ExplorePageContent(props: GrafanaRouteComponentProps<{}, ExploreQueryPa
|
||||
paneStyle={{ overflow: 'auto', display: 'flex', flexDirection: 'column' }}
|
||||
onDragFinished={(size) => size && updateSplitSize(size)}
|
||||
>
|
||||
{panes.map(([exploreId]) => {
|
||||
{panes.map(([exploreId, pane]) => {
|
||||
return (
|
||||
<ErrorBoundaryAlert key={exploreId} style="page">
|
||||
<ExplorePaneContainer exploreId={exploreId} />
|
||||
{pane.initialized ? (
|
||||
<ExplorePaneContainer exploreId={exploreId} />
|
||||
) : (
|
||||
<LoadingPlaceholder text={t('explore.pane.loading-placeholder', 'Loading...')} />
|
||||
)}
|
||||
</ErrorBoundaryAlert>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -79,7 +79,7 @@ describe('Explore QueryRows', () => {
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<QueryRows exploreId={'left'} />
|
||||
<QueryRows exploreId={'left'} changeCompactMode={jest.fn()} />
|
||||
</Provider>
|
||||
);
|
||||
|
||||
@@ -100,7 +100,7 @@ describe('Explore QueryRows', () => {
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<QueryLibraryContextProviderMock queryLibraryEnabled={true}>
|
||||
<QueryRows exploreId={'left'} />
|
||||
<QueryRows exploreId={'left'} changeCompactMode={jest.fn()} />
|
||||
</QueryLibraryContextProviderMock>
|
||||
</Provider>
|
||||
);
|
||||
@@ -117,7 +117,7 @@ describe('Explore QueryRows', () => {
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<QueryLibraryContextProviderMock queryLibraryEnabled={false}>
|
||||
<QueryRows exploreId={'left'} />
|
||||
<QueryRows exploreId={'left'} changeCompactMode={jest.fn()} />
|
||||
</QueryLibraryContextProviderMock>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useCallback, useMemo } from 'react';
|
||||
import { CoreApp, getNextRefId } from '@grafana/data';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { DataQuery, DataSourceRef } from '@grafana/schema';
|
||||
import { ExploreItemState } from 'app/types/explore';
|
||||
import { useDispatch, useSelector } from 'app/types/store';
|
||||
|
||||
import { getDatasourceSrv } from '../plugins/datasource_srv';
|
||||
@@ -16,23 +17,25 @@ import { getExploreItemSelector } from './state/selectors';
|
||||
|
||||
interface Props {
|
||||
exploreId: string;
|
||||
changeCompactMode: (compact: boolean) => void;
|
||||
isOpen?: boolean;
|
||||
}
|
||||
|
||||
const makeSelectors = (exploreId: string) => {
|
||||
const exploreItemSelector = getExploreItemSelector(exploreId);
|
||||
return {
|
||||
getQueries: createSelector(exploreItemSelector, (s) => s!.queries),
|
||||
getQueryResponse: createSelector(exploreItemSelector, (s) => s!.queryResponse),
|
||||
getHistory: createSelector(exploreItemSelector, (s) => s!.history),
|
||||
getEventBridge: createSelector(exploreItemSelector, (s) => s!.eventBridge),
|
||||
getQueries: createSelector(exploreItemSelector, (s: ExploreItemState | undefined) => s!.queries),
|
||||
getQueryResponse: createSelector(exploreItemSelector, (s: ExploreItemState | undefined) => s!.queryResponse),
|
||||
getHistory: createSelector(exploreItemSelector, (s: ExploreItemState | undefined) => s!.history),
|
||||
getEventBridge: createSelector(exploreItemSelector, (s: ExploreItemState | undefined) => s!.eventBridge),
|
||||
getDatasourceInstanceSettings: createSelector(
|
||||
exploreItemSelector,
|
||||
(s) => getDatasourceSrv().getInstanceSettings(s!.datasourceInstance?.uid)!
|
||||
(s: ExploreItemState | undefined) => getDatasourceSrv().getInstanceSettings(s!.datasourceInstance?.uid)!
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const QueryRows = ({ exploreId }: Props) => {
|
||||
export const QueryRows = ({ exploreId, isOpen, changeCompactMode }: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
const { getQueries, getDatasourceInstanceSettings, getQueryResponse, getHistory, getEventBridge } = useMemo(
|
||||
() => makeSelectors(exploreId),
|
||||
@@ -86,6 +89,12 @@ export const QueryRows = ({ exploreId }: Props) => {
|
||||
reportInteraction('grafana_query_row_toggle', queryStatus === undefined ? {} : { queryEnabled: queryStatus });
|
||||
};
|
||||
|
||||
const onQueryOpenChanged = () => {
|
||||
// Disables compact mode when query is opened.
|
||||
// Compact mode can also be disabled by opening Content Outline.
|
||||
changeCompactMode(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<QueryEditorRows
|
||||
dsSettings={dsSettings}
|
||||
@@ -98,10 +107,12 @@ export const QueryRows = ({ exploreId }: Props) => {
|
||||
onQueryRemoved={onQueryRemoved}
|
||||
onQueryToggled={onQueryToggled}
|
||||
onQueryReplacedFromLibrary={onQueryReplacedFromLibrary}
|
||||
onQueryOpenChanged={onQueryOpenChanged}
|
||||
data={queryResponse}
|
||||
app={CoreApp.Explore}
|
||||
history={history}
|
||||
eventBus={eventBridge}
|
||||
isOpen={isOpen}
|
||||
queryRowWrapper={(children, refId) => (
|
||||
<ContentOutlineItem
|
||||
title={refId}
|
||||
|
||||
@@ -13,6 +13,7 @@ export function getUrlStateFromPaneState(pane: ExploreItemState): ExploreUrlStat
|
||||
range: toURLRange(pane.range.raw),
|
||||
// don't include panelsState in the url unless a piece of state is actually set
|
||||
panelsState: pruneObject(pane.panelsState),
|
||||
compact: pane.compact,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -118,5 +118,6 @@ function applyDefaults(input: unknown): ExploreUrlState {
|
||||
hasKey('to', input.range) &&
|
||||
typeof input.range.from === 'string' &&
|
||||
typeof input.range.to === 'string' && { range: { from: input.range.from, to: input.range.to } }),
|
||||
...(hasKey('compact', input) && typeof input.compact === 'boolean' && { compact: input.compact }),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ export function syncFromURL(
|
||||
queries: withUniqueRefIds(queries),
|
||||
range: fromURLRange(range),
|
||||
panelsState,
|
||||
compact: !!urlPane.compact,
|
||||
position: i,
|
||||
eventBridge: new EventBusSrv(),
|
||||
})
|
||||
|
||||
@@ -33,7 +33,7 @@ export function initializeFromURL(
|
||||
dispatch(clearPanes());
|
||||
|
||||
Promise.all(
|
||||
Object.entries(urlState.panes).map(([exploreId, { datasource, queries, range, panelsState }]) => {
|
||||
Object.entries(urlState.panes).map(([exploreId, { datasource, queries, range, panelsState, compact }]) => {
|
||||
return getPaneDatasource(datasource, queries, orgId).then((paneDatasource) => {
|
||||
return Promise.resolve(
|
||||
// Given the Grafana datasource will always be present, this should always be defined.
|
||||
@@ -64,13 +64,13 @@ export function initializeFromURL(
|
||||
];
|
||||
}
|
||||
|
||||
return { exploreId, range, panelsState, queries: validQueries, datasource: paneDatasource };
|
||||
return { exploreId, compact, range, panelsState, queries: validQueries, datasource: paneDatasource };
|
||||
});
|
||||
});
|
||||
})
|
||||
).then(async (panes) => {
|
||||
const initializedPanes = await Promise.all(
|
||||
panes.map(({ exploreId, range, panelsState, queries, datasource }) => {
|
||||
panes.map(({ exploreId, range, panelsState, queries, datasource, compact }) => {
|
||||
return dispatch(
|
||||
initializeExplore({
|
||||
exploreId,
|
||||
@@ -79,6 +79,7 @@ export function initializeFromURL(
|
||||
range: fromURLRange(range),
|
||||
panelsState,
|
||||
eventBridge: new EventBusSrv(),
|
||||
compact: !!compact,
|
||||
})
|
||||
).unwrap();
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ import { MutableRefObject } from 'react';
|
||||
import { UrlQueryMap } from '@grafana/data';
|
||||
import { LocationService } from '@grafana/runtime';
|
||||
import { changeDatasource } from 'app/features/explore/state/datasource';
|
||||
import { changePanelsStateAction } from 'app/features/explore/state/explorePane';
|
||||
import { changeCompactModeAction, changePanelsStateAction } from 'app/features/explore/state/explorePane';
|
||||
import { splitClose, splitOpen } from 'app/features/explore/state/main';
|
||||
import { runQueries } from 'app/features/explore/state/query';
|
||||
import { changeRangeAction } from 'app/features/explore/state/time';
|
||||
@@ -21,6 +21,7 @@ We want to update the URL when:
|
||||
- range is changed
|
||||
- panel state is updated
|
||||
- a datasource change has completed.
|
||||
- compact mode changes
|
||||
|
||||
Note: Changing datasource causes a bunch of actions to be dispatched, we want to update the URL
|
||||
only when the change set has completed. This is done by checking if the changeDatasource.pending action
|
||||
@@ -37,6 +38,7 @@ export function syncToURLPredicate(paused: MutableRefObject<boolean>, action: Ac
|
||||
changeRangeAction.type,
|
||||
changePanelsStateAction.type,
|
||||
changeDatasource.fulfilled.type,
|
||||
changeCompactModeAction.type,
|
||||
].includes(action.type) && !paused.current
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,12 @@ export interface ChangeSizePayload {
|
||||
|
||||
export const changeSizeAction = createAction<ChangeSizePayload>('explore/changeSize');
|
||||
|
||||
interface ChangeCompactModePayload {
|
||||
exploreId: string;
|
||||
compact: boolean;
|
||||
}
|
||||
export const changeCompactModeAction = createAction<ChangeCompactModePayload>('explore/changeCompactMode');
|
||||
|
||||
/**
|
||||
* Tracks the state of explore panels that gets synced with the url.
|
||||
*/
|
||||
@@ -101,6 +107,7 @@ interface InitializeExplorePayload {
|
||||
range: TimeRange;
|
||||
history: HistoryItem[];
|
||||
datasourceInstance?: DataSourceApi;
|
||||
compact: boolean;
|
||||
eventBridge: EventBusExtended;
|
||||
}
|
||||
|
||||
@@ -127,6 +134,10 @@ export function changeSize(exploreId: string, { width }: { width: number }): Pay
|
||||
return changeSizeAction({ exploreId, width });
|
||||
}
|
||||
|
||||
export function changeCompactMode(exploreId: string, compact: boolean): PayloadAction<ChangeCompactModePayload> {
|
||||
return changeCompactModeAction({ exploreId, compact });
|
||||
}
|
||||
|
||||
export interface InitializeExploreOptions {
|
||||
exploreId: string;
|
||||
datasource: DataSourceRef | string | undefined;
|
||||
@@ -136,6 +147,7 @@ export interface InitializeExploreOptions {
|
||||
correlationHelperData?: ExploreCorrelationHelperData;
|
||||
position?: number;
|
||||
eventBridge: EventBusExtended;
|
||||
compact: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,6 +167,7 @@ export const initializeExplore = createAsyncThunk(
|
||||
queries,
|
||||
range,
|
||||
panelsState,
|
||||
compact,
|
||||
correlationHelperData,
|
||||
eventBridge,
|
||||
}: InitializeExploreOptions,
|
||||
@@ -177,6 +190,7 @@ export const initializeExplore = createAsyncThunk(
|
||||
range: getRange(range, getTimeZone(getState().user)),
|
||||
datasourceInstance: instance,
|
||||
history,
|
||||
compact,
|
||||
eventBridge,
|
||||
})
|
||||
);
|
||||
@@ -226,6 +240,11 @@ export const paneReducer = (state: ExploreItemState = makeExplorePaneState(), ac
|
||||
return { ...state, containerWidth };
|
||||
}
|
||||
|
||||
if (changeCompactModeAction.match(action)) {
|
||||
const compact = action.payload.compact;
|
||||
return { ...state, compact };
|
||||
}
|
||||
|
||||
if (changePanelsStateAction.match(action)) {
|
||||
const { panelsState } = action.payload;
|
||||
return { ...state, panelsState };
|
||||
@@ -244,7 +263,7 @@ export const paneReducer = (state: ExploreItemState = makeExplorePaneState(), ac
|
||||
}
|
||||
|
||||
if (initializeExploreAction.match(action)) {
|
||||
const { queries, range, datasourceInstance, history, eventBridge } = action.payload;
|
||||
const { queries, range, datasourceInstance, history, eventBridge, compact } = action.payload;
|
||||
|
||||
return {
|
||||
...state,
|
||||
@@ -258,6 +277,7 @@ export const paneReducer = (state: ExploreItemState = makeExplorePaneState(), ac
|
||||
queryResponse: createEmptyQueryResponse(),
|
||||
cache: [],
|
||||
correlations: [],
|
||||
compact,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ export const splitOpen = createAsyncThunk(
|
||||
panelsState: options?.panelsState || originState?.panelsState,
|
||||
correlationHelperData: options?.correlationHelperData,
|
||||
eventBridge: new EventBusSrv(),
|
||||
compact: !!options?.compact,
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ export const makeExplorePaneState = (overrides?: Partial<ExploreItemState>): Exp
|
||||
supplementaryQueries: loadSupplementaryQueries(),
|
||||
panelsState: {},
|
||||
correlations: undefined,
|
||||
compact: false,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
|
||||
@@ -64,9 +64,11 @@ export interface Props<TQuery extends DataQuery> {
|
||||
onQueryCopied?: () => void;
|
||||
onQueryRemoved?: () => void;
|
||||
onQueryToggled?: (queryStatus?: boolean | undefined) => void;
|
||||
onQueryOpenChanged?: (status?: boolean | undefined) => void;
|
||||
onQueryReplacedFromLibrary?: () => void;
|
||||
collapsable?: boolean;
|
||||
hideRefId?: boolean;
|
||||
isOpen?: boolean;
|
||||
}
|
||||
|
||||
interface State<TQuery extends DataQuery> {
|
||||
@@ -433,7 +435,7 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
|
||||
};
|
||||
|
||||
render() {
|
||||
const { query, index, visualization, collapsable, hideActionButtons } = this.props;
|
||||
const { query, index, visualization, collapsable, hideActionButtons, isOpen, onQueryOpenChanged } = this.props;
|
||||
const { datasource, showingHelp, data } = this.state;
|
||||
const isHidden = query.hide;
|
||||
const error =
|
||||
@@ -459,6 +461,8 @@ export class QueryEditorRow<TQuery extends DataQuery> extends PureComponent<Prop
|
||||
index={index}
|
||||
headerElement={this.renderHeader}
|
||||
actions={hideActionButtons ? undefined : this.renderActions}
|
||||
isOpen={isOpen}
|
||||
onOpen={onQueryOpenChanged}
|
||||
>
|
||||
<div className={rowClasses} id={this.id}>
|
||||
<ErrorBoundaryAlert>
|
||||
|
||||
@@ -37,9 +37,11 @@ export interface Props {
|
||||
onQueryCopied?: () => void;
|
||||
onQueryRemoved?: () => void;
|
||||
onQueryToggled?: (queryStatus?: boolean | undefined) => void;
|
||||
onQueryOpenChanged?: (status?: boolean | undefined) => void;
|
||||
onUpdateDatasources?: (datasource: DataSourceRef) => void;
|
||||
onQueryReplacedFromLibrary?: () => void;
|
||||
queryRowWrapper?: (children: ReactNode, refId: string) => ReactNode;
|
||||
isOpen?: boolean;
|
||||
}
|
||||
|
||||
export class QueryEditorRows extends PureComponent<Props> {
|
||||
@@ -173,8 +175,10 @@ export class QueryEditorRows extends PureComponent<Props> {
|
||||
onQueryCopied,
|
||||
onQueryRemoved,
|
||||
onQueryToggled,
|
||||
onQueryOpenChanged,
|
||||
onQueryReplacedFromLibrary,
|
||||
queryRowWrapper,
|
||||
isOpen,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@@ -206,12 +210,14 @@ export class QueryEditorRows extends PureComponent<Props> {
|
||||
onQueryCopied={onQueryCopied}
|
||||
onQueryRemoved={onQueryRemoved}
|
||||
onQueryToggled={onQueryToggled}
|
||||
onQueryOpenChanged={onQueryOpenChanged}
|
||||
onQueryReplacedFromLibrary={onQueryReplacedFromLibrary}
|
||||
queries={queries}
|
||||
app={app}
|
||||
range={getTimeSrv().timeRange()}
|
||||
history={history}
|
||||
eventBus={eventBus}
|
||||
isOpen={isOpen}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -229,6 +229,11 @@ export interface ExploreItemState {
|
||||
correlationEditorHelperData?: ExploreCorrelationHelperData;
|
||||
|
||||
correlations?: CorrelationData[];
|
||||
|
||||
/**
|
||||
* If set to true, all query rows will be collapsed initially and the content outline will be hidden
|
||||
*/
|
||||
compact: boolean;
|
||||
}
|
||||
|
||||
export interface ExploreUpdateState {
|
||||
|
||||
@@ -7006,6 +7006,9 @@
|
||||
"pro-tip-define-sources-through-configuration-files": " ProTip: You can also define data sources through configuration files. "
|
||||
}
|
||||
},
|
||||
"pane": {
|
||||
"loading-placeholder": "Loading..."
|
||||
},
|
||||
"prev": "Prev",
|
||||
"queryless-apps-extensions": {
|
||||
"aria-label-go-queryless": "Go queryless"
|
||||
|
||||
Reference in New Issue
Block a user