mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: move panes into a keyed object (#66117)
This commit is contained in:
parent
049029b6a2
commit
f03d0698d3
@ -385,8 +385,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "28"]
|
||||
],
|
||||
"packages/grafana-data/src/types/explore.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"packages/grafana-data/src/types/fieldOverrides.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
@ -2748,9 +2747,25 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
|
||||
],
|
||||
"public/app/features/explore/state/history.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/explore/state/main.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
],
|
||||
"public/app/features/explore/state/query.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/explore/state/time.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/features/explore/state/time.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"]
|
||||
],
|
||||
"public/app/features/explore/state/utils.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||
|
@ -22,7 +22,7 @@ export interface ExploreTracePanelState {
|
||||
spanId?: string;
|
||||
}
|
||||
|
||||
export interface SplitOpenOptions<T> {
|
||||
export interface SplitOpenOptions<T extends AnyQuery = AnyQuery> {
|
||||
datasourceUid: string;
|
||||
/** @deprecated Will be removed in a future version. Use queries instead. */
|
||||
query?: T;
|
||||
@ -34,4 +34,4 @@ export interface SplitOpenOptions<T> {
|
||||
/**
|
||||
* SplitOpen type is used in Explore and related components.
|
||||
*/
|
||||
export type SplitOpen = <T extends DataQuery = any>(options?: SplitOpenOptions<T> | undefined) => void;
|
||||
export type SplitOpen = (options?: SplitOpenOptions | undefined) => void;
|
||||
|
@ -2,9 +2,9 @@ import { dateTime, ExploreUrlState, LogsSortOrder } from '@grafana/data';
|
||||
import { serializeStateToUrlParam } from '@grafana/data/src/utils/url';
|
||||
import { RefreshPicker } from '@grafana/ui';
|
||||
import store from 'app/core/store';
|
||||
import { ExploreId } from 'app/types';
|
||||
|
||||
import { DatasourceSrvMock, MockDataSourceApi } from '../../../test/mocks/datasource_srv';
|
||||
import { ExploreId } from '../../types';
|
||||
|
||||
import {
|
||||
buildQueryTransaction,
|
||||
|
@ -22,11 +22,13 @@ import { AddToDashboard } from '.';
|
||||
const setup = (children: ReactNode, queries: DataQuery[] = [{ refId: 'A' }]) => {
|
||||
const store = configureStore({
|
||||
explore: {
|
||||
left: {
|
||||
queries,
|
||||
queryResponse: createEmptyQueryResponse(),
|
||||
panes: {
|
||||
left: {
|
||||
queries,
|
||||
queryResponse: createEmptyQueryResponse(),
|
||||
},
|
||||
},
|
||||
} as ExploreState,
|
||||
} as unknown as ExploreState,
|
||||
});
|
||||
|
||||
return render(<Provider store={store}>{children}</Provider>);
|
||||
|
@ -4,7 +4,7 @@ import { AutoSizerProps } from 'react-virtualized-auto-sizer';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
|
||||
import { DataSourceApi, LoadingState, CoreApp, createTheme, EventBusSrv } from '@grafana/data';
|
||||
import { ExploreId } from 'app/types/explore';
|
||||
import { ExploreId } from 'app/types';
|
||||
|
||||
import { Explore, Props } from './Explore';
|
||||
import { scanStopAction } from './state/query';
|
||||
@ -86,7 +86,7 @@ const dummyProps: Props = {
|
||||
showTrace: true,
|
||||
showNodeGraph: true,
|
||||
showFlameGraph: true,
|
||||
splitOpen: () => {},
|
||||
splitOpen: jest.fn(),
|
||||
splitted: false,
|
||||
isFromCompactUrl: false,
|
||||
eventBus: new EventBusSrv(),
|
||||
|
@ -253,7 +253,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
};
|
||||
|
||||
onSplitOpen = (panelType: string) => {
|
||||
return async (options?: SplitOpenOptions<DataQuery>) => {
|
||||
return async (options?: SplitOpenOptions) => {
|
||||
this.props.splitOpen(options);
|
||||
if (options && this.props.datasourceInstance) {
|
||||
const target = (await getDataSourceSrv().get(options.datasourceUid)).type;
|
||||
@ -546,7 +546,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
|
||||
function mapStateToProps(state: StoreState, { exploreId }: ExploreProps) {
|
||||
const explore = state.explore;
|
||||
const { syncedTimes } = explore;
|
||||
const item: ExploreItemState = explore[exploreId]!;
|
||||
const item: ExploreItemState = explore.panes[exploreId]!;
|
||||
const timeZone = getTimeZone(state.user);
|
||||
const {
|
||||
datasourceInstance,
|
||||
|
@ -168,7 +168,9 @@ export function ExplorePage(props: GrafanaRouteComponentProps<{}, ExploreQueryPa
|
||||
const useExplorePageTitle = () => {
|
||||
const navModel = useNavModel('explore');
|
||||
const datasources = useSelector((state) =>
|
||||
[state.explore.left.datasourceInstance?.name, state.explore.right?.datasourceInstance?.name].filter(isTruthy)
|
||||
[state.explore.panes.left!.datasourceInstance?.name, state.explore.panes.right?.datasourceInstance?.name].filter(
|
||||
isTruthy
|
||||
)
|
||||
);
|
||||
|
||||
document.title = `${navModel.main.text} - ${datasources.join(' | ')} - ${Branding.AppTitle}`;
|
||||
|
@ -122,16 +122,16 @@ class ExplorePaneContainerUnconnected extends React.PureComponent<Props> {
|
||||
reportInteraction('grafana_explore_compact_notice');
|
||||
}
|
||||
|
||||
this.props.initializeExplore(
|
||||
this.props.initializeExplore({
|
||||
exploreId,
|
||||
rootDatasourceOverride || queries[0]?.datasource || initialDatasource,
|
||||
datasource: rootDatasourceOverride || queries[0]?.datasource || initialDatasource,
|
||||
queries,
|
||||
initialRange,
|
||||
width,
|
||||
this.exploreEvents,
|
||||
range: initialRange,
|
||||
containerWidth: width,
|
||||
eventBridge: this.exploreEvents,
|
||||
panelsState,
|
||||
isFromCompactUrl
|
||||
);
|
||||
isFromCompactUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +181,7 @@ function mapStateToProps(state: StoreState, props: OwnProps) {
|
||||
: getTimeRange(timeZone, DEFAULT_RANGE, fiscalYearStartMonth);
|
||||
|
||||
return {
|
||||
initialized: state.explore[props.exploreId]?.initialized,
|
||||
initialized: state.explore.panes[props.exploreId]?.initialized,
|
||||
initialDatasource,
|
||||
initialQueries: queries,
|
||||
initialRange,
|
||||
|
@ -90,7 +90,7 @@ export function ExploreQueryInspector(props: Props) {
|
||||
|
||||
function mapStateToProps(state: StoreState, { exploreId }: { exploreId: ExploreId }) {
|
||||
const explore = state.explore;
|
||||
const item: ExploreItemState = explore[exploreId]!;
|
||||
const item: ExploreItemState = explore.panes[exploreId]!;
|
||||
const { loading, queryResponse } = item;
|
||||
|
||||
return {
|
||||
|
@ -52,7 +52,7 @@ export function ExploreToolbar({ exploreId, topOfViewRef, onChangeTime }: Props)
|
||||
const { refreshInterval, loading, datasourceInstance, range, isLive, isPaused, syncedTimes } = useSelector(
|
||||
(state: StoreState) => ({
|
||||
...pick(
|
||||
state.explore[exploreId]!,
|
||||
state.explore.panes[exploreId]!,
|
||||
'refreshInterval',
|
||||
'loading',
|
||||
'datasourceInstance',
|
||||
@ -65,9 +65,9 @@ export function ExploreToolbar({ exploreId, topOfViewRef, onChangeTime }: Props)
|
||||
shallowEqual
|
||||
);
|
||||
const isLargerPane = useSelector((state: StoreState) => state.explore.largerExploreId === exploreId);
|
||||
const showSmallTimePicker = useSelector((state) => splitted || state.explore[exploreId]!.containerWidth < 1210);
|
||||
const showSmallTimePicker = useSelector((state) => splitted || state.explore.panes[exploreId]!.containerWidth < 1210);
|
||||
const showSmallDataSourcePicker = useSelector(
|
||||
(state) => state.explore[exploreId]!.containerWidth < (splitted ? 700 : 800)
|
||||
(state) => state.explore.panes[exploreId]!.containerWidth < (splitted ? 700 : 800)
|
||||
);
|
||||
|
||||
const shouldRotateSplitIcon = useMemo(
|
||||
|
@ -208,10 +208,9 @@ class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }) {
|
||||
function mapStateToProps(state: StoreState, { exploreId }: { exploreId: ExploreId }) {
|
||||
const explore = state.explore;
|
||||
// @ts-ignore
|
||||
const item: ExploreItemState = explore[exploreId];
|
||||
const item: ExploreItemState = explore.panes[exploreId]!;
|
||||
const {
|
||||
logsResult,
|
||||
loading,
|
||||
|
@ -2,8 +2,7 @@ import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { getDefaultTimeRange, MutableDataFrame } from '@grafana/data';
|
||||
|
||||
import { ExploreId } from '../../types';
|
||||
import { ExploreId } from 'app/types';
|
||||
|
||||
import { UnconnectedNodeGraphContainer } from './NodeGraphContainer';
|
||||
|
||||
|
@ -108,7 +108,7 @@ export function UnconnectedNodeGraphContainer(props: Props) {
|
||||
|
||||
function mapStateToProps(state: StoreState, { exploreId }: OwnProps) {
|
||||
return {
|
||||
range: state.explore[exploreId]!.range,
|
||||
range: state.explore.panes[exploreId]!.range,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -50,15 +50,16 @@ function setup(queries: DataQuery[]) {
|
||||
|
||||
const leftState = makeExplorePaneState();
|
||||
const initialState: ExploreState = {
|
||||
left: {
|
||||
...leftState,
|
||||
richHistory: [],
|
||||
datasourceInstance: datasources['someDs-uid'],
|
||||
queries,
|
||||
panes: {
|
||||
left: {
|
||||
...leftState,
|
||||
richHistory: [],
|
||||
datasourceInstance: datasources['someDs-uid'],
|
||||
queries,
|
||||
},
|
||||
},
|
||||
syncedTimes: false,
|
||||
correlations: [],
|
||||
right: undefined,
|
||||
richHistoryStorageFull: false,
|
||||
richHistoryLimitExceededWarningShown: false,
|
||||
};
|
||||
|
@ -2,7 +2,7 @@ import { fireEvent, render, screen, within } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { FieldType, getDefaultTimeRange, InternalTimeZones, toDataFrame } from '@grafana/data';
|
||||
import { ExploreId, TABLE_RESULTS_STYLE } from 'app/types/explore';
|
||||
import { ExploreId, TABLE_RESULTS_STYLE } from 'app/types';
|
||||
|
||||
import { RawPrometheusContainer } from './RawPrometheusContainer';
|
||||
|
||||
|
@ -30,7 +30,7 @@ interface PrometheusContainerState {
|
||||
|
||||
function mapStateToProps(state: StoreState, { exploreId }: RawPrometheusContainerProps) {
|
||||
const explore = state.explore;
|
||||
const item: ExploreItemState = explore[exploreId]!;
|
||||
const item: ExploreItemState = explore.panes[exploreId]!;
|
||||
const { loading: loadingInState, tableResult, rawPrometheusResult, range } = item;
|
||||
const rawPrometheusFrame: DataFrame[] = rawPrometheusResult ? [rawPrometheusResult] : [];
|
||||
const result = (tableResult?.length ?? false) > 0 && rawPrometheusResult ? tableResult : rawPrometheusFrame;
|
||||
|
@ -4,9 +4,9 @@ import { Provider } from 'react-redux';
|
||||
|
||||
import { DataQueryError, LoadingState, getDefaultTimeRange } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { ExploreId } from 'app/types';
|
||||
|
||||
import { configureStore } from '../../store/configureStore';
|
||||
import { ExploreId } from '../../types';
|
||||
|
||||
import { ResponseErrorContainer } from './ResponseErrorContainer';
|
||||
|
||||
@ -46,7 +46,7 @@ describe('ResponseErrorContainer', () => {
|
||||
|
||||
function setup(error: DataQueryError) {
|
||||
const store = configureStore();
|
||||
store.getState().explore[ExploreId.left].queryResponse = {
|
||||
store.getState().explore.panes.left!.queryResponse = {
|
||||
timeRange: getDefaultTimeRange(),
|
||||
series: [],
|
||||
state: LoadingState.Error,
|
||||
|
@ -11,7 +11,7 @@ interface Props {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
export function ResponseErrorContainer(props: Props) {
|
||||
const queryResponse = useSelector((state) => state.explore[props.exploreId]?.queryResponse);
|
||||
const queryResponse = useSelector((state) => state.explore.panes[props.exploreId]!.queryResponse);
|
||||
const queryError = queryResponse?.state === LoadingState.Error ? queryResponse?.error : undefined;
|
||||
|
||||
// Errors with ref ids are shown below the corresponding query
|
||||
|
@ -3,8 +3,7 @@ import React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { SortOrder } from 'app/core/utils/richHistory';
|
||||
|
||||
import { ExploreId } from '../../../types/explore';
|
||||
import { ExploreId } from 'app/types';
|
||||
|
||||
import { RichHistory, RichHistoryProps, Tabs } from './RichHistory';
|
||||
|
||||
|
@ -6,8 +6,8 @@ import { DataSourceApi, DataSourceInstanceSettings, DataSourcePluginMeta } from
|
||||
import { DataQuery, DataSourceRef } from '@grafana/schema';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { MixedDatasource } from 'app/plugins/datasource/mixed/MixedDataSource';
|
||||
import { ExploreId, RichHistoryQuery } from 'app/types';
|
||||
import { ShowConfirmModalEvent } from 'app/types/events';
|
||||
import { ExploreId, RichHistoryQuery } from 'app/types/explore';
|
||||
|
||||
import { RichHistoryCard, Props } from './RichHistoryCard';
|
||||
|
||||
|
@ -23,7 +23,7 @@ import { RichHistoryQuery, ExploreId } from 'app/types/explore';
|
||||
|
||||
function mapStateToProps(state: StoreState, { exploreId }: { exploreId: ExploreId }) {
|
||||
const explore = state.explore;
|
||||
const { datasourceInstance } = explore[exploreId]!;
|
||||
const { datasourceInstance } = explore.panes[exploreId]!;
|
||||
return {
|
||||
exploreId,
|
||||
datasourceInstance,
|
||||
|
@ -2,8 +2,7 @@ import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { SortOrder } from 'app/core/utils/richHistory';
|
||||
|
||||
import { ExploreId } from '../../../types/explore';
|
||||
import { ExploreId } from 'app/types';
|
||||
|
||||
import { Tabs } from './RichHistory';
|
||||
import { RichHistoryContainer, Props } from './RichHistoryContainer';
|
||||
|
@ -26,8 +26,7 @@ import { RichHistory, Tabs } from './RichHistory';
|
||||
|
||||
function mapStateToProps(state: StoreState, { exploreId }: { exploreId: ExploreId }) {
|
||||
const explore = state.explore;
|
||||
// @ts-ignore
|
||||
const item: ExploreItemState = explore[exploreId];
|
||||
const item: ExploreItemState = explore.panes[exploreId]!;
|
||||
const richHistorySearchFilters = item.richHistorySearchFilters;
|
||||
const richHistorySettings = explore.richHistorySettings;
|
||||
const { datasourceInstance } = item;
|
||||
|
@ -2,8 +2,7 @@ import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { SortOrder } from 'app/core/utils/richHistory';
|
||||
|
||||
import { ExploreId } from '../../../types/explore';
|
||||
import { ExploreId } from 'app/types';
|
||||
|
||||
import { RichHistoryStarredTab, RichHistoryStarredTabProps } from './RichHistoryStarredTab';
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { render, screen, within } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { DataFrame, FieldType, getDefaultTimeRange, InternalTimeZones, toDataFrame } from '@grafana/data';
|
||||
import { ExploreId } from 'app/types/explore';
|
||||
import { ExploreId } from 'app/types';
|
||||
|
||||
import { TableContainer } from './TableContainer';
|
||||
|
||||
|
@ -21,8 +21,7 @@ interface TableContainerProps {
|
||||
|
||||
function mapStateToProps(state: StoreState, { exploreId }: TableContainerProps) {
|
||||
const explore = state.explore;
|
||||
// @ts-ignore
|
||||
const item: ExploreItemState = explore[exploreId];
|
||||
const item: ExploreItemState = explore.panes[exploreId]!;
|
||||
const { loading: loadingInState, tableResult, range } = item;
|
||||
const loading = tableResult && tableResult.length > 0 ? false : loadingInState;
|
||||
return { loading, tableResult, range };
|
||||
|
@ -256,7 +256,7 @@ function useFocusSpanLink(options: {
|
||||
refId?: string;
|
||||
datasource?: DataSourceApi;
|
||||
}): [string | undefined, (traceId: string, spanId: string) => LinkModel<Field>] {
|
||||
const panelState = useSelector((state) => state.explore[options.exploreId]?.panelsState.trace);
|
||||
const panelState = useSelector((state) => state.explore.panes[options.exploreId]?.panelsState.trace);
|
||||
const focusedSpanId = panelState?.spanId;
|
||||
|
||||
const dispatch = useDispatch();
|
||||
@ -269,7 +269,7 @@ function useFocusSpanLink(options: {
|
||||
);
|
||||
|
||||
const query = useSelector((state) =>
|
||||
state.explore[options.exploreId]?.queries.find((query) => query.refId === options.refId)
|
||||
state.explore.panes[options.exploreId]?.queries.find((query) => query.refId === options.refId)
|
||||
);
|
||||
|
||||
const createFocusSpanLink = (traceId: string, spanId: string) => {
|
||||
|
@ -47,7 +47,7 @@ export function TraceViewContainer(props: Props) {
|
||||
const [focusedSpanIdForSearch, setFocusedSpanIdForSearch] = useState('');
|
||||
const [searchBarSuffix, setSearchBarSuffix] = useState('');
|
||||
const datasource = useSelector(
|
||||
(state: StoreState) => state.explore[props.exploreId!]?.datasourceInstance ?? undefined
|
||||
(state: StoreState) => state.explore.panes[props.exploreId]?.datasourceInstance ?? undefined
|
||||
);
|
||||
const datasourceType = datasource ? datasource?.type : 'unknown';
|
||||
|
||||
|
@ -3,9 +3,9 @@ import { of } from 'rxjs';
|
||||
|
||||
import { serializeStateToUrlParam } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { ExploreId } from 'app/types';
|
||||
|
||||
import { silenceConsoleOutput } from '../../../../test/core/utils/silenceConsoleOutput';
|
||||
import { ExploreId } from '../../../types';
|
||||
|
||||
import {
|
||||
assertDataSourceFilterVisibility,
|
||||
|
@ -45,7 +45,7 @@ export function changeDatasource(
|
||||
return async (dispatch, getState) => {
|
||||
const orgId = getState().user.orgId;
|
||||
const { history, instance } = await loadAndInitDatasource(orgId, { uid: datasourceUid });
|
||||
const currentDataSourceInstance = getState().explore[exploreId]!.datasourceInstance;
|
||||
const currentDataSourceInstance = getState().explore.panes[exploreId]!.datasourceInstance;
|
||||
|
||||
reportInteraction('explore_change_ds', {
|
||||
from: (currentDataSourceInstance?.meta?.mixed ? 'mixed' : currentDataSourceInstance?.type) || 'unknown',
|
||||
@ -61,11 +61,11 @@ export function changeDatasource(
|
||||
);
|
||||
|
||||
if (options?.importQueries) {
|
||||
const queries = getState().explore[exploreId]!.queries;
|
||||
const queries = getState().explore.panes[exploreId]!.queries;
|
||||
await dispatch(importQueries(exploreId, queries, currentDataSourceInstance, instance));
|
||||
}
|
||||
|
||||
if (getState().explore[exploreId]!.isLive) {
|
||||
if (getState().explore.panes[exploreId]!.isLive) {
|
||||
dispatch(changeRefreshInterval(exploreId, RefreshPicker.offOption.value));
|
||||
}
|
||||
|
||||
|
@ -29,9 +29,11 @@ function setupStore(state?: any) {
|
||||
return configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
...(state || {}),
|
||||
panes: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
...(state || {}),
|
||||
},
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
@ -98,7 +100,7 @@ describe('refreshExplore', () => {
|
||||
await dispatch(
|
||||
refreshExplore(ExploreId.left, serializeStateToUrlParam({ datasource: 'newDs', queries: [], range: testRange }))
|
||||
);
|
||||
expect(getState().explore[ExploreId.left].datasourceInstance?.name).toBe('newDs');
|
||||
expect(getState().explore.panes.left!.datasourceInstance?.name).toBe('newDs');
|
||||
});
|
||||
|
||||
it('should change and run new queries from the URL', async () => {
|
||||
@ -111,21 +113,19 @@ describe('refreshExplore', () => {
|
||||
)
|
||||
);
|
||||
// same
|
||||
const state = getState().explore[ExploreId.left];
|
||||
const state = getState().explore.panes.left!;
|
||||
expect(state.datasourceInstance?.name).toBe('someDs');
|
||||
expect(state.queries.length).toBe(1);
|
||||
expect(state.queries).toMatchObject([{ expr: 'count()' }]);
|
||||
expect(datasources.someDs.query).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should not do anything if pane is not initialized', async () => {
|
||||
const { dispatch, getState } = setup({
|
||||
initialized: false,
|
||||
});
|
||||
it('should not do anything if pane is not present', async () => {
|
||||
const { dispatch, getState } = setup({});
|
||||
const state = getState();
|
||||
await dispatch(
|
||||
refreshExplore(
|
||||
ExploreId.left,
|
||||
ExploreId.right,
|
||||
serializeStateToUrlParam({ datasource: 'newDs', queries: [{ expr: 'count()', refId: 'A' }], range: testRange })
|
||||
)
|
||||
);
|
||||
|
@ -22,7 +22,7 @@ import {
|
||||
getTimeRangeFromUrl,
|
||||
} from 'app/core/utils/explore';
|
||||
import { getFiscalYearStartMonth, getTimeZone } from 'app/features/profile/state/selectors';
|
||||
import { ThunkResult } from 'app/types';
|
||||
import { createAsyncThunk, ThunkResult } from 'app/types';
|
||||
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||
|
||||
import { datasourceReducer } from './datasource';
|
||||
@ -67,7 +67,7 @@ export function changePanelState(
|
||||
panelState: ExplorePanelsState[PreferredVisualisationType]
|
||||
): ThunkResult<void> {
|
||||
return async (dispatch, getState) => {
|
||||
const exploreItem = getState().explore[exploreId];
|
||||
const exploreItem = getState().explore.panes[exploreId];
|
||||
if (exploreItem === undefined) {
|
||||
return;
|
||||
}
|
||||
@ -89,7 +89,7 @@ export function changePanelState(
|
||||
* 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 interface InitializeExplorePayload {
|
||||
interface InitializeExplorePayload {
|
||||
exploreId: ExploreId;
|
||||
containerWidth: number;
|
||||
eventBridge: EventBusExtended;
|
||||
@ -99,7 +99,7 @@ export interface InitializeExplorePayload {
|
||||
datasourceInstance?: DataSourceApi;
|
||||
isFromCompactUrl?: boolean;
|
||||
}
|
||||
export const initializeExploreAction = createAction<InitializeExplorePayload>('explore/initializeExplore');
|
||||
const initializeExploreAction = createAction<InitializeExplorePayload>('explore/initializeExploreAction');
|
||||
|
||||
export interface SetUrlReplacedPayload {
|
||||
exploreId: ExploreId;
|
||||
@ -117,6 +117,16 @@ export function changeSize(
|
||||
return changeSizeAction({ exploreId, height, width });
|
||||
}
|
||||
|
||||
interface InitializeExploreOptions {
|
||||
exploreId: ExploreId;
|
||||
datasource: DataSourceRef | string;
|
||||
queries: DataQuery[];
|
||||
range: TimeRange;
|
||||
containerWidth: number;
|
||||
eventBridge: EventBusExtended;
|
||||
panelsState?: ExplorePanelsState;
|
||||
isFromCompactUrl?: boolean;
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
@ -125,17 +135,21 @@ export function changeSize(
|
||||
* and can be either a string that is the name or uid, or a datasourceRef
|
||||
* This is to maximize compatability with how datasources are accessed from the URL param.
|
||||
*/
|
||||
export function initializeExplore(
|
||||
exploreId: ExploreId,
|
||||
datasource: DataSourceRef | string,
|
||||
queries: DataQuery[],
|
||||
range: TimeRange,
|
||||
containerWidth: number,
|
||||
eventBridge: EventBusExtended,
|
||||
panelsState?: ExplorePanelsState,
|
||||
isFromCompactUrl?: boolean
|
||||
): ThunkResult<void> {
|
||||
return async (dispatch, getState) => {
|
||||
export const initializeExplore = createAsyncThunk(
|
||||
'explore/initializeExplore',
|
||||
async (
|
||||
{
|
||||
exploreId,
|
||||
datasource,
|
||||
queries,
|
||||
range,
|
||||
containerWidth,
|
||||
eventBridge,
|
||||
panelsState,
|
||||
isFromCompactUrl,
|
||||
}: InitializeExploreOptions,
|
||||
{ dispatch, getState }
|
||||
) => {
|
||||
const exploreDatasources = getDataSourceSrv().getList();
|
||||
let instance = undefined;
|
||||
let history: HistoryItem[] = [];
|
||||
@ -170,8 +184,8 @@ export function initializeExplore(
|
||||
// user to go back to previous url.
|
||||
dispatch(runQueries(exploreId, { replaceUrl: true }));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Reacts to changes in URL state that we need to sync back to our redux state. Computes diff of newUrlQuery vs current
|
||||
@ -179,8 +193,8 @@ export function initializeExplore(
|
||||
*/
|
||||
export function refreshExplore(exploreId: ExploreId, newUrlQuery: string): ThunkResult<void> {
|
||||
return async (dispatch, getState) => {
|
||||
const itemState = getState().explore[exploreId];
|
||||
if (!itemState?.initialized) {
|
||||
const itemState = getState().explore.panes[exploreId];
|
||||
if (!itemState) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -208,7 +222,15 @@ export function refreshExplore(exploreId: ExploreId, newUrlQuery: string): Thunk
|
||||
if (update.datasource) {
|
||||
const initialQueries = await ensureQueries(queries);
|
||||
await dispatch(
|
||||
initializeExplore(exploreId, datasource, initialQueries, range, containerWidth, eventBridge, panelsState)
|
||||
initializeExplore({
|
||||
exploreId,
|
||||
datasource,
|
||||
queries: initialQueries,
|
||||
range,
|
||||
containerWidth,
|
||||
eventBridge,
|
||||
panelsState,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { DefaultTimeZone, TimeRange, toUtc, SupplementaryQueryType } from '@grafana/data';
|
||||
|
||||
import { ExploreId } from '../../../types';
|
||||
|
||||
export const createDefaultInitialState = () => {
|
||||
const t = toUtc();
|
||||
const testRange: TimeRange = {
|
||||
@ -19,37 +17,39 @@ export const createDefaultInitialState = () => {
|
||||
timeZone: DefaultTimeZone,
|
||||
},
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
datasourceInstance: {
|
||||
query: jest.fn(),
|
||||
getRef: jest.fn(),
|
||||
getDataProvider: jest.fn(),
|
||||
getSupportedSupplementaryQueryTypes: jest
|
||||
.fn()
|
||||
.mockImplementation(() => [SupplementaryQueryType.LogsVolume, SupplementaryQueryType.LogsSample]),
|
||||
getSupplementaryQuery: jest.fn(),
|
||||
meta: {
|
||||
id: 'something',
|
||||
panes: {
|
||||
left: {
|
||||
datasourceInstance: {
|
||||
query: jest.fn(),
|
||||
getRef: jest.fn(),
|
||||
getDataProvider: jest.fn(),
|
||||
getSupportedSupplementaryQueryTypes: jest
|
||||
.fn()
|
||||
.mockImplementation(() => [SupplementaryQueryType.LogsVolume, SupplementaryQueryType.LogsSample]),
|
||||
getSupplementaryQuery: jest.fn(),
|
||||
meta: {
|
||||
id: 'something',
|
||||
},
|
||||
},
|
||||
},
|
||||
initialized: true,
|
||||
containerWidth: 1920,
|
||||
eventBridge: { emit: () => {} },
|
||||
queries: [{ expr: 'test' }],
|
||||
range: testRange,
|
||||
history: [],
|
||||
refreshInterval: {
|
||||
label: 'Off',
|
||||
value: 0,
|
||||
},
|
||||
cache: [],
|
||||
richHistory: [],
|
||||
supplementaryQueries: {
|
||||
[SupplementaryQueryType.LogsVolume]: {
|
||||
enabled: true,
|
||||
initialized: true,
|
||||
containerWidth: 1920,
|
||||
eventBridge: { emit: () => {} },
|
||||
queries: [{ expr: 'test' }],
|
||||
range: testRange,
|
||||
history: [],
|
||||
refreshInterval: {
|
||||
label: 'Off',
|
||||
value: 0,
|
||||
},
|
||||
[SupplementaryQueryType.LogsSample]: {
|
||||
enabled: true,
|
||||
cache: [],
|
||||
richHistory: [],
|
||||
supplementaryQueries: {
|
||||
[SupplementaryQueryType.LogsVolume]: {
|
||||
enabled: true,
|
||||
},
|
||||
[SupplementaryQueryType.LogsSample]: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -67,8 +67,9 @@ const updateRichHistoryState = ({ updatedQuery, deletedId }: SyncHistoryUpdatesO
|
||||
};
|
||||
|
||||
const forEachExplorePane = (state: ExploreState, callback: (item: ExploreItemState, exploreId: ExploreId) => void) => {
|
||||
callback(state.left, ExploreId.left);
|
||||
state.right && callback(state.right, ExploreId.right);
|
||||
Object.entries(state.panes).forEach(([exploreId, item]) => {
|
||||
callback(item!, exploreId as ExploreId);
|
||||
});
|
||||
};
|
||||
|
||||
export const addHistoryItem = (
|
||||
@ -130,7 +131,7 @@ export const deleteRichHistory = (): ThunkResult<void> => {
|
||||
|
||||
export const loadRichHistory = (exploreId: ExploreId): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const filters = getState().explore![exploreId]?.richHistorySearchFilters;
|
||||
const filters = getState().explore.panes[exploreId]!.richHistorySearchFilters;
|
||||
if (filters) {
|
||||
const richHistoryResults = await getRichHistory(filters);
|
||||
dispatch(richHistoryUpdatedAction({ richHistoryResults, exploreId }));
|
||||
@ -140,8 +141,8 @@ export const loadRichHistory = (exploreId: ExploreId): ThunkResult<void> => {
|
||||
|
||||
export const loadMoreRichHistory = (exploreId: ExploreId): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const currentFilters = getState().explore![exploreId]?.richHistorySearchFilters;
|
||||
const currentRichHistory = getState().explore![exploreId]?.richHistory;
|
||||
const currentFilters = getState().explore.panes[exploreId]?.richHistorySearchFilters;
|
||||
const currentRichHistory = getState().explore.panes[exploreId]?.richHistory;
|
||||
if (currentFilters && currentRichHistory) {
|
||||
const nextFilters = { ...currentFilters, page: (currentFilters?.page || 1) + 1 };
|
||||
const moreRichHistory = await getRichHistory(nextFilters);
|
||||
|
@ -117,7 +117,7 @@ describe('navigateToExplore', () => {
|
||||
describe('Explore reducer', () => {
|
||||
describe('split view', () => {
|
||||
describe('split close', () => {
|
||||
it('should keep right pane as left when left is closed', () => {
|
||||
it('should move right pane to left when left is closed', () => {
|
||||
const leftItemMock = {
|
||||
containerWidth: 100,
|
||||
} as unknown as ExploreItemState;
|
||||
@ -127,8 +127,10 @@ describe('Explore reducer', () => {
|
||||
} as unknown as ExploreItemState;
|
||||
|
||||
const initialState = {
|
||||
left: leftItemMock,
|
||||
right: rightItemMock,
|
||||
panes: {
|
||||
left: leftItemMock,
|
||||
right: rightItemMock,
|
||||
},
|
||||
} as unknown as ExploreState;
|
||||
|
||||
// closing left item
|
||||
@ -138,9 +140,10 @@ describe('Explore reducer', () => {
|
||||
.thenStateShouldEqual({
|
||||
evenSplitPanes: true,
|
||||
largerExploreId: undefined,
|
||||
left: rightItemMock,
|
||||
panes: {
|
||||
left: rightItemMock,
|
||||
},
|
||||
maxedExploreId: undefined,
|
||||
right: undefined,
|
||||
syncedTimes: false,
|
||||
} as unknown as ExploreState);
|
||||
});
|
||||
@ -154,8 +157,10 @@ describe('Explore reducer', () => {
|
||||
} as unknown as ExploreItemState;
|
||||
|
||||
const initialState = {
|
||||
left: leftItemMock,
|
||||
right: rightItemMock,
|
||||
panes: {
|
||||
left: leftItemMock,
|
||||
right: rightItemMock,
|
||||
},
|
||||
} as unknown as ExploreState;
|
||||
|
||||
// closing left item
|
||||
@ -165,9 +170,10 @@ describe('Explore reducer', () => {
|
||||
.thenStateShouldEqual({
|
||||
evenSplitPanes: true,
|
||||
largerExploreId: undefined,
|
||||
left: leftItemMock,
|
||||
panes: {
|
||||
left: leftItemMock,
|
||||
},
|
||||
maxedExploreId: undefined,
|
||||
right: undefined,
|
||||
syncedTimes: false,
|
||||
} as unknown as ExploreState);
|
||||
});
|
||||
@ -178,8 +184,10 @@ describe('Explore reducer', () => {
|
||||
} as unknown as ExploreItemState;
|
||||
|
||||
const initialState = {
|
||||
left: itemMock,
|
||||
right: itemMock,
|
||||
panes: {
|
||||
right: itemMock,
|
||||
left: itemMock,
|
||||
},
|
||||
syncedTimes: true,
|
||||
} as unknown as ExploreState;
|
||||
|
||||
@ -188,8 +196,9 @@ describe('Explore reducer', () => {
|
||||
.whenActionIsDispatched(splitCloseAction({ itemId: ExploreId.right }))
|
||||
.thenStateShouldEqual({
|
||||
evenSplitPanes: true,
|
||||
left: itemMock,
|
||||
right: undefined,
|
||||
panes: {
|
||||
left: itemMock,
|
||||
},
|
||||
syncedTimes: false,
|
||||
} as unknown as ExploreState);
|
||||
});
|
||||
|
@ -3,18 +3,17 @@ import { AnyAction } from 'redux';
|
||||
|
||||
import { ExploreUrlState, serializeStateToUrlParam, SplitOpenOptions, UrlQueryMap } from '@grafana/data';
|
||||
import { DataSourceSrv, locationService } from '@grafana/runtime';
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { GetExploreUrlArguments, stopQueryState } from 'app/core/utils/explore';
|
||||
import { PanelModel } from 'app/features/dashboard/state';
|
||||
import { ExploreId, ExploreItemState, ExploreState } from 'app/types/explore';
|
||||
|
||||
import { RichHistoryResults } from '../../../core/history/RichHistoryStorage';
|
||||
import { RichHistorySearchFilters, RichHistorySettings } from '../../../core/utils/richHistoryTypes';
|
||||
import { ThunkResult } from '../../../types';
|
||||
import { createAsyncThunk, ThunkResult } from '../../../types';
|
||||
import { CorrelationData } from '../../correlations/useCorrelations';
|
||||
import { TimeSrv } from '../../dashboard/services/TimeSrv';
|
||||
|
||||
import { paneReducer } from './explorePane';
|
||||
import { initializeExplore, paneReducer } from './explorePane';
|
||||
import { getUrlStateFromPaneState, makeExplorePaneState } from './utils';
|
||||
|
||||
//
|
||||
@ -72,12 +71,12 @@ export const splitCloseAction = createAction<SplitCloseActionPayload>('explore/s
|
||||
* Not all of the redux state is reflected in URL though.
|
||||
*/
|
||||
export const stateSave = (options?: { replace?: boolean }): ThunkResult<void> => {
|
||||
return (dispatch, getState) => {
|
||||
const { left, right } = getState().explore;
|
||||
return (_, getState) => {
|
||||
const { left, right } = getState().explore.panes;
|
||||
const orgId = getState().user.orgId.toString();
|
||||
const urlStates: { [index: string]: string | null } = { orgId };
|
||||
|
||||
urlStates.left = serializeStateToUrlParam(getUrlStateFromPaneState(left));
|
||||
urlStates.left = serializeStateToUrlParam(getUrlStateFromPaneState(left!));
|
||||
|
||||
if (right) {
|
||||
urlStates.right = serializeStateToUrlParam(getUrlStateFromPaneState(right));
|
||||
@ -100,9 +99,10 @@ export const lastSavedUrl: UrlQueryMap = {};
|
||||
* or uses values from options arg. This does only navigation each pane is then responsible for initialization from
|
||||
* the URL.
|
||||
*/
|
||||
export const splitOpen = <T extends DataQuery = DataQuery>(options?: SplitOpenOptions<T>): ThunkResult<void> => {
|
||||
return async (dispatch, getState) => {
|
||||
const leftState: ExploreItemState = getState().explore[ExploreId.left];
|
||||
export const splitOpen = createAsyncThunk(
|
||||
'explore/splitOpen',
|
||||
async (options: SplitOpenOptions | undefined, { getState }) => {
|
||||
const leftState: ExploreItemState = getState().explore.panes.left!;
|
||||
const leftUrlState = getUrlStateFromPaneState(leftState);
|
||||
let rightUrlState: ExploreUrlState = leftUrlState;
|
||||
|
||||
@ -119,8 +119,8 @@ export const splitOpen = <T extends DataQuery = DataQuery>(options?: SplitOpenOp
|
||||
|
||||
const urlState = serializeStateToUrlParam(rightUrlState);
|
||||
locationService.partial({ right: urlState }, true);
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Close the split view and save URL state. We need to update the state here because when closing we cannot just
|
||||
@ -169,8 +169,9 @@ export const navigateToExplore = (
|
||||
const initialExploreItemState = makeExplorePaneState();
|
||||
export const initialExploreState: ExploreState = {
|
||||
syncedTimes: false,
|
||||
left: initialExploreItemState,
|
||||
right: undefined,
|
||||
panes: {
|
||||
[ExploreId.left]: initialExploreItemState,
|
||||
},
|
||||
correlations: undefined,
|
||||
richHistoryStorageFull: false,
|
||||
richHistoryLimitExceededWarningShown: false,
|
||||
@ -186,13 +187,12 @@ export const initialExploreState: ExploreState = {
|
||||
export const exploreReducer = (state = initialExploreState, action: AnyAction): ExploreState => {
|
||||
if (splitCloseAction.match(action)) {
|
||||
const { itemId } = action.payload;
|
||||
const targetSplit = {
|
||||
left: itemId === ExploreId.left ? state.right! : state.left,
|
||||
right: undefined,
|
||||
const panes = {
|
||||
left: itemId === ExploreId.left ? state.panes.right : state.panes.left,
|
||||
};
|
||||
return {
|
||||
...state,
|
||||
...targetSplit,
|
||||
panes,
|
||||
largerExploreId: undefined,
|
||||
maxedExploreId: undefined,
|
||||
evenSplitPanes: true,
|
||||
@ -255,18 +255,18 @@ export const exploreReducer = (state = initialExploreState, action: AnyAction):
|
||||
}
|
||||
|
||||
if (resetExploreAction.match(action)) {
|
||||
const leftState = state[ExploreId.left];
|
||||
const rightState = state[ExploreId.right];
|
||||
stopQueryState(leftState.querySubscription);
|
||||
if (rightState) {
|
||||
stopQueryState(rightState.querySubscription);
|
||||
// FIXME: reducers should REALLY not have side effects.
|
||||
for (const [, pane] of Object.entries(state.panes).filter(([exploreId]) => exploreId !== ExploreId.left)) {
|
||||
stopQueryState(pane!.querySubscription);
|
||||
}
|
||||
|
||||
return {
|
||||
...initialExploreState,
|
||||
left: {
|
||||
...initialExploreItemState,
|
||||
queries: state.left.queries,
|
||||
panes: {
|
||||
left: {
|
||||
...initialExploreItemState,
|
||||
queries: state.panes.left!.queries,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -279,12 +279,40 @@ export const exploreReducer = (state = initialExploreState, action: AnyAction):
|
||||
};
|
||||
}
|
||||
|
||||
if (splitOpen.pending.match(action)) {
|
||||
return {
|
||||
...state,
|
||||
panes: {
|
||||
...state.panes,
|
||||
right: initialExploreItemState,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (initializeExplore.pending.match(action)) {
|
||||
return {
|
||||
...state,
|
||||
panes: {
|
||||
...state.panes,
|
||||
[action.meta.arg.exploreId]: initialExploreItemState,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (action.payload) {
|
||||
const { exploreId } = action.payload;
|
||||
if (exploreId !== undefined) {
|
||||
// @ts-ignore
|
||||
const explorePaneState = state[exploreId];
|
||||
return { ...state, [exploreId]: paneReducer(explorePaneState, action) };
|
||||
return {
|
||||
...state,
|
||||
panes: Object.entries(state.panes).reduce<ExploreState['panes']>((acc, [id, pane]) => {
|
||||
if (id === exploreId) {
|
||||
acc[id as ExploreId] = paneReducer(pane, action);
|
||||
} else {
|
||||
acc[id as ExploreId] = pane;
|
||||
}
|
||||
return acc;
|
||||
}, {}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ jest.mock('@grafana/runtime', () => ({
|
||||
}));
|
||||
|
||||
function setupQueryResponse(state: StoreState) {
|
||||
const leftDatasourceInstance = assertIsDefined(state.explore[ExploreId.left].datasourceInstance);
|
||||
const leftDatasourceInstance = assertIsDefined(state.explore.panes.left!.datasourceInstance);
|
||||
|
||||
jest.mocked(leftDatasourceInstance.query).mockReturnValueOnce(
|
||||
of({
|
||||
@ -126,10 +126,12 @@ async function setupStore(queries: DataQuery[], datasourceInstance: Partial<Data
|
||||
const store: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[exploreId]: {
|
||||
...defaultInitialState.explore[exploreId],
|
||||
queries: queries,
|
||||
datasourceInstance: datasourceInstance,
|
||||
panes: {
|
||||
[exploreId]: {
|
||||
...defaultInitialState.explore.panes[exploreId],
|
||||
queries: queries,
|
||||
datasourceInstance: datasourceInstance,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
@ -157,8 +159,8 @@ describe('runQueries', () => {
|
||||
setupQueryResponse(getState());
|
||||
await dispatch(saveCorrelationsAction([]));
|
||||
await dispatch(runQueries(ExploreId.left));
|
||||
expect(getState().explore[ExploreId.left].showMetrics).toBeTruthy();
|
||||
expect(getState().explore[ExploreId.left].graphResult).toBeDefined();
|
||||
expect(getState().explore.panes.left!.showMetrics).toBeTruthy();
|
||||
expect(getState().explore.panes.left!.graphResult).toBeDefined();
|
||||
});
|
||||
|
||||
it('should modify the request-id for all supplementary queries', () => {
|
||||
@ -167,7 +169,7 @@ describe('runQueries', () => {
|
||||
dispatch(saveCorrelationsAction([]));
|
||||
dispatch(runQueries(ExploreId.left));
|
||||
|
||||
const state = getState().explore[ExploreId.left];
|
||||
const state = getState().explore.panes.left!;
|
||||
expect(state.queryResponse.request?.requestId).toBe('explore_left');
|
||||
const datasource = state.datasourceInstance as unknown as DataSourceWithSupplementaryQueriesSupport<DataQuery>;
|
||||
for (const type of supplementaryQueryTypes) {
|
||||
@ -182,21 +184,21 @@ describe('runQueries', () => {
|
||||
|
||||
it('should set state to done if query completes without emitting', async () => {
|
||||
const { dispatch, getState } = setupTests();
|
||||
const leftDatasourceInstance = assertIsDefined(getState().explore[ExploreId.left].datasourceInstance);
|
||||
const leftDatasourceInstance = assertIsDefined(getState().explore.panes.left!.datasourceInstance);
|
||||
jest.mocked(leftDatasourceInstance.query).mockReturnValueOnce(EMPTY);
|
||||
await dispatch(saveCorrelationsAction([]));
|
||||
await dispatch(runQueries(ExploreId.left));
|
||||
await new Promise((resolve) => setTimeout(() => resolve(''), 500));
|
||||
expect(getState().explore[ExploreId.left].queryResponse.state).toBe(LoadingState.Done);
|
||||
expect(getState().explore.panes.left!.queryResponse.state).toBe(LoadingState.Done);
|
||||
});
|
||||
|
||||
it('shows results only after correlations are loaded', async () => {
|
||||
const { dispatch, getState } = setupTests();
|
||||
setupQueryResponse(getState());
|
||||
await dispatch(runQueries(ExploreId.left));
|
||||
expect(getState().explore[ExploreId.left].graphResult).not.toBeDefined();
|
||||
expect(getState().explore.panes.left!.graphResult).not.toBeDefined();
|
||||
await dispatch(saveCorrelationsAction([]));
|
||||
expect(getState().explore[ExploreId.left].graphResult).toBeDefined();
|
||||
expect(getState().explore.panes.left!.graphResult).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@ -207,16 +209,18 @@ describe('running queries', () => {
|
||||
const exploreId = ExploreId.left;
|
||||
const initialState = {
|
||||
explore: {
|
||||
[exploreId]: {
|
||||
datasourceInstance: { name: 'testDs' },
|
||||
initialized: true,
|
||||
loading: true,
|
||||
querySubscription: unsubscribable,
|
||||
queries: ['A'],
|
||||
range: testRange,
|
||||
supplementaryQueries: {
|
||||
[SupplementaryQueryType.LogsVolume]: { enabled: true },
|
||||
[SupplementaryQueryType.LogsSample]: { enabled: true },
|
||||
panes: {
|
||||
[exploreId]: {
|
||||
datasourceInstance: { name: 'testDs' },
|
||||
initialized: true,
|
||||
loading: true,
|
||||
querySubscription: unsubscribable,
|
||||
queries: ['A'],
|
||||
range: testRange,
|
||||
supplementaryQueries: {
|
||||
[SupplementaryQueryType.LogsVolume]: { enabled: true },
|
||||
[SupplementaryQueryType.LogsSample]: { enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -257,10 +261,12 @@ describe('changeQueries', () => {
|
||||
const { dispatch } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
datasourceInstance: datasources[0],
|
||||
queries: originalQueries,
|
||||
panes: {
|
||||
left: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
datasourceInstance: datasources[0],
|
||||
queries: originalQueries,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
@ -289,10 +295,12 @@ describe('changeQueries', () => {
|
||||
const { dispatch } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
datasourceInstance: datasources[0],
|
||||
queries: [{ refId: 'A', datasource: datasources[0].getRef() }],
|
||||
panes: {
|
||||
left: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
datasourceInstance: datasources[0],
|
||||
queries: [{ refId: 'A', datasource: datasources[0].getRef() }],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
@ -316,10 +324,12 @@ describe('changeQueries', () => {
|
||||
const { dispatch, getState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
datasourceInstance: datasources[0],
|
||||
queries: originalQueries,
|
||||
panes: {
|
||||
left: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
datasourceInstance: datasources[0],
|
||||
queries: originalQueries,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
@ -331,18 +341,20 @@ describe('changeQueries', () => {
|
||||
})
|
||||
);
|
||||
|
||||
expect(getState().explore[ExploreId.left].queries[0]).toHaveProperty('refId', 'A');
|
||||
expect(getState().explore[ExploreId.left].queries[0]).toHaveProperty('datasource', datasources[1].getRef());
|
||||
expect(getState().explore.panes.left!.queries[0]).toHaveProperty('refId', 'A');
|
||||
expect(getState().explore.panes.left!.queries[0]).toHaveProperty('datasource', datasources[1].getRef());
|
||||
});
|
||||
|
||||
it('should not import queries when datasource is not changed', async () => {
|
||||
const { dispatch, getState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
datasourceInstance: datasources[0],
|
||||
queries: [{ refId: 'A', datasource: datasources[0].getRef() }],
|
||||
panes: {
|
||||
left: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
datasourceInstance: datasources[0],
|
||||
queries: [{ refId: 'A', datasource: datasources[0].getRef() }],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
@ -354,9 +366,9 @@ describe('changeQueries', () => {
|
||||
})
|
||||
);
|
||||
|
||||
expect(getState().explore[ExploreId.left].queries[0]).toHaveProperty('refId', 'A');
|
||||
expect(getState().explore[ExploreId.left].queries[0]).toHaveProperty('datasource', datasources[0].getRef());
|
||||
expect(getState().explore[ExploreId.left].queries[0]).toEqual({
|
||||
expect(getState().explore.panes.left!.queries[0]).toHaveProperty('refId', 'A');
|
||||
expect(getState().explore.panes.left!.queries[0]).toHaveProperty('datasource', datasources[0].getRef());
|
||||
expect(getState().explore.panes.left!.queries[0]).toEqual({
|
||||
refId: 'A',
|
||||
datasource: datasources[0].getRef(),
|
||||
queryType: 'someValue',
|
||||
@ -371,9 +383,11 @@ describe('importing queries', () => {
|
||||
const { dispatch, getState }: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
datasourceInstance: datasources[0],
|
||||
panes: {
|
||||
left: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
datasourceInstance: datasources[0],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
@ -390,10 +404,10 @@ describe('importing queries', () => {
|
||||
)
|
||||
);
|
||||
|
||||
expect(getState().explore[ExploreId.left].queries[0]).toHaveProperty('refId', 'refId_A');
|
||||
expect(getState().explore[ExploreId.left].queries[1]).toHaveProperty('refId', 'refId_B');
|
||||
expect(getState().explore[ExploreId.left].queries[0]).toHaveProperty('datasource.uid', 'ds2');
|
||||
expect(getState().explore[ExploreId.left].queries[1]).toHaveProperty('datasource.uid', 'ds2');
|
||||
expect(getState().explore.panes.left!.queries[0]).toHaveProperty('refId', 'refId_A');
|
||||
expect(getState().explore.panes.left!.queries[1]).toHaveProperty('refId', 'refId_B');
|
||||
expect(getState().explore.panes.left!.queries[0]).toHaveProperty('datasource.uid', 'ds2');
|
||||
expect(getState().explore.panes.left!.queries[1]).toHaveProperty('datasource.uid', 'ds2');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -415,10 +429,10 @@ describe('adding new query rows', () => {
|
||||
|
||||
const getState = await setupStore(queries, datasourceInstance);
|
||||
|
||||
expect(getState().explore[exploreId].datasourceInstance?.meta?.id).toBe('mixed');
|
||||
expect(getState().explore[exploreId].datasourceInstance?.meta?.mixed).toBe(true);
|
||||
expect(getState().explore[exploreId].queries).toHaveLength(1);
|
||||
expect(getState().explore[exploreId].queryKeys).toEqual(['uid-loki-0']);
|
||||
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('mixed');
|
||||
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(true);
|
||||
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(1);
|
||||
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['uid-loki-0']);
|
||||
});
|
||||
it('should add query row when there is not yet a row and meta.mixed === false', async () => {
|
||||
const queries: DataQuery[] = [];
|
||||
@ -435,10 +449,10 @@ describe('adding new query rows', () => {
|
||||
|
||||
const getState = await setupStore(queries, datasourceInstance);
|
||||
|
||||
expect(getState().explore[exploreId].datasourceInstance?.meta?.id).toBe('loki');
|
||||
expect(getState().explore[exploreId].datasourceInstance?.meta?.mixed).toBe(false);
|
||||
expect(getState().explore[exploreId].queries).toHaveLength(1);
|
||||
expect(getState().explore[exploreId].queryKeys).toEqual(['uid-loki-0']);
|
||||
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('loki');
|
||||
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(false);
|
||||
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(1);
|
||||
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['uid-loki-0']);
|
||||
});
|
||||
|
||||
it('should add another query row if there are two rows already', async () => {
|
||||
@ -462,10 +476,10 @@ describe('adding new query rows', () => {
|
||||
};
|
||||
const getState = await setupStore(queries, datasourceInstance);
|
||||
|
||||
expect(getState().explore[exploreId].datasourceInstance?.meta?.id).toBe('loki');
|
||||
expect(getState().explore[exploreId].datasourceInstance?.meta?.mixed).toBe(false);
|
||||
expect(getState().explore[exploreId].queries).toHaveLength(3);
|
||||
expect(getState().explore[exploreId].queryKeys).toEqual(['ds3-0', 'ds4-1', 'ds4-2']);
|
||||
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('loki');
|
||||
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(false);
|
||||
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(3);
|
||||
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['ds3-0', 'ds4-1', 'ds4-2']);
|
||||
});
|
||||
});
|
||||
describe('with mixed datasources enabled', () => {
|
||||
@ -490,11 +504,11 @@ describe('adding new query rows', () => {
|
||||
|
||||
const getState = await setupStore(queries, datasourceInstance);
|
||||
|
||||
expect(getState().explore[exploreId].datasourceInstance?.meta?.id).toBe('mixed');
|
||||
expect(getState().explore[exploreId].datasourceInstance?.meta?.mixed).toBe(true);
|
||||
expect(getState().explore[exploreId].queries).toHaveLength(1);
|
||||
expect(getState().explore[exploreId].queries[0]?.datasource?.type).toBe('postgres');
|
||||
expect(getState().explore[exploreId].queryKeys).toEqual(['ds1-0']);
|
||||
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('mixed');
|
||||
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(true);
|
||||
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(1);
|
||||
expect(getState().explore.panes[exploreId]!.queries[0]?.datasource?.type).toBe('postgres');
|
||||
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['ds1-0']);
|
||||
});
|
||||
|
||||
it('should add query row whith root ds (with overriding the default ds) when there is not yet a row', async () => {
|
||||
@ -512,11 +526,11 @@ describe('adding new query rows', () => {
|
||||
|
||||
const getState = await setupStore(queries, datasourceInstance);
|
||||
|
||||
expect(getState().explore[exploreId].datasourceInstance?.meta?.id).toBe('loki');
|
||||
expect(getState().explore[exploreId].datasourceInstance?.meta?.mixed).toBe(false);
|
||||
expect(getState().explore[exploreId].queries).toHaveLength(1);
|
||||
expect(getState().explore[exploreId].queries[0]?.datasource?.type).toBe('loki');
|
||||
expect(getState().explore[exploreId].queryKeys).toEqual(['uid-loki-0']);
|
||||
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('loki');
|
||||
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(false);
|
||||
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(1);
|
||||
expect(getState().explore.panes[exploreId]!.queries[0]?.datasource?.type).toBe('loki');
|
||||
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['uid-loki-0']);
|
||||
});
|
||||
|
||||
it('should add another query row if there are two rows already (impossible in UI)', async () => {
|
||||
@ -542,11 +556,11 @@ describe('adding new query rows', () => {
|
||||
|
||||
const getState = await setupStore(queries, datasourceInstance);
|
||||
|
||||
expect(getState().explore[exploreId].datasourceInstance?.meta?.id).toBe('postgres');
|
||||
expect(getState().explore[exploreId].datasourceInstance?.meta?.mixed).toBe(false);
|
||||
expect(getState().explore[exploreId].queries).toHaveLength(3);
|
||||
expect(getState().explore[exploreId].queries[2]?.datasource?.type).toBe('loki');
|
||||
expect(getState().explore[exploreId].queryKeys).toEqual(['ds3-0', 'ds4-1', 'ds4-2']);
|
||||
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.id).toBe('postgres');
|
||||
expect(getState().explore.panes[exploreId]!.datasourceInstance?.meta?.mixed).toBe(false);
|
||||
expect(getState().explore.panes[exploreId]!.queries).toHaveLength(3);
|
||||
expect(getState().explore.panes[exploreId]!.queries[2]?.datasource?.type).toBe('loki');
|
||||
expect(getState().explore.panes[exploreId]!.queryKeys).toEqual(['ds3-0', 'ds4-1', 'ds4-2']);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -630,20 +644,22 @@ describe('reducer', () => {
|
||||
const { dispatch, getState }: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
queryResponse: {
|
||||
series: [{ name: 'test name' }],
|
||||
state: LoadingState.Done,
|
||||
panes: {
|
||||
left: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
queryResponse: {
|
||||
series: [{ name: 'test name' }],
|
||||
state: LoadingState.Done,
|
||||
},
|
||||
absoluteRange: { from: 1621348027000, to: 1621348050000 },
|
||||
},
|
||||
absoluteRange: { from: 1621348027000, to: 1621348050000 },
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
|
||||
await dispatch(addResultsToCache(ExploreId.left));
|
||||
|
||||
expect(getState().explore[ExploreId.left].cache).toEqual([
|
||||
expect(getState().explore.panes.left!.cache).toEqual([
|
||||
{ key: 'from=1621348027000&to=1621348050000', value: { series: [{ name: 'test name' }], state: 'Done' } },
|
||||
]);
|
||||
});
|
||||
@ -652,44 +668,48 @@ describe('reducer', () => {
|
||||
const { dispatch, getState }: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
queryResponse: { series: [{ name: 'test name' }], state: LoadingState.Loading },
|
||||
absoluteRange: { from: 1621348027000, to: 1621348050000 },
|
||||
panes: {
|
||||
left: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
queryResponse: { series: [{ name: 'test name' }], state: LoadingState.Loading },
|
||||
absoluteRange: { from: 1621348027000, to: 1621348050000 },
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
|
||||
await dispatch(addResultsToCache(ExploreId.left));
|
||||
|
||||
expect(getState().explore[ExploreId.left].cache).toEqual([]);
|
||||
expect(getState().explore.panes.left!.cache).toEqual([]);
|
||||
});
|
||||
|
||||
it('should not add duplicate response to cache', async () => {
|
||||
const { dispatch, getState }: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
queryResponse: {
|
||||
series: [{ name: 'test name' }],
|
||||
state: LoadingState.Done,
|
||||
},
|
||||
absoluteRange: { from: 1621348027000, to: 1621348050000 },
|
||||
cache: [
|
||||
{
|
||||
key: 'from=1621348027000&to=1621348050000',
|
||||
value: { series: [{ name: 'old test name' }], state: LoadingState.Done },
|
||||
panes: {
|
||||
left: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
queryResponse: {
|
||||
series: [{ name: 'test name' }],
|
||||
state: LoadingState.Done,
|
||||
},
|
||||
],
|
||||
absoluteRange: { from: 1621348027000, to: 1621348050000 },
|
||||
cache: [
|
||||
{
|
||||
key: 'from=1621348027000&to=1621348050000',
|
||||
value: { series: [{ name: 'old test name' }], state: LoadingState.Done },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
|
||||
await dispatch(addResultsToCache(ExploreId.left));
|
||||
|
||||
expect(getState().explore[ExploreId.left].cache).toHaveLength(1);
|
||||
expect(getState().explore[ExploreId.left].cache).toEqual([
|
||||
expect(getState().explore.panes.left!.cache).toHaveLength(1);
|
||||
expect(getState().explore.panes.left!.cache).toEqual([
|
||||
{ key: 'from=1621348027000&to=1621348050000', value: { series: [{ name: 'old test name' }], state: 'Done' } },
|
||||
]);
|
||||
});
|
||||
@ -698,21 +718,23 @@ describe('reducer', () => {
|
||||
const { dispatch, getState }: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
cache: [
|
||||
{
|
||||
key: 'from=1621348027000&to=1621348050000',
|
||||
value: { series: [{ name: 'old test name' }], state: 'Done' },
|
||||
},
|
||||
],
|
||||
panes: {
|
||||
left: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
cache: [
|
||||
{
|
||||
key: 'from=1621348027000&to=1621348050000',
|
||||
value: { series: [{ name: 'old test name' }], state: 'Done' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
|
||||
await dispatch(clearCache(ExploreId.left));
|
||||
|
||||
expect(getState().explore[ExploreId.left].cache).toEqual([]);
|
||||
expect(getState().explore.panes.left!.cache).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -739,22 +761,24 @@ describe('reducer', () => {
|
||||
const store: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
datasourceInstance: {
|
||||
query: jest.fn(),
|
||||
getRef: jest.fn(),
|
||||
meta: {
|
||||
id: 'something',
|
||||
panes: {
|
||||
left: {
|
||||
...defaultInitialState.explore.panes.left,
|
||||
datasourceInstance: {
|
||||
query: jest.fn(),
|
||||
getRef: jest.fn(),
|
||||
meta: {
|
||||
id: 'something',
|
||||
},
|
||||
getDataProvider: () => {
|
||||
return mockDataProvider();
|
||||
},
|
||||
getSupportedSupplementaryQueryTypes: () => [
|
||||
SupplementaryQueryType.LogsVolume,
|
||||
SupplementaryQueryType.LogsSample,
|
||||
],
|
||||
getSupplementaryQuery: jest.fn(),
|
||||
},
|
||||
getDataProvider: () => {
|
||||
return mockDataProvider();
|
||||
},
|
||||
getSupportedSupplementaryQueryTypes: () => [
|
||||
SupplementaryQueryType.LogsVolume,
|
||||
SupplementaryQueryType.LogsSample,
|
||||
],
|
||||
getSupplementaryQuery: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -797,8 +821,8 @@ describe('reducer', () => {
|
||||
expect(unsubscribes[1]).toBeCalled();
|
||||
|
||||
for (const type of supplementaryQueryTypes) {
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[type].data).toBeUndefined();
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[type].dataProvider).toBeUndefined();
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[type].data).toBeUndefined();
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[type].dataProvider).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
@ -814,20 +838,20 @@ describe('reducer', () => {
|
||||
dispatch(runQueries(ExploreId.left));
|
||||
|
||||
for (const type of supplementaryQueryTypes) {
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[type].data).toBeDefined();
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[type].data!.state).toBe(LoadingState.Loading);
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[type].dataProvider).toBeDefined();
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[type].data).toBeDefined();
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[type].data!.state).toBe(LoadingState.Loading);
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[type].dataProvider).toBeDefined();
|
||||
}
|
||||
for (const type of supplementaryQueryTypes) {
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[type].data).toBeDefined();
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[type].data!.state).toBe(LoadingState.Loading);
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[type].dataProvider).toBeDefined();
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[type].data).toBeDefined();
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[type].data!.state).toBe(LoadingState.Loading);
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[type].dataProvider).toBeDefined();
|
||||
}
|
||||
|
||||
dispatch(cancelQueries(ExploreId.left));
|
||||
for (const type of supplementaryQueryTypes) {
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[type].data).toBeUndefined();
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[type].data).toBeUndefined();
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[type].data).toBeUndefined();
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[type].data).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
@ -841,17 +865,17 @@ describe('reducer', () => {
|
||||
dispatch(runQueries(ExploreId.left));
|
||||
|
||||
for (const types of supplementaryQueryTypes) {
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[types].data).toBeDefined();
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[types].data!.state).toBe(LoadingState.Done);
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[types].dataProvider).toBeDefined();
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[types].data).toBeDefined();
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[types].data!.state).toBe(LoadingState.Done);
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[types].dataProvider).toBeDefined();
|
||||
}
|
||||
|
||||
dispatch(cancelQueries(ExploreId.left));
|
||||
|
||||
for (const types of supplementaryQueryTypes) {
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[types].data).toBeDefined();
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[types].data!.state).toBe(LoadingState.Done);
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[types].dataProvider).toBeUndefined();
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[types].data).toBeDefined();
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[types].data!.state).toBe(LoadingState.Done);
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[types].dataProvider).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
@ -861,34 +885,30 @@ describe('reducer', () => {
|
||||
};
|
||||
// turn logs volume off (but keep logs sample on)
|
||||
dispatch(setSupplementaryQueryEnabled(ExploreId.left, false, SupplementaryQueryType.LogsVolume));
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].enabled).toBe(
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsVolume].enabled).toBe(
|
||||
false
|
||||
);
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsSample].enabled).toBe(
|
||||
true
|
||||
);
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsSample].enabled).toBe(true);
|
||||
|
||||
// verify that if we run a query, it will: 1) not do logs volume, 2) do logs sample 3) provider will still be set for both
|
||||
dispatch(runQueries(ExploreId.left));
|
||||
|
||||
expect(
|
||||
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].data
|
||||
getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsVolume].data
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataSubscription
|
||||
getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsVolume].dataSubscription
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider
|
||||
getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider
|
||||
).toBeDefined();
|
||||
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsSample].data).toBeDefined();
|
||||
expect(
|
||||
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsSample].data
|
||||
getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsSample].dataSubscription
|
||||
).toBeDefined();
|
||||
expect(
|
||||
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsSample].dataSubscription
|
||||
).toBeDefined();
|
||||
expect(
|
||||
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsSample].dataProvider
|
||||
getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsSample].dataProvider
|
||||
).toBeDefined();
|
||||
});
|
||||
|
||||
@ -900,30 +920,28 @@ describe('reducer', () => {
|
||||
// runQueries sets up providers, but does not run queries
|
||||
dispatch(runQueries(ExploreId.left));
|
||||
expect(
|
||||
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider
|
||||
getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsVolume].dataProvider
|
||||
).toBeDefined();
|
||||
expect(
|
||||
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsSample].dataProvider
|
||||
getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsSample].dataProvider
|
||||
).toBeDefined();
|
||||
|
||||
// we turn 1 supplementary query (logs volume) on
|
||||
dispatch(setSupplementaryQueryEnabled(ExploreId.left, true, SupplementaryQueryType.LogsVolume));
|
||||
|
||||
// verify it was turned on
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].enabled).toBe(
|
||||
true
|
||||
);
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsVolume].enabled).toBe(true);
|
||||
// verify that other stay off
|
||||
expect(getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsSample].enabled).toBe(
|
||||
expect(getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsSample].enabled).toBe(
|
||||
false
|
||||
);
|
||||
|
||||
expect(
|
||||
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsVolume].dataSubscription
|
||||
getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsVolume].dataSubscription
|
||||
).toBeDefined();
|
||||
|
||||
expect(
|
||||
getState().explore[ExploreId.left].supplementaryQueries[SupplementaryQueryType.LogsSample].dataSubscription
|
||||
getState().explore.panes.left!.supplementaryQueries[SupplementaryQueryType.LogsSample].dataSubscription
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@ -934,24 +952,26 @@ describe('reducer', () => {
|
||||
const { dispatch, getState }: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
queryResponse: {
|
||||
state: LoadingState.Streaming,
|
||||
},
|
||||
logsResult: {
|
||||
hasUniqueLabels: false,
|
||||
rows: logRows,
|
||||
panes: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore.panes[ExploreId.left],
|
||||
queryResponse: {
|
||||
state: LoadingState.Streaming,
|
||||
},
|
||||
logsResult: {
|
||||
hasUniqueLabels: false,
|
||||
rows: logRows,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
expect(getState().explore[ExploreId.left].logsResult?.rows.length).toBe(logRows.length);
|
||||
expect(getState().explore.panes[ExploreId.left]?.logsResult?.rows.length).toBe(logRows.length);
|
||||
|
||||
await dispatch(clearLogs({ exploreId: ExploreId.left }));
|
||||
|
||||
expect(getState().explore[ExploreId.left].logsResult?.rows.length).toBe(0);
|
||||
expect(getState().explore[ExploreId.left].clearedAtIndex).toBe(logRows.length - 1);
|
||||
expect(getState().explore.panes[ExploreId.left]?.logsResult?.rows.length).toBe(0);
|
||||
expect(getState().explore.panes[ExploreId.left]?.clearedAtIndex).toBe(logRows.length - 1);
|
||||
});
|
||||
|
||||
it('should filter new log rows', async () => {
|
||||
@ -962,21 +982,23 @@ describe('reducer', () => {
|
||||
const { dispatch, getState }: { dispatch: ThunkDispatch; getState: () => StoreState } = configureStore({
|
||||
...defaultInitialState,
|
||||
explore: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore[ExploreId.left],
|
||||
isLive: true,
|
||||
queryResponse: {
|
||||
state: LoadingState.Streaming,
|
||||
},
|
||||
logsResult: {
|
||||
hasUniqueLabels: false,
|
||||
rows: oldLogRows,
|
||||
panes: {
|
||||
[ExploreId.left]: {
|
||||
...defaultInitialState.explore.panes[ExploreId.left],
|
||||
isLive: true,
|
||||
queryResponse: {
|
||||
state: LoadingState.Streaming,
|
||||
},
|
||||
logsResult: {
|
||||
hasUniqueLabels: false,
|
||||
rows: oldLogRows,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as Partial<StoreState>);
|
||||
|
||||
expect(getState().explore[ExploreId.left].logsResult?.rows.length).toBe(oldLogRows.length);
|
||||
expect(getState().explore.panes[ExploreId.left]?.logsResult?.rows.length).toBe(oldLogRows.length);
|
||||
|
||||
await dispatch(clearLogs({ exploreId: ExploreId.left }));
|
||||
await dispatch(
|
||||
@ -996,8 +1018,8 @@ describe('reducer', () => {
|
||||
} as unknown as QueryEndedPayload)
|
||||
);
|
||||
|
||||
expect(getState().explore[ExploreId.left].logsResult?.rows.length).toBe(newLogRows.length);
|
||||
expect(getState().explore[ExploreId.left].clearedAtIndex).toBe(oldLogRows.length - 1);
|
||||
expect(getState().explore.panes[ExploreId.left]?.logsResult?.rows.length).toBe(newLogRows.length);
|
||||
expect(getState().explore.panes[ExploreId.left]?.clearedAtIndex).toBe(oldLogRows.length - 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -241,13 +241,13 @@ export const clearCacheAction = createAction<ClearCachePayload>('explore/clearCa
|
||||
*/
|
||||
export function addQueryRow(exploreId: ExploreId, index: number): ThunkResult<void> {
|
||||
return async (dispatch, getState) => {
|
||||
const queries = getState().explore[exploreId]!.queries;
|
||||
const queries = getState().explore.panes[exploreId]!.queries;
|
||||
let datasourceOverride = undefined;
|
||||
|
||||
// if this is the first query being added, check for a root datasource
|
||||
// if it's not mixed, send it as an override. generateEmptyQuery doesn't have access to state
|
||||
if (queries.length === 0) {
|
||||
const rootDatasource = getState().explore[exploreId]!.datasourceInstance;
|
||||
const rootDatasource = getState().explore.panes[exploreId]!.datasourceInstance;
|
||||
if (!config.featureToggles.exploreMixedDatasource || !rootDatasource?.meta.mixed) {
|
||||
datasourceOverride = rootDatasource;
|
||||
}
|
||||
@ -267,7 +267,7 @@ export function cancelQueries(exploreId: ExploreId): ThunkResult<void> {
|
||||
dispatch(scanStopAction({ exploreId }));
|
||||
dispatch(cancelQueriesAction({ exploreId }));
|
||||
|
||||
const supplementaryQueries = getState().explore[exploreId]!.supplementaryQueries;
|
||||
const supplementaryQueries = getState().explore.panes[exploreId]!.supplementaryQueries;
|
||||
// Cancel all data providers
|
||||
for (const type of supplementaryQueryTypes) {
|
||||
dispatch(cleanSupplementaryQueryDataProviderAction({ exploreId, type }));
|
||||
@ -311,7 +311,7 @@ export const changeQueries = createAsyncThunk<void, ChangeQueriesPayload>(
|
||||
'explore/changeQueries',
|
||||
async ({ queries, exploreId }, { getState, dispatch }) => {
|
||||
let queriesImported = false;
|
||||
const oldQueries = getState().explore[exploreId]!.queries;
|
||||
const oldQueries = getState().explore.panes[exploreId]!.queries;
|
||||
|
||||
for (const newQuery of queries) {
|
||||
for (const oldQuery of oldQueries) {
|
||||
@ -423,7 +423,7 @@ export function modifyQueries(
|
||||
modifier: (query: DataQuery, modification: QueryFixAction) => Promise<DataQuery>
|
||||
): ThunkResult<void> {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState().explore[exploreId]!;
|
||||
const state = getState().explore.panes[exploreId]!;
|
||||
|
||||
const { queries } = state;
|
||||
|
||||
@ -457,8 +457,9 @@ async function handleHistory(
|
||||
// Because filtering happens in the backend we cannot add a new entry without checking if it matches currently
|
||||
// used filters. Instead, we refresh the query history list.
|
||||
// TODO: run only if Query History list is opened (#47252)
|
||||
await dispatch(loadRichHistory(ExploreId.left));
|
||||
await dispatch(loadRichHistory(ExploreId.right));
|
||||
for (const exploreId in state.panes) {
|
||||
await dispatch(loadRichHistory(exploreId as ExploreId));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -479,7 +480,7 @@ export const runQueries = (
|
||||
dispatch(clearCache(exploreId));
|
||||
}
|
||||
|
||||
const exploreItemState = getState().explore[exploreId]!;
|
||||
const exploreItemState = getState().explore.panes[exploreId]!;
|
||||
const {
|
||||
datasourceInstance,
|
||||
containerWidth,
|
||||
@ -582,9 +583,9 @@ export const runQueries = (
|
||||
dispatch(queryStreamUpdatedAction({ exploreId, response: data }));
|
||||
|
||||
// Keep scanning for results if this was the last scanning transaction
|
||||
if (getState().explore[exploreId]!.scanning) {
|
||||
if (getState().explore.panes[exploreId]!.scanning) {
|
||||
if (data.state === LoadingState.Done && data.series.length === 0) {
|
||||
const range = getShiftedTimeRange(-1, getState().explore[exploreId]!.range);
|
||||
const range = getShiftedTimeRange(-1, getState().explore.panes[exploreId]!.range);
|
||||
dispatch(updateTime({ exploreId, absoluteRange: range }));
|
||||
dispatch(runQueries(exploreId));
|
||||
} else {
|
||||
@ -602,7 +603,7 @@ export const runQueries = (
|
||||
// In case we don't get any response at all but the observable completed, make sure we stop loading state.
|
||||
// This is for cases when some queries are noop like running first query after load but we don't have any
|
||||
// actual query input.
|
||||
if (getState().explore[exploreId]!.queryResponse.state === LoadingState.Loading) {
|
||||
if (getState().explore.panes[exploreId]!.queryResponse.state === LoadingState.Loading) {
|
||||
dispatch(changeLoadingStateAction({ exploreId, loadingState: LoadingState.Done }));
|
||||
}
|
||||
},
|
||||
@ -777,7 +778,7 @@ function canReuseSupplementaryQueryData(
|
||||
export function setQueries(exploreId: ExploreId, rawQueries: DataQuery[]): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
// Inject react keys into query objects
|
||||
const queries = getState().explore[exploreId]!.queries;
|
||||
const queries = getState().explore.panes[exploreId]!.queries;
|
||||
const nextQueries = rawQueries.map((query, index) => generateNewKeyAndAddRefIdIfMissing(query, queries, index));
|
||||
dispatch(setQueriesAction({ exploreId, queries: nextQueries }));
|
||||
dispatch(runQueries(exploreId));
|
||||
@ -794,7 +795,7 @@ export function scanStart(exploreId: ExploreId): ThunkResult<void> {
|
||||
// Register the scanner
|
||||
dispatch(scanStartAction({ exploreId }));
|
||||
// Scanning must trigger query run, and return the new range
|
||||
const range = getShiftedTimeRange(-1, getState().explore[exploreId]!.range);
|
||||
const range = getShiftedTimeRange(-1, getState().explore.panes[exploreId]!.range);
|
||||
// Set the new range to be displayed
|
||||
dispatch(updateTime({ exploreId, absoluteRange: range }));
|
||||
dispatch(runQueries(exploreId));
|
||||
@ -803,8 +804,8 @@ export function scanStart(exploreId: ExploreId): ThunkResult<void> {
|
||||
|
||||
export function addResultsToCache(exploreId: ExploreId): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
const queryResponse = getState().explore[exploreId]!.queryResponse;
|
||||
const absoluteRange = getState().explore[exploreId]!.absoluteRange;
|
||||
const queryResponse = getState().explore.panes[exploreId]!.queryResponse;
|
||||
const absoluteRange = getState().explore.panes[exploreId]!.absoluteRange;
|
||||
const cacheKey = createCacheKey(absoluteRange);
|
||||
|
||||
// Save results to cache only when all results received and loading is done
|
||||
@ -825,7 +826,7 @@ export function clearCache(exploreId: ExploreId): ThunkResult<void> {
|
||||
*/
|
||||
export function loadSupplementaryQueryData(exploreId: ExploreId, type: SupplementaryQueryType): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
const { supplementaryQueries } = getState().explore[exploreId]!;
|
||||
const { supplementaryQueries } = getState().explore.panes[exploreId]!;
|
||||
const dataProvider = supplementaryQueries[type].dataProvider;
|
||||
|
||||
if (dataProvider) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ExploreId, StoreState } from 'app/types';
|
||||
|
||||
export const isSplit = (state: StoreState) => Boolean(state.explore[ExploreId.left] && state.explore[ExploreId.right]);
|
||||
export const isSplit = (state: StoreState) => Object.keys(state.explore.panes).length > 1;
|
||||
|
||||
export const getExploreItemSelector = (exploreId: ExploreId) => (state: StoreState) => state.explore[exploreId];
|
||||
export const getExploreItemSelector = (exploreId: ExploreId) => (state: StoreState) => state.explore.panes[exploreId];
|
||||
|
@ -2,7 +2,7 @@ import { reducerTester } from 'test/core/redux/reducerTester';
|
||||
|
||||
import { dateTime, LoadingState } from '@grafana/data';
|
||||
import { configureStore } from 'app/store/configureStore';
|
||||
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||
import { ExploreId, ExploreItemState } from 'app/types';
|
||||
|
||||
import { silenceConsoleOutput } from '../../../../test/core/utils/silenceConsoleOutput';
|
||||
|
||||
|
@ -43,12 +43,10 @@ export const updateTimeRange = (options: {
|
||||
return (dispatch, getState) => {
|
||||
const { syncedTimes } = getState().explore;
|
||||
if (syncedTimes) {
|
||||
dispatch(updateTime({ ...options, exploreId: ExploreId.left }));
|
||||
// When running query by updating time range, we want to preserve cache.
|
||||
// Cached results are currently used in Logs pagination.
|
||||
dispatch(runQueries(ExploreId.left, { preserveCache: true }));
|
||||
dispatch(updateTime({ ...options, exploreId: ExploreId.right }));
|
||||
dispatch(runQueries(ExploreId.right, { preserveCache: true }));
|
||||
Object.keys(getState().explore.panes).forEach((exploreId) => {
|
||||
dispatch(updateTime({ ...options, exploreId: exploreId as ExploreId }));
|
||||
dispatch(runQueries(exploreId as ExploreId, { preserveCache: true }));
|
||||
});
|
||||
} else {
|
||||
dispatch(updateTime({ ...options }));
|
||||
dispatch(runQueries(options.exploreId, { preserveCache: true }));
|
||||
@ -73,7 +71,7 @@ export const updateTime = (config: {
|
||||
}): ThunkResult<void> => {
|
||||
return (dispatch, getState) => {
|
||||
const { exploreId, absoluteRange: absRange, rawRange: actionRange } = config;
|
||||
const itemState = getState().explore[exploreId]!;
|
||||
const itemState = getState().explore.panes[exploreId]!;
|
||||
const timeZone = getTimeZone(getState().user);
|
||||
const fiscalYearStartMonth = getFiscalYearStartMonth(getState().user);
|
||||
const { range: rangeInState } = itemState;
|
||||
@ -118,13 +116,14 @@ export const updateTime = (config: {
|
||||
*/
|
||||
export function syncTimes(exploreId: ExploreId): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
if (exploreId === ExploreId.left) {
|
||||
const leftState = getState().explore.left;
|
||||
dispatch(updateTimeRange({ exploreId: ExploreId.right, rawRange: leftState.range.raw }));
|
||||
} else {
|
||||
const rightState = getState().explore.right!;
|
||||
dispatch(updateTimeRange({ exploreId: ExploreId.left, rawRange: rightState.range.raw }));
|
||||
}
|
||||
const range = getState().explore.panes[exploreId]!.range.raw;
|
||||
|
||||
Object.keys(getState().explore.panes)
|
||||
.filter((key) => key !== exploreId)
|
||||
.forEach((exploreId) => {
|
||||
dispatch(updateTimeRange({ exploreId: exploreId as ExploreId, rawRange: range }));
|
||||
});
|
||||
|
||||
const isTimeSynced = getState().explore.syncedTimes;
|
||||
dispatch(syncTimesAction({ syncedTimes: !isTimeSynced }));
|
||||
dispatch(stateSave());
|
||||
@ -140,16 +139,13 @@ export function makeAbsoluteTime(): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
const timeZone = getTimeZone(getState().user);
|
||||
const fiscalYearStartMonth = getFiscalYearStartMonth(getState().user);
|
||||
const leftState = getState().explore.left;
|
||||
const leftRange = getTimeRange(timeZone, leftState.range.raw, fiscalYearStartMonth);
|
||||
const leftAbsoluteRange: AbsoluteTimeRange = { from: leftRange.from.valueOf(), to: leftRange.to.valueOf() };
|
||||
dispatch(updateTime({ exploreId: ExploreId.left, absoluteRange: leftAbsoluteRange }));
|
||||
const rightState = getState().explore.right!;
|
||||
if (rightState) {
|
||||
const rightRange = getTimeRange(timeZone, rightState.range.raw, fiscalYearStartMonth);
|
||||
const rightAbsoluteRange: AbsoluteTimeRange = { from: rightRange.from.valueOf(), to: rightRange.to.valueOf() };
|
||||
dispatch(updateTime({ exploreId: ExploreId.right, absoluteRange: rightAbsoluteRange }));
|
||||
}
|
||||
|
||||
Object.entries(getState().explore.panes).forEach(([exploreId, exploreItemState]) => {
|
||||
const range = getTimeRange(timeZone, exploreItemState!.range.raw, fiscalYearStartMonth);
|
||||
const absoluteRange: AbsoluteTimeRange = { from: range.from.valueOf(), to: range.to.valueOf() };
|
||||
dispatch(updateTime({ exploreId: exploreId as ExploreId, absoluteRange }));
|
||||
});
|
||||
|
||||
dispatch(stateSave());
|
||||
};
|
||||
}
|
||||
|
@ -38,14 +38,14 @@ export interface ExploreState {
|
||||
* True if time interval for panels are synced. Only possible with split mode.
|
||||
*/
|
||||
syncedTimes: boolean;
|
||||
/**
|
||||
* Explore state of the left split (left is default in non-split view).
|
||||
*/
|
||||
left: ExploreItemState;
|
||||
/**
|
||||
* Explore state of the right area in split view.
|
||||
*/
|
||||
right?: ExploreItemState;
|
||||
|
||||
// This being optional wouldn't be needed with noUncheckedIndexedAccess set to true, but it cause more than 5k errors currently.
|
||||
// In order to be safe, we declare each item as pssobly undefined to force existence checks.
|
||||
// This will have the side effect of also forcing undefined checks when iterating over this object entries, but
|
||||
// it's better to error on the safer side.
|
||||
panes: {
|
||||
[paneId in ExploreId]?: ExploreItemState;
|
||||
};
|
||||
|
||||
correlations?: CorrelationData[];
|
||||
|
||||
@ -68,12 +68,12 @@ export interface ExploreState {
|
||||
/**
|
||||
* On a split manual resize, we calculate which pane is larger, or if they are roughly the same size. If undefined, it is not split or they are roughly the same size
|
||||
*/
|
||||
largerExploreId?: ExploreId;
|
||||
largerExploreId?: keyof ExploreState['panes'];
|
||||
|
||||
/**
|
||||
* If a maximize pane button is pressed, this indicates which side was maximized. Will be undefined if not split or if it is manually resized
|
||||
*/
|
||||
maxedExploreId?: ExploreId;
|
||||
maxedExploreId?: keyof ExploreState['panes'];
|
||||
|
||||
/**
|
||||
* If a minimize pane button is pressed, it will do an even split of panes. Will be undefined if split or on a manual resize
|
||||
|
Loading…
Reference in New Issue
Block a user