@grafana/data#ExploreMode TypeScript Examples
The following examples show how to use
@grafana/data#ExploreMode.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: explore.test.ts From grafana-chinese with Apache License 2.0 | 6 votes |
DEFAULT_EXPLORE_STATE: ExploreUrlState = {
datasource: null,
queries: [],
range: DEFAULT_RANGE,
mode: ExploreMode.Metrics,
ui: {
showingGraph: true,
showingTable: true,
showingLogs: true,
dedupStrategy: LogsDedupStrategy.none,
},
originPanelId: undefined,
}
Example #2
Source File: richHistory.ts From grafana-chinese with Apache License 2.0 | 6 votes |
createUrlFromRichHistory = (query: RichHistoryQuery) => {
const queries = query.queries.map(query => ({ expr: query }));
const exploreState: ExploreUrlState = {
/* Default range, as we are not saving timerange in rich history */
range: { from: 'now-1h', to: 'now' },
datasource: query.datasourceName,
queries,
/* Default mode. In the future, we can also save the query mode */
mode: query.datasourceId === 'loki' ? ExploreMode.Logs : ExploreMode.Metrics,
ui: {
showingGraph: true,
showingLogs: true,
showingTable: true,
},
context: 'explore',
};
const serializedState = serializeStateToUrlParam(exploreState, true);
const baseUrl = /.*(?=\/explore)/.exec(`${window.location.href}`)[0];
const url = renderUrl(`${baseUrl}/explore`, { left: serializedState });
return url;
}
Example #3
Source File: LokiExploreQueryEditor.test.tsx From grafana-chinese with Apache License 2.0 | 6 votes |
describe('LokiExploreQueryEditor', () => {
let originalGetSelection: typeof window.getSelection;
beforeAll(() => {
originalGetSelection = window.getSelection;
window.getSelection = () => null;
});
afterAll(() => {
window.getSelection = originalGetSelection;
});
it('should render component', () => {
const wrapper = setup(shallow);
expect(wrapper).toMatchSnapshot();
});
it('should render LokiQueryField with ExtraFieldElement when ExploreMode is set to Logs', async () => {
await act(async () => {
const wrapper = setup(mount);
expect(wrapper.find(LokiExploreExtraField).length).toBe(1);
});
});
it('should render LokiQueryField with no ExtraFieldElement when ExploreMode is not Logs', async () => {
await act(async () => {
const wrapper = setup(mount, { exploreMode: ExploreMode.Metrics });
expect(wrapper.find(LokiExploreExtraField).length).toBe(0);
});
});
});
Example #4
Source File: ResultProcessor.ts From grafana-chinese with Apache License 2.0 | 6 votes |
getLogsResult(): LogsModel | null {
if (this.state.mode !== ExploreMode.Logs) {
return null;
}
const newResults = dataFrameToLogsModel(this.dataFrames, this.intervalMs, this.timeZone);
const sortOrder = refreshIntervalToSortOrder(this.state.refreshInterval);
const sortedNewResults = sortLogsResult(newResults, sortOrder);
const rows = sortedNewResults.rows;
const series = sortedNewResults.series;
return { ...sortedNewResults, rows, series };
}
Example #5
Source File: ResultProcessor.ts From grafana-chinese with Apache License 2.0 | 6 votes |
getGraphResult(): GraphSeriesXY[] | null {
if (this.state.mode !== ExploreMode.Metrics) {
return null;
}
const onlyTimeSeries = this.dataFrames.filter(isTimeSeries);
if (onlyTimeSeries.length === 0) {
return null;
}
return getGraphSeriesModel(
onlyTimeSeries,
this.timeZone,
{},
{ showBars: false, showLines: true, showPoints: false },
{ asTable: false, isVisible: true, placement: 'under' }
);
}
Example #6
Source File: reducers.ts From grafana-chinese with Apache License 2.0 | 6 votes |
getModesForDatasource = (dataSource: DataSourceApi, currentMode: ExploreMode): [ExploreMode[], ExploreMode] => {
const supportsGraph = dataSource.meta.metrics;
const supportsLogs = dataSource.meta.logs;
let mode = currentMode || ExploreMode.Metrics;
const supportedModes: ExploreMode[] = [];
if (supportsGraph) {
supportedModes.push(ExploreMode.Metrics);
}
if (supportsLogs) {
supportedModes.push(ExploreMode.Logs);
}
if (supportedModes.length === 1) {
mode = supportedModes[0];
}
// HACK: Used to set Loki's default explore mode to Logs mode.
// A better solution would be to introduce a "default" or "preferred" mode to the datasource config
if (dataSource.meta.name === 'Loki' && !currentMode) {
mode = ExploreMode.Logs;
}
return [supportedModes, mode];
}
Example #7
Source File: actions.ts From grafana-chinese with Apache License 2.0 | 6 votes |
/**
* Initialize Explore state with state from the URL and the React component.
* Call this only on components for with the Explore state has not been initialized.
*/
export function initializeExplore(
exploreId: ExploreId,
datasourceName: string,
queries: DataQuery[],
range: TimeRange,
mode: ExploreMode,
containerWidth: number,
eventBridge: Emitter,
ui: ExploreUIState,
originPanelId: number
): ThunkResult<void> {
return async (dispatch, getState) => {
dispatch(loadExploreDatasourcesAndSetDatasource(exploreId, datasourceName));
dispatch(
initializeExploreAction({
exploreId,
containerWidth,
eventBridge,
queries,
range,
mode,
ui,
originPanelId,
})
);
dispatch(updateTime({ exploreId }));
const richHistory = getRichHistory();
dispatch(richHistoryUpdatedAction({ richHistory }));
};
}
Example #8
Source File: reducers.test.ts From grafana-chinese with Apache License 2.0 | 6 votes |
setup = (urlStateOverrides?: any) => {
const update = makeInitialUpdateState();
const urlStateDefaults: ExploreUrlState = {
datasource: 'some-datasource',
queries: [],
range: {
from: '',
to: '',
},
mode: ExploreMode.Metrics,
ui: {
dedupStrategy: LogsDedupStrategy.none,
showingGraph: false,
showingTable: false,
showingLogs: false,
},
};
const urlState: ExploreUrlState = { ...urlStateDefaults, ...urlStateOverrides };
const serializedUrlState = serializeStateToUrlParam(urlState);
const initialState = ({
split: false,
left: { urlState, update },
right: { urlState, update },
} as unknown) as ExploreState;
return {
initialState,
serializedUrlState,
};
}
Example #9
Source File: explore.ts From grafana-chinese with Apache License 2.0 | 5 votes |
export function parseUrlState(initial: string | undefined): ExploreUrlState {
const parsed = safeParseJson(initial);
const errorResult: any = {
datasource: null,
queries: [],
range: DEFAULT_RANGE,
ui: DEFAULT_UI_STATE,
mode: null,
originPanelId: null,
};
if (!parsed) {
return errorResult;
}
if (!Array.isArray(parsed)) {
return parsed;
}
if (parsed.length <= ParseUrlStateIndex.SegmentsStart) {
console.error('Error parsing compact URL state for Explore.');
return errorResult;
}
const range = {
from: parsed[ParseUrlStateIndex.RangeFrom],
to: parsed[ParseUrlStateIndex.RangeTo],
};
const datasource = parsed[ParseUrlStateIndex.Datasource];
const parsedSegments = parsed.slice(ParseUrlStateIndex.SegmentsStart);
const metricProperties = ['expr', 'target', 'datasource', 'query'];
const queries = parsedSegments.filter(segment => isSegment(segment, ...metricProperties));
const modeObj = parsedSegments.filter(segment => isSegment(segment, 'mode'))[0];
const mode = modeObj ? modeObj.mode : ExploreMode.Metrics;
const uiState = parsedSegments.filter(segment => isSegment(segment, 'ui'))[0];
const ui = uiState
? {
showingGraph: uiState.ui[ParseUiStateIndex.Graph],
showingLogs: uiState.ui[ParseUiStateIndex.Logs],
showingTable: uiState.ui[ParseUiStateIndex.Table],
dedupStrategy: uiState.ui[ParseUiStateIndex.Strategy],
}
: DEFAULT_UI_STATE;
const originPanelId = parsedSegments.filter(segment => isSegment(segment, 'originPanelId'))[0];
return { datasource, queries, range, ui, mode, originPanelId };
}
Example #10
Source File: datasource.ts From grafana-chinese with Apache License 2.0 | 5 votes |
query(options: DataQueryRequest<LokiQuery>): Observable<DataQueryResponse> {
const subQueries: Array<Observable<DataQueryResponse>> = [];
const filteredTargets = options.targets
.filter(target => target.expr && !target.hide)
.map(target => ({
...target,
expr: this.templateSrv.replace(target.expr, options.scopedVars, this.interpolateQueryExpr),
}));
if (options.exploreMode === ExploreMode.Metrics) {
filteredTargets.forEach(target =>
subQueries.push(
this.runInstantQuery(target, options, filteredTargets.length),
this.runRangeQueryWithFallback(target, options, filteredTargets.length)
)
);
} else {
filteredTargets.forEach(target =>
subQueries.push(
this.runRangeQueryWithFallback(target, options, filteredTargets.length).pipe(
map(dataQueryResponse => {
if (options.exploreMode === ExploreMode.Logs && dataQueryResponse.data.find(d => isTimeSeries(d))) {
throw new Error(
'Logs mode does not support queries that return time series data. Please perform a logs query or switch to Metrics mode.'
);
} else {
return dataQueryResponse;
}
})
)
)
);
}
// No valid targets, return the empty result to save a round trip.
if (isEmpty(subQueries)) {
return of({
data: [],
state: LoadingState.Done,
});
}
return merge(...subQueries);
}
Example #11
Source File: LokiExploreQueryEditor.tsx From grafana-chinese with Apache License 2.0 | 5 votes |
export function LokiExploreQueryEditor(props: Props) {
const { query, data, datasource, exploreMode, history, onChange, onRunQuery } = props;
let absolute: AbsoluteTimeRange;
if (data && !_.isEmpty(data.request)) {
const { range } = data.request;
absolute = {
from: range.from.valueOf(),
to: range.to.valueOf(),
};
} else {
absolute = {
from: Date.now() - 10000,
to: Date.now(),
};
}
function onChangeQueryLimit(value: string) {
const { query, onChange } = props;
const nextQuery = { ...query, maxLines: preprocessMaxLines(value) };
onChange(nextQuery);
}
function preprocessMaxLines(value: string): number {
if (value.length === 0) {
// empty input - falls back to dataSource.maxLines limit
return NaN;
} else if (value.length > 0 && (isNaN(+value) || +value < 0)) {
// input with at least 1 character and that is either incorrect (value in the input field is not a number) or negative
// falls back to the limit of 0 lines
return 0;
} else {
// default case - correct input
return +value;
}
}
function onMaxLinesChange(e: React.SyntheticEvent<HTMLInputElement>) {
if (query.maxLines !== preprocessMaxLines(e.currentTarget.value)) {
onChangeQueryLimit(e.currentTarget.value);
}
}
function onReturnKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
if (e.key === 'Enter') {
onRunQuery();
}
}
return (
<LokiQueryField
datasource={datasource}
query={query}
onChange={onChange}
onRunQuery={onRunQuery}
history={history}
data={data}
absoluteRange={absolute}
ExtraFieldElement={
exploreMode === ExploreMode.Logs ? (
<LokiExploreExtraField
label={'Line limit'}
onChangeFunc={onMaxLinesChange}
onKeyDownFunc={onReturnKeyDown}
value={query?.maxLines?.toString() || ''}
type={'number'}
min={0}
/>
) : null
}
/>
);
}
Example #12
Source File: LokiExploreQueryEditor.test.tsx From grafana-chinese with Apache License 2.0 | 5 votes |
setup = (renderMethod: any, propOverrides?: object) => {
const datasource: LokiDatasource = makeMockLokiDatasource({});
datasource.languageProvider = new LokiLanguageProvider(datasource);
const onRunQuery = jest.fn();
const onChange = jest.fn();
const query: LokiQuery = { expr: '', refId: 'A', maxLines: 0 };
const data: PanelData = {
state: LoadingState.NotStarted,
series: [],
request: {
requestId: '1',
dashboardId: 1,
interval: '1s',
panelId: 1,
range: {
from: toUtc('2020-01-01', 'YYYY-MM-DD'),
to: toUtc('2020-01-02', 'YYYY-MM-DD'),
raw: {
from: toUtc('2020-01-01', 'YYYY-MM-DD'),
to: toUtc('2020-01-02', 'YYYY-MM-DD'),
},
},
scopedVars: {},
targets: [],
timezone: 'GMT',
app: 'Grafana',
startTime: 0,
},
timeRange: {
from: toUtc('2020-01-01', 'YYYY-MM-DD'),
to: toUtc('2020-01-02', 'YYYY-MM-DD'),
raw: {
from: toUtc('2020-01-01', 'YYYY-MM-DD'),
to: toUtc('2020-01-02', 'YYYY-MM-DD'),
},
},
};
const history: any[] = [];
const exploreMode: ExploreMode = ExploreMode.Logs;
const props: any = {
query,
data,
datasource,
exploreMode,
history,
onChange,
onRunQuery,
};
Object.assign(props, { ...props, ...propOverrides });
return renderMethod(<LokiExploreQueryEditor {...props} />);
}
Example #13
Source File: LokiCheatSheet.tsx From grafana-chinese with Apache License 2.0 | 5 votes |
render() {
const { exploreMode } = this.props;
return exploreMode === ExploreMode.Logs ? this.renderLogsCheatSheet() : this.renderMetricsCheatSheet();
}
Example #14
Source File: ResultProcessor.ts From grafana-chinese with Apache License 2.0 | 5 votes |
getTableResult(): DataFrame | null {
if (this.state.mode !== ExploreMode.Metrics) {
return null;
}
// For now ignore time series
// We can change this later, just need to figure out how to
// Ignore time series only for prometheus
const onlyTables = this.dataFrames.filter(frame => !isTimeSeries(frame));
if (onlyTables.length === 0) {
return null;
}
const tables = onlyTables.map(frame => {
const { fields } = frame;
const fieldCount = fields.length;
const rowCount = frame.length;
const columns = fields.map(field => ({
text: field.name,
type: field.type,
filterable: field.config.filterable,
}));
const rows: any[][] = [];
for (let i = 0; i < rowCount; i++) {
const row: any[] = [];
for (let j = 0; j < fieldCount; j++) {
row.push(frame.fields[j].values.get(i));
}
rows.push(row);
}
return new TableModel({
columns,
rows,
meta: frame.meta,
});
});
const mergedTable = mergeTablesIntoModel(new TableModel(), ...tables);
const data = toDataFrame(mergedTable);
// set display processor
for (const field of data.fields) {
field.display = getDisplayProcessor({
field,
theme: config.theme,
});
}
return data;
}
Example #15
Source File: ResultProcessor.test.ts From grafana-chinese with Apache License 2.0 | 5 votes |
testContext = (options: any = {}) => {
const timeSeries = toDataFrame({
name: 'A-series',
refId: 'A',
fields: [
{ name: 'A-series', type: FieldType.number, values: [4, 5, 6] },
{ name: 'time', type: FieldType.time, values: [100, 200, 300] },
],
});
const table = toDataFrame({
name: 'table-res',
refId: 'A',
fields: [
{ name: 'value', type: FieldType.number, values: [4, 5, 6] },
{ name: 'time', type: FieldType.time, values: [100, 200, 300] },
{ name: 'message', type: FieldType.string, values: ['this is a message', 'second message', 'third'] },
],
});
const emptyTable = toDataFrame({ name: 'empty-table', refId: 'A', fields: [] });
const defaultOptions = {
mode: ExploreMode.Metrics,
dataFrames: [timeSeries, table, emptyTable],
graphResult: [] as TimeSeries[],
tableResult: new TableModel(),
logsResult: { hasUniqueLabels: false, rows: [] as LogRowModel[] },
};
const combinedOptions = { ...defaultOptions, ...options };
const state = ({
mode: combinedOptions.mode,
graphResult: combinedOptions.graphResult,
tableResult: combinedOptions.tableResult,
logsResult: combinedOptions.logsResult,
queryIntervals: { intervalMs: 10 },
} as any) as ExploreItemState;
const resultProcessor = new ResultProcessor(state, combinedOptions.dataFrames, 60000, 'utc');
return {
dataFrames: combinedOptions.dataFrames,
resultProcessor,
};
}
Example #16
Source File: reducers.ts From grafana-chinese with Apache License 2.0 | 5 votes |
updateChildRefreshState = (
state: Readonly<ExploreItemState>,
payload: LocationUpdate,
exploreId: ExploreId
): ExploreItemState => {
const path = payload.path || '';
const queryState = payload.query[exploreId] as string;
if (!queryState) {
return state;
}
const urlState = parseUrlState(queryState);
if (!state.urlState || path !== '/explore') {
// we only want to refresh when browser back/forward
return {
...state,
urlState,
update: { datasource: false, queries: false, range: false, mode: false, ui: false },
};
}
const datasource = _.isEqual(urlState ? urlState.datasource : '', state.urlState.datasource) === false;
const queries = _.isEqual(urlState ? urlState.queries : [], state.urlState.queries) === false;
const range = _.isEqual(urlState ? urlState.range : DEFAULT_RANGE, state.urlState.range) === false;
const mode = _.isEqual(urlState ? urlState.mode : ExploreMode.Metrics, state.urlState.mode) === false;
const ui = _.isEqual(urlState ? urlState.ui : DEFAULT_UI_STATE, state.urlState.ui) === false;
return {
...state,
urlState,
update: {
...state.update,
datasource,
queries,
range,
mode,
ui,
},
};
}
Example #17
Source File: actions.ts From grafana-chinese with Apache License 2.0 | 5 votes |
/**
* Change the display mode in Explore.
*/
export function changeMode(exploreId: ExploreId, mode: ExploreMode): ThunkResult<void> {
return dispatch => {
dispatch(changeModeAction({ exploreId, mode }));
};
}
Example #18
Source File: actions.ts From grafana-chinese with Apache License 2.0 | 5 votes |
/**
* Loads a new datasource identified by the given name.
*/
export function changeDatasource(exploreId: ExploreId, datasource: string): ThunkResult<void> {
return async (dispatch, getState) => {
let newDataSourceInstance: DataSourceApi = null;
if (!datasource) {
newDataSourceInstance = await getDatasourceSrv().get();
} else {
newDataSourceInstance = await getDatasourceSrv().get(datasource);
}
const currentDataSourceInstance = getState().explore[exploreId].datasourceInstance;
const queries = getState().explore[exploreId].queries;
const orgId = getState().user.orgId;
const datasourceVersion = newDataSourceInstance.getVersion && (await newDataSourceInstance.getVersion());
// HACK: Switch to logs mode if coming from Prometheus to Loki
const prometheusToLoki =
currentDataSourceInstance?.meta?.name === 'Prometheus' && newDataSourceInstance?.meta?.name === 'Loki';
dispatch(
updateDatasourceInstanceAction({
exploreId,
datasourceInstance: newDataSourceInstance,
version: datasourceVersion,
mode: prometheusToLoki ? ExploreMode.Logs : undefined,
})
);
await dispatch(importQueries(exploreId, queries, currentDataSourceInstance, newDataSourceInstance));
if (getState().explore[exploreId].isLive) {
dispatch(changeRefreshInterval(exploreId, RefreshPicker.offOption.value));
}
await dispatch(loadDatasource(exploreId, newDataSourceInstance, orgId));
dispatch(runQueries(exploreId));
};
}
Example #19
Source File: actions.test.ts From grafana-chinese with Apache License 2.0 | 5 votes |
describe('changing datasource', () => {
it('should switch to logs mode when changing from prometheus to loki', async () => {
const lokiMock = {
testDatasource: () => Promise.resolve({ status: 'success' }),
name: 'Loki',
init: jest.fn(),
meta: { id: 'some id', name: 'Loki' },
};
getDatasourceSrvMock.mockImplementation(
() =>
({
getExternal: jest.fn().mockReturnValue([]),
get: jest.fn().mockReturnValue(lokiMock),
} as any)
);
const exploreId = ExploreId.left;
const name = 'Prometheus';
const mockPromDatasourceInstance = {
testDatasource: () => Promise.resolve({ status: 'success' }),
name,
init: jest.fn(),
meta: { id: 'some id', name },
};
const initialState = {
explore: {
[exploreId]: {
requestedDatasourceName: 'Loki',
datasourceInstance: mockPromDatasourceInstance,
},
},
user: {
orgId: 1,
},
};
jest.spyOn(Actions, 'importQueries').mockImplementationOnce(() => jest.fn);
jest.spyOn(Actions, 'loadDatasource').mockImplementationOnce(() => jest.fn);
jest.spyOn(Actions, 'runQueries').mockImplementationOnce(() => jest.fn);
const dispatchedActions = await thunkTester(initialState)
.givenThunk(changeDatasource)
.whenThunkIsDispatched(exploreId, name);
expect(dispatchedActions).toEqual([
updateDatasourceInstanceAction({
exploreId,
datasourceInstance: lokiMock as any,
version: undefined,
mode: ExploreMode.Logs,
}),
]);
});
});
Example #20
Source File: actions.test.ts From grafana-chinese with Apache License 2.0 | 5 votes |
setup = (updateOverides?: Partial<ExploreUpdateState>) => {
const exploreId = ExploreId.left;
const containerWidth = 1920;
const eventBridge = {} as Emitter;
const ui = { dedupStrategy: LogsDedupStrategy.none, showingGraph: false, showingLogs: false, showingTable: false };
const timeZone = DefaultTimeZone;
const range = testRange;
const urlState: ExploreUrlState = {
datasource: 'some-datasource',
queries: [],
range: range.raw,
mode: ExploreMode.Metrics,
ui,
};
const updateDefaults = makeInitialUpdateState();
const update = { ...updateDefaults, ...updateOverides };
const initialState = {
user: {
orgId: '1',
timeZone,
},
explore: {
[exploreId]: {
initialized: true,
urlState,
containerWidth,
eventBridge,
update,
datasourceInstance: { name: 'some-datasource' },
queries: [] as DataQuery[],
range,
ui,
refreshInterval: {
label: 'Off',
value: 0,
},
},
},
};
return {
initialState,
exploreId,
range,
ui,
containerWidth,
eventBridge,
};
}
Example #21
Source File: QueryRow.tsx From grafana-chinese with Apache License 2.0 | 5 votes |
render() {
const {
datasourceInstance,
history,
query,
exploreEvents,
range,
absoluteRange,
queryResponse,
mode,
latency,
} = this.props;
const canToggleEditorModes =
mode === ExploreMode.Metrics && has(datasourceInstance, 'components.QueryCtrl.prototype.toggleEditorMode');
const isNotStarted = queryResponse.state === LoadingState.NotStarted;
const queryErrors = queryResponse.error && queryResponse.error.refId === query.refId ? [queryResponse.error] : [];
let QueryField;
if (mode === ExploreMode.Metrics && datasourceInstance.components?.ExploreMetricsQueryField) {
QueryField = datasourceInstance.components.ExploreMetricsQueryField;
} else if (mode === ExploreMode.Logs && datasourceInstance.components?.ExploreLogsQueryField) {
QueryField = datasourceInstance.components.ExploreLogsQueryField;
} else {
QueryField = datasourceInstance.components?.ExploreQueryField;
}
return (
<>
<div className="query-row">
<div className="query-row-field flex-shrink-1">
{QueryField ? (
<QueryField
datasource={datasourceInstance}
query={query}
history={history}
onRunQuery={this.onRunQuery}
onBlur={noopOnBlur}
onChange={this.onChange}
data={queryResponse}
absoluteRange={absoluteRange}
exploreMode={mode}
/>
) : (
<QueryEditor
error={queryErrors}
datasource={datasourceInstance}
onQueryChange={this.onChange}
onExecuteQuery={this.onRunQuery}
initialQuery={query}
exploreEvents={exploreEvents}
range={range}
textEditModeEnabled={this.state.textEditModeEnabled}
/>
)}
</div>
<QueryRowActions
canToggleEditorModes={canToggleEditorModes}
isDisabled={query.hide}
isNotStarted={isNotStarted}
latency={latency}
onClickToggleEditorMode={this.onClickToggleEditorMode}
onClickToggleDisabled={this.onClickToggleDisabled}
onClickRemoveButton={this.onClickRemoveButton}
/>
</div>
{queryErrors.length > 0 && <ErrorContainer queryError={queryErrors[0]} />}
</>
);
}
Example #22
Source File: ExploreToolbar.tsx From grafana-chinese with Apache License 2.0 | 5 votes |
mapStateToProps = (state: StoreState, { exploreId }: OwnProps): StateProps => {
const splitted = state.explore.split;
const syncedTimes = state.explore.syncedTimes;
const exploreItem: ExploreItemState = state.explore[exploreId];
const {
datasourceInstance,
datasourceMissing,
range,
refreshInterval,
loading,
supportedModes,
mode,
isLive,
isPaused,
originPanelId,
queries,
datasourceLoading,
containerWidth,
} = exploreItem;
const hasLiveOption = datasourceInstance?.meta?.streaming && mode === ExploreMode.Logs;
return {
datasourceMissing,
datasourceName: datasourceInstance?.name,
loading,
range,
timeZone: getTimeZone(state.user),
splitted,
refreshInterval,
supportedModes,
selectedMode: supportedModes.includes(mode) ? mode : supportedModes[0],
hasLiveOption,
isLive,
isPaused,
originPanelId,
queries,
syncedTimes,
datasourceLoading,
containerWidth,
};
}
Example #23
Source File: ExploreToolbar.tsx From grafana-chinese with Apache License 2.0 | 5 votes |
onModeChange = (mode: ExploreMode) => {
const { changeMode, exploreId } = this.props;
changeMode(exploreId, mode);
};
Example #24
Source File: Explore.tsx From grafana-chinese with Apache License 2.0 | 5 votes |
function mapStateToProps(state: StoreState, { exploreId }: ExploreProps): Partial<ExploreProps> {
const explore = state.explore;
const { split, syncedTimes } = explore;
const item: ExploreItemState = explore[exploreId];
const timeZone = getTimeZone(state.user);
const {
datasourceInstance,
datasourceMissing,
initialized,
queryKeys,
urlState,
update,
isLive,
supportedModes,
mode,
graphResult,
loading,
showingGraph,
showingTable,
absoluteRange,
queryResponse,
} = item;
const { datasource, queries, range: urlRange, mode: urlMode, ui, originPanelId } = (urlState ||
{}) as ExploreUrlState;
const initialDatasource = datasource || store.get(lastUsedDatasourceKeyForOrgId(state.user.orgId));
const initialQueries: DataQuery[] = ensureQueriesMemoized(queries);
const initialRange = urlRange
? getTimeRangeFromUrlMemoized(urlRange, timeZone)
: getTimeRange(timeZone, DEFAULT_RANGE);
let newMode: ExploreMode | undefined;
if (supportedModes.length) {
const urlModeIsValid = supportedModes.includes(urlMode);
const modeStateIsValid = supportedModes.includes(mode);
if (modeStateIsValid) {
newMode = mode;
} else if (urlModeIsValid) {
newMode = urlMode;
} else {
newMode = supportedModes[0];
}
} else {
newMode = [ExploreMode.Metrics, ExploreMode.Logs].includes(urlMode) ? urlMode : undefined;
}
const initialUI = ui || DEFAULT_UI_STATE;
return {
datasourceInstance,
datasourceMissing,
initialized,
split,
queryKeys,
update,
initialDatasource,
initialQueries,
initialRange,
mode: newMode,
initialUI,
isLive,
graphResult,
loading,
showingGraph,
showingTable,
absoluteRange,
queryResponse,
originPanelId,
syncedTimes,
timeZone,
};
}
Example #25
Source File: actions.ts From grafana-chinese with Apache License 2.0 | 4 votes |
runQueries = (exploreId: ExploreId): ThunkResult<void> => {
return (dispatch, getState) => {
dispatch(updateTime({ exploreId }));
const richHistory = getState().explore.richHistory;
const exploreItemState = getState().explore[exploreId];
const {
datasourceInstance,
queries,
containerWidth,
isLive: live,
range,
scanning,
queryResponse,
querySubscription,
history,
mode,
showingGraph,
showingTable,
} = exploreItemState;
if (!hasNonEmptyQuery(queries)) {
dispatch(clearQueriesAction({ exploreId }));
dispatch(stateSave()); // Remember to save to state and update location
return;
}
// Some datasource's query builders allow per-query interval limits,
// but we're using the datasource interval limit for now
const minInterval = datasourceInstance.interval;
stopQueryState(querySubscription);
const datasourceId = datasourceInstance.meta.id;
const queryOptions: QueryOptions = {
minInterval,
// maxDataPoints is used in:
// Loki - used for logs streaming for buffer size, with undefined it falls back to datasource config if it supports that.
// Elastic - limits the number of datapoints for the counts query and for logs it has hardcoded limit.
// Influx - used to correctly display logs in graph
maxDataPoints: mode === ExploreMode.Logs && datasourceId === 'loki' ? undefined : containerWidth,
liveStreaming: live,
showingGraph,
showingTable,
mode,
};
const datasourceName = exploreItemState.requestedDatasourceName;
const transaction = buildQueryTransaction(queries, queryOptions, range, scanning);
let firstResponse = true;
const newQuerySub = runRequest(datasourceInstance, transaction.request)
.pipe(
// Simple throttle for live tailing, in case of > 1000 rows per interval we spend about 200ms on processing and
// rendering. In case this is optimized this can be tweaked, but also it should be only as fast as user
// actually can see what is happening.
live ? throttleTime(500) : identity,
map((data: PanelData) => preProcessPanelData(data, queryResponse))
)
.subscribe((data: PanelData) => {
if (!data.error && firstResponse) {
// Side-effect: Saving history in localstorage
const nextHistory = updateHistory(history, datasourceId, queries);
const arrayOfStringifiedQueries = queries.map(query =>
datasourceInstance?.getQueryDisplayText
? datasourceInstance.getQueryDisplayText(query)
: getQueryDisplayText(query)
);
const nextRichHistory = addToRichHistory(
richHistory || [],
datasourceId,
datasourceName,
arrayOfStringifiedQueries,
false,
'',
''
);
dispatch(historyUpdatedAction({ exploreId, history: nextHistory }));
dispatch(richHistoryUpdatedAction({ richHistory: nextRichHistory }));
// We save queries to the URL here so that only successfully run queries change the URL.
dispatch(stateSave());
}
firstResponse = false;
dispatch(queryStreamUpdatedAction({ exploreId, response: data }));
// Keep scanning for results if this was the last scanning transaction
if (getState().explore[exploreId].scanning) {
if (data.state === LoadingState.Done && data.series.length === 0) {
const range = getShiftedTimeRange(-1, getState().explore[exploreId].range);
dispatch(updateTime({ exploreId, absoluteRange: range }));
dispatch(runQueries(exploreId));
} else {
// We can stop scanning if we have a result
dispatch(scanStopAction({ exploreId }));
}
}
});
dispatch(queryStoreSubscriptionAction({ exploreId, querySubscription: newQuerySub }));
};
}
Example #26
Source File: reducers.test.ts From grafana-chinese with Apache License 2.0 | 4 votes |
describe('Explore item reducer', () => {
describe('scanning', () => {
it('should start scanning', () => {
const initialState = {
...makeExploreItemState(),
scanning: false,
};
reducerTester<ExploreItemState>()
.givenReducer(itemReducer, initialState)
.whenActionIsDispatched(scanStartAction({ exploreId: ExploreId.left }))
.thenStateShouldEqual({
...makeExploreItemState(),
scanning: true,
});
});
it('should stop scanning', () => {
const initialState = {
...makeExploreItemState(),
scanning: true,
scanRange: {} as RawTimeRange,
};
reducerTester<ExploreItemState>()
.givenReducer(itemReducer, initialState)
.whenActionIsDispatched(scanStopAction({ exploreId: ExploreId.left }))
.thenStateShouldEqual({
...makeExploreItemState(),
scanning: false,
scanRange: undefined,
});
});
});
describe('changing datasource', () => {
describe('when changeMode is dispatched', () => {
it('then it should set correct state', () => {
reducerTester<ExploreItemState>()
.givenReducer(itemReducer, ({} as unknown) as ExploreItemState)
.whenActionIsDispatched(changeModeAction({ exploreId: ExploreId.left, mode: ExploreMode.Logs }))
.thenStatePredicateShouldEqual((resultingState: ExploreItemState) => {
expect(resultingState.mode).toEqual(ExploreMode.Logs);
expect(resultingState.logsResult).toBeNull();
expect(resultingState.graphResult).toBeNull();
expect(resultingState.tableResult).toBeNull();
return true;
});
});
});
describe('when updateDatasourceInstanceAction is dispatched', () => {
describe('and datasourceInstance supports graph, logs, table and has a startpage', () => {
it('then it should set correct state', () => {
const StartPage = {};
const datasourceInstance = {
meta: {
metrics: true,
logs: true,
},
components: {
ExploreStartPage: StartPage,
},
} as DataSourceApi;
const queries: DataQuery[] = [];
const queryKeys: string[] = [];
const initialState: ExploreItemState = ({
datasourceInstance: null,
queries,
queryKeys,
} as unknown) as ExploreItemState;
const expectedState: any = {
datasourceInstance,
queries,
queryKeys,
graphResult: null,
logsResult: null,
tableResult: null,
supportedModes: [ExploreMode.Metrics, ExploreMode.Logs],
mode: ExploreMode.Metrics,
latency: 0,
loading: false,
queryResponse: createEmptyQueryResponse(),
};
reducerTester<ExploreItemState>()
.givenReducer(itemReducer, initialState)
.whenActionIsDispatched(updateDatasourceInstanceAction({ exploreId: ExploreId.left, datasourceInstance }))
.thenStateShouldEqual(expectedState);
});
});
});
});
describe('changing refresh intervals', () => {
it("should result in 'streaming' state, when live-tailing is active", () => {
const initialState = makeExploreItemState();
const expectedState = {
...makeExploreItemState(),
refreshInterval: 'LIVE',
isLive: true,
loading: true,
logsResult: {
hasUniqueLabels: false,
rows: [] as any[],
},
queryResponse: {
...makeExploreItemState().queryResponse,
state: LoadingState.Streaming,
},
};
reducerTester<ExploreItemState>()
.givenReducer(itemReducer, initialState)
.whenActionIsDispatched(changeRefreshIntervalAction({ exploreId: ExploreId.left, refreshInterval: 'LIVE' }))
.thenStateShouldEqual(expectedState);
});
it("should result in 'done' state, when live-tailing is stopped", () => {
const initialState = makeExploreItemState();
const expectedState = {
...makeExploreItemState(),
refreshInterval: '',
logsResult: {
hasUniqueLabels: false,
rows: [] as any[],
},
queryResponse: {
...makeExploreItemState().queryResponse,
state: LoadingState.Done,
},
};
reducerTester<ExploreItemState>()
.givenReducer(itemReducer, initialState)
.whenActionIsDispatched(changeRefreshIntervalAction({ exploreId: ExploreId.left, refreshInterval: '' }))
.thenStateShouldEqual(expectedState);
});
});
describe('toggling panels', () => {
describe('when toggleGraphAction is dispatched', () => {
it('then it should set correct state', () => {
reducerTester<ExploreItemState>()
.givenReducer(itemReducer, ({ graphResult: [] } as unknown) as ExploreItemState)
.whenActionIsDispatched(toggleGraphAction({ exploreId: ExploreId.left }))
.thenStateShouldEqual(({ showingGraph: true, graphResult: [] } as unknown) as ExploreItemState)
.whenActionIsDispatched(toggleGraphAction({ exploreId: ExploreId.left }))
.thenStateShouldEqual(({ showingGraph: false, graphResult: null } as unknown) as ExploreItemState);
});
});
describe('when toggleTableAction is dispatched', () => {
it('then it should set correct state', () => {
const table = toDataFrame({
name: 'logs',
fields: [
{
name: 'time',
type: 'number',
values: [1, 2],
},
],
});
reducerTester<ExploreItemState>()
.givenReducer(itemReducer, ({ tableResult: table } as unknown) as ExploreItemState)
.whenActionIsDispatched(toggleTableAction({ exploreId: ExploreId.left }))
.thenStateShouldEqual(({ showingTable: true, tableResult: table } as unknown) as ExploreItemState)
.whenActionIsDispatched(toggleTableAction({ exploreId: ExploreId.left }))
.thenStateShouldEqual(({ showingTable: false, tableResult: null } as unknown) as ExploreItemState);
});
});
});
describe('changing range', () => {
describe('when changeRangeAction is dispatched', () => {
it('then it should set correct state', () => {
reducerTester<ExploreItemState>()
.givenReducer(itemReducer, ({
update: { ...makeInitialUpdateState(), range: true },
range: null,
absoluteRange: null,
} as unknown) as ExploreItemState)
.whenActionIsDispatched(
changeRangeAction({
exploreId: ExploreId.left,
absoluteRange: { from: 1546297200000, to: 1546383600000 },
range: { from: dateTime('2019-01-01'), to: dateTime('2019-01-02'), raw: { from: 'now-1d', to: 'now' } },
})
)
.thenStateShouldEqual(({
update: { ...makeInitialUpdateState(), range: false },
absoluteRange: { from: 1546297200000, to: 1546383600000 },
range: { from: dateTime('2019-01-01'), to: dateTime('2019-01-02'), raw: { from: 'now-1d', to: 'now' } },
} as unknown) as ExploreItemState);
});
});
});
});
Example #27
Source File: ResultProcessor.test.ts From grafana-chinese with Apache License 2.0 | 4 votes |
describe('ResultProcessor', () => {
describe('constructed without result', () => {
describe('when calling getGraphResult', () => {
it('then it should return null', () => {
const { resultProcessor } = testContext({ dataFrames: [] });
const theResult = resultProcessor.getGraphResult();
expect(theResult).toEqual(null);
});
});
describe('when calling getTableResult', () => {
it('then it should return null', () => {
const { resultProcessor } = testContext({ dataFrames: [] });
const theResult = resultProcessor.getTableResult();
expect(theResult).toEqual(null);
});
});
describe('when calling getLogsResult', () => {
it('then it should return null', () => {
const { resultProcessor } = testContext({ dataFrames: [] });
const theResult = resultProcessor.getLogsResult();
expect(theResult).toBeNull();
});
});
});
describe('constructed with a result that is a DataQueryResponse', () => {
describe('when calling getGraphResult', () => {
it('then it should return correct graph result', () => {
const { resultProcessor, dataFrames } = testContext();
const timeField = dataFrames[0].fields[1];
const valueField = dataFrames[0].fields[0];
const theResult = resultProcessor.getGraphResult();
expect(theResult).toEqual([
{
label: 'A-series',
color: '#7EB26D',
data: [
[100, 4],
[200, 5],
[300, 6],
],
info: undefined,
isVisible: true,
yAxis: {
index: 1,
},
seriesIndex: 0,
timeField,
valueField,
timeStep: 100,
},
]);
});
});
describe('when calling getTableResult', () => {
it('then it should return correct table result', () => {
const { resultProcessor } = testContext();
let theResult = resultProcessor.getTableResult();
expect(theResult.fields[0].name).toEqual('value');
expect(theResult.fields[1].name).toEqual('time');
expect(theResult.fields[2].name).toEqual('message');
expect(theResult.fields[1].display).not.toBeNull();
expect(theResult.length).toBe(3);
// Same data though a DataFrame
theResult = toDataFrame(
new TableModel({
columns: [
{ text: 'value', type: 'number' },
{ text: 'time', type: 'time' },
{ text: 'message', type: 'string' },
],
rows: [
[4, 100, 'this is a message'],
[5, 200, 'second message'],
[6, 300, 'third'],
],
type: 'table',
})
);
expect(theResult.fields[0].name).toEqual('value');
expect(theResult.fields[1].name).toEqual('time');
expect(theResult.fields[2].name).toEqual('message');
expect(theResult.fields[1].display).not.toBeNull();
expect(theResult.length).toBe(3);
});
});
describe('when calling getLogsResult', () => {
it('then it should return correct logs result', () => {
const { resultProcessor, dataFrames } = testContext({ mode: ExploreMode.Logs });
const timeField = dataFrames[0].fields[1];
const valueField = dataFrames[0].fields[0];
const logsDataFrame = dataFrames[1];
const theResult = resultProcessor.getLogsResult();
expect(theResult).toEqual({
hasUniqueLabels: false,
meta: [],
rows: [
{
rowIndex: 2,
dataFrame: logsDataFrame,
entry: 'third',
entryFieldIndex: 2,
hasAnsi: false,
labels: undefined,
logLevel: 'unknown',
raw: 'third',
searchWords: [] as string[],
timeEpochMs: 300,
timeFromNow: 'fromNow() jest mocked',
timeLocal: 'format() jest mocked',
timeUtc: 'format() jest mocked',
uid: '2',
uniqueLabels: {},
},
{
rowIndex: 1,
dataFrame: logsDataFrame,
entry: 'second message',
entryFieldIndex: 2,
hasAnsi: false,
labels: undefined,
logLevel: 'unknown',
raw: 'second message',
searchWords: [] as string[],
timeEpochMs: 200,
timeFromNow: 'fromNow() jest mocked',
timeLocal: 'format() jest mocked',
timeUtc: 'format() jest mocked',
uid: '1',
uniqueLabels: {},
},
{
rowIndex: 0,
dataFrame: logsDataFrame,
entry: 'this is a message',
entryFieldIndex: 2,
hasAnsi: false,
labels: undefined,
logLevel: 'unknown',
raw: 'this is a message',
searchWords: [] as string[],
timeEpochMs: 100,
timeFromNow: 'fromNow() jest mocked',
timeLocal: 'format() jest mocked',
timeUtc: 'format() jest mocked',
uid: '0',
uniqueLabels: {},
},
],
series: [
{
label: 'A-series',
color: '#7EB26D',
data: [
[100, 4],
[200, 5],
[300, 6],
],
info: undefined,
isVisible: true,
yAxis: {
index: 1,
},
seriesIndex: 0,
timeField,
valueField,
timeStep: 100,
},
],
});
});
});
});
});
Example #28
Source File: ExploreToolbar.tsx From grafana-chinese with Apache License 2.0 | 4 votes |
render() {
const {
datasourceMissing,
closeSplit,
exploreId,
loading,
range,
timeZone,
splitted,
syncedTimes,
refreshInterval,
onChangeTime,
split,
supportedModes,
selectedMode,
hasLiveOption,
isLive,
isPaused,
originPanelId,
datasourceLoading,
containerWidth,
} = this.props;
const styles = getStyles();
const originDashboardIsEditable = originPanelId && Number.isInteger(originPanelId);
const panelReturnClasses = classNames('btn', 'navbar-button', {
'btn--radius-right-0': originDashboardIsEditable,
'navbar-button navbar-button--border-right-0': originDashboardIsEditable,
});
const showSmallDataSourcePicker = (splitted ? containerWidth < 700 : containerWidth < 800) || false;
const showSmallTimePicker = splitted || containerWidth < 1210;
return (
<div className={splitted ? 'explore-toolbar splitted' : 'explore-toolbar'}>
<div className="explore-toolbar-item">
<div className="explore-toolbar-header">
<div className="explore-toolbar-header-title">
{exploreId === 'left' && (
<span className="navbar-page-btn">
<i className="gicon gicon-explore" />
Explore
</span>
)}
</div>
{splitted && (
<a className="explore-toolbar-header-close" onClick={() => closeSplit(exploreId)}>
<i className="fa fa-times fa-fw" />
</a>
)}
</div>
</div>
<div className="explore-toolbar-item">
<div className="explore-toolbar-content">
{!datasourceMissing ? (
<div className="explore-toolbar-content-item">
<div
className={classNames(
'explore-ds-picker',
showSmallDataSourcePicker ? 'explore-ds-picker--small' : ''
)}
>
<DataSourcePicker
onChange={this.onChangeDatasource}
datasources={getExploreDatasources()}
current={this.getSelectedDatasource()}
showLoading={datasourceLoading}
hideTextValue={showSmallDataSourcePicker}
/>
</div>
{supportedModes.length > 1 ? (
<div className="query-type-toggle">
<ToggleButtonGroup label="" transparent={true}>
<ToggleButton
key={ExploreMode.Metrics}
value={ExploreMode.Metrics}
onChange={this.onModeChange}
selected={selectedMode === ExploreMode.Metrics}
>
{'Metrics'}
</ToggleButton>
<ToggleButton
key={ExploreMode.Logs}
value={ExploreMode.Logs}
onChange={this.onModeChange}
selected={selectedMode === ExploreMode.Logs}
>
{'Logs'}
</ToggleButton>
</ToggleButtonGroup>
</div>
) : null}
</div>
) : null}
{originPanelId && Number.isInteger(originPanelId) && !splitted && (
<div className="explore-toolbar-content-item">
<Tooltip content={'Return to panel'} placement="bottom">
<button className={panelReturnClasses} onClick={() => this.returnToPanel()}>
<i className="fa fa-arrow-left" />
</button>
</Tooltip>
{originDashboardIsEditable && (
<ButtonSelect
className="navbar-button--attached btn--radius-left-0$"
options={[{ label: 'Return to panel with changes', value: '' }]}
onChange={() => this.returnToPanel({ withChanges: true })}
maxMenuHeight={380}
/>
)}
</div>
)}
{exploreId === 'left' && !splitted ? (
<div className="explore-toolbar-content-item explore-icon-align">
<ResponsiveButton
splitted={splitted}
title="Split"
onClick={split}
iconClassName="fa fa-fw fa-columns icon-margin-right"
disabled={isLive}
/>
</div>
) : null}
{!isLive && (
<div className="explore-toolbar-content-item">
<ExploreTimeControls
exploreId={exploreId}
range={range}
timeZone={timeZone}
onChangeTime={onChangeTime}
splitted={splitted}
syncedTimes={syncedTimes}
onChangeTimeSync={this.onChangeTimeSync}
hideText={showSmallTimePicker}
/>
</div>
)}
{!isLive && (
<div className="explore-toolbar-content-item explore-icon-align">
<ResponsiveButton
splitted={splitted}
title="Clear All"
onClick={this.onClearAll}
iconClassName="fa fa-fw fa-trash icon-margin-right"
/>
</div>
)}
<div className="explore-toolbar-content-item">
<RunButton
refreshInterval={refreshInterval}
onChangeRefreshInterval={this.onChangeRefreshInterval}
splitted={splitted}
loading={loading || (isLive && !isPaused)}
onRun={this.onRunQuery}
showDropdown={!isLive}
/>
{refreshInterval && <SetInterval func={this.onRunQuery} interval={refreshInterval} loading={loading} />}
</div>
{hasLiveOption && (
<div className={`explore-toolbar-content-item ${styles.liveTailButtons}`}>
<LiveTailControls exploreId={exploreId}>
{controls => (
<LiveTailButton
splitted={splitted}
isLive={isLive}
isPaused={isPaused}
start={controls.start}
pause={controls.pause}
resume={controls.resume}
stop={controls.stop}
/>
)}
</LiveTailControls>
</div>
)}
</div>
</div>
</div>
);
}
Example #29
Source File: Explore.tsx From grafana-chinese with Apache License 2.0 | 4 votes |
render() {
const {
datasourceInstance,
datasourceMissing,
exploreId,
split,
queryKeys,
mode,
graphResult,
loading,
absoluteRange,
showingGraph,
showingTable,
timeZone,
queryResponse,
syncedTimes,
isLive,
} = this.props;
const { showRichHistory } = this.state;
const exploreClass = split ? 'explore explore-split' : 'explore';
const styles = getStyles();
const StartPage = datasourceInstance?.components?.ExploreStartPage;
const showStartPage = !queryResponse || queryResponse.state === LoadingState.NotStarted;
// gets an error without a refID, so non-query-row-related error, like a connection error
const queryErrors = queryResponse.error ? [queryResponse.error] : undefined;
const queryError = getFirstNonQueryRowSpecificError(queryErrors);
return (
<div className={exploreClass} ref={this.getRef}>
<ExploreToolbar exploreId={exploreId} onChangeTime={this.onChangeTime} />
{datasourceMissing ? this.renderEmptyState() : null}
{datasourceInstance && (
<div className="explore-container">
<QueryRows exploreEvents={this.exploreEvents} exploreId={exploreId} queryKeys={queryKeys} />
<div className="gf-form">
<button
aria-label="Add row button"
className={`gf-form-label gf-form-label--btn ${styles.button}`}
onClick={this.onClickAddQueryRowButton}
disabled={isLive}
>
<i className={'fa fa-fw fa-plus icon-margin-right'} />
<span className="btn-title">{'\xA0' + 'Add query'}</span>
</button>
<button
aria-label="Rich history button"
className={cx(`gf-form-label gf-form-label--btn ${styles.button}`, {
['explore-active-button']: showRichHistory,
})}
onClick={this.toggleShowRichHistory}
>
<i className={'fa fa-fw fa-history icon-margin-right '} />
<span className="btn-title">{'\xA0' + 'Query history'}</span>
</button>
</div>
<ErrorContainer queryError={queryError} />
<AutoSizer onResize={this.onResize} disableHeight>
{({ width }) => {
if (width === 0) {
return null;
}
return (
<main className={`m-t-2 ${styles.logsMain}`} style={{ width }}>
<ErrorBoundaryAlert>
{showStartPage && StartPage && (
<div className={'grafana-info-box grafana-info-box--max-lg'}>
<StartPage
onClickExample={this.onClickExample}
datasource={datasourceInstance}
exploreMode={mode}
/>
</div>
)}
{!showStartPage && (
<>
{mode === ExploreMode.Metrics && (
<ExploreGraphPanel
series={graphResult}
width={width}
loading={loading}
absoluteRange={absoluteRange}
isStacked={false}
showPanel={true}
showingGraph={showingGraph}
showingTable={showingTable}
timeZone={timeZone}
onToggleGraph={this.onToggleGraph}
onUpdateTimeRange={this.onUpdateTimeRange}
showBars={false}
showLines={true}
/>
)}
{mode === ExploreMode.Metrics && (
<TableContainer width={width} exploreId={exploreId} onClickCell={this.onClickFilterLabel} />
)}
{mode === ExploreMode.Logs && (
<LogsContainer
width={width}
exploreId={exploreId}
syncedTimes={syncedTimes}
onClickFilterLabel={this.onClickFilterLabel}
onClickFilterOutLabel={this.onClickFilterOutLabel}
onStartScanning={this.onStartScanning}
onStopScanning={this.onStopScanning}
/>
)}
</>
)}
{showRichHistory && (
<RichHistoryContainer
width={width}
exploreId={exploreId}
onClose={this.toggleShowRichHistory}
/>
)}
</ErrorBoundaryAlert>
</main>
);
}}
</AutoSizer>
</div>
)}
</div>
);
}