mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
explore: fix issues when loading and both graph/table are collapsed (#17113)
Removes the functionality of being able to collapse/expand the logs container. When both graph and table are collapsed and you reload the page then the start page should not be displayed. When both graph and table are collapsed and you reload the page then the graph and table panels should be displayed. Fix so that reducer tests are run. On of the test used fit() instead of it() which had the consequence of only 1 reducer test was executed and the rest skipped. There was some failing tests that now is updated and now passes. Fixes #17098
This commit is contained in:
parent
32cdab4cbf
commit
1a80885180
@ -46,22 +46,20 @@ export class GraphContainer extends PureComponent<GraphContainerProps> {
|
||||
const graphHeight = showingGraph && showingTable ? 200 : 400;
|
||||
const timeRange = { from: range.from.valueOf(), to: range.to.valueOf() };
|
||||
|
||||
if (!graphResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Panel label="Graph" isOpen={showingGraph} loading={loading} onToggle={this.onClickGraphButton}>
|
||||
<Graph
|
||||
data={graphResult}
|
||||
height={graphHeight}
|
||||
id={`explore-graph-${exploreId}`}
|
||||
onChangeTime={this.onChangeTime}
|
||||
range={timeRange}
|
||||
timeZone={timeZone}
|
||||
split={split}
|
||||
width={width}
|
||||
/>
|
||||
<Panel label="Graph" collapsible isOpen={showingGraph} loading={loading} onToggle={this.onClickGraphButton}>
|
||||
{graphResult && (
|
||||
<Graph
|
||||
data={graphResult}
|
||||
height={graphHeight}
|
||||
id={`explore-graph-${exploreId}`}
|
||||
onChangeTime={this.onChangeTime}
|
||||
range={timeRange}
|
||||
timeZone={timeZone}
|
||||
split={split}
|
||||
width={width}
|
||||
/>
|
||||
)}
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||
import { LogsModel, LogsDedupStrategy } from 'app/core/logs_model';
|
||||
import { StoreState } from 'app/types';
|
||||
|
||||
import { toggleLogs, changeDedupStrategy, changeTime } from './state/actions';
|
||||
import { changeDedupStrategy, changeTime } from './state/actions';
|
||||
import Logs from './Logs';
|
||||
import Panel from './Panel';
|
||||
import { toggleLogLevelAction } from 'app/features/explore/state/actionTypes';
|
||||
@ -27,8 +27,6 @@ interface LogsContainerProps {
|
||||
timeZone: TimeZone;
|
||||
scanning?: boolean;
|
||||
scanRange?: RawTimeRange;
|
||||
showingLogs: boolean;
|
||||
toggleLogs: typeof toggleLogs;
|
||||
toggleLogLevelAction: typeof toggleLogLevelAction;
|
||||
changeDedupStrategy: typeof changeDedupStrategy;
|
||||
dedupStrategy: LogsDedupStrategy;
|
||||
@ -48,10 +46,6 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
changeTime(exploreId, range);
|
||||
};
|
||||
|
||||
onClickLogsButton = () => {
|
||||
this.props.toggleLogs(this.props.exploreId, this.props.showingLogs);
|
||||
};
|
||||
|
||||
handleDedupStrategyChange = (dedupStrategy: LogsDedupStrategy) => {
|
||||
this.props.changeDedupStrategy(this.props.exploreId, dedupStrategy);
|
||||
};
|
||||
@ -76,7 +70,6 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
onStopScanning,
|
||||
range,
|
||||
timeZone,
|
||||
showingLogs,
|
||||
scanning,
|
||||
scanRange,
|
||||
width,
|
||||
@ -84,7 +77,7 @@ export class LogsContainer extends PureComponent<LogsContainerProps> {
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Panel label="Logs" loading={loading} isOpen={showingLogs} onToggle={this.onClickLogsButton}>
|
||||
<Panel label="Logs" loading={loading} isOpen>
|
||||
<Logs
|
||||
dedupStrategy={this.props.dedupStrategy || LogsDedupStrategy.none}
|
||||
data={logsResult}
|
||||
@ -115,7 +108,7 @@ function mapStateToProps(state: StoreState, { exploreId }) {
|
||||
const item: ExploreItemState = explore[exploreId];
|
||||
const { logsHighlighterExpressions, logsResult, logIsLoading, scanning, scanRange, range } = item;
|
||||
const loading = logIsLoading;
|
||||
const { showingLogs, dedupStrategy } = exploreItemUIStateSelector(item);
|
||||
const { dedupStrategy } = exploreItemUIStateSelector(item);
|
||||
const hiddenLogLevels = new Set(item.hiddenLogLevels);
|
||||
const dedupedResult = deduplicatedLogsSelector(item);
|
||||
const timeZone = getTimeZone(state.user);
|
||||
@ -126,7 +119,6 @@ function mapStateToProps(state: StoreState, { exploreId }) {
|
||||
logsResult,
|
||||
scanning,
|
||||
scanRange,
|
||||
showingLogs,
|
||||
range,
|
||||
timeZone,
|
||||
dedupStrategy,
|
||||
@ -136,7 +128,6 @@ function mapStateToProps(state: StoreState, { exploreId }) {
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
toggleLogs,
|
||||
changeDedupStrategy,
|
||||
toggleLogLevelAction,
|
||||
changeTime,
|
||||
|
@ -4,18 +4,27 @@ interface Props {
|
||||
isOpen: boolean;
|
||||
label: string;
|
||||
loading?: boolean;
|
||||
onToggle: (isOpen: boolean) => void;
|
||||
collapsible?: boolean;
|
||||
onToggle?: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
export default class Panel extends PureComponent<Props> {
|
||||
onClickToggle = () => this.props.onToggle(!this.props.isOpen);
|
||||
onClickToggle = () => {
|
||||
const { onToggle, isOpen } = this.props;
|
||||
if (onToggle) {
|
||||
onToggle(!isOpen);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isOpen, loading } = this.props;
|
||||
const { isOpen, loading, collapsible } = this.props;
|
||||
const panelClass = collapsible
|
||||
? 'explore-panel explore-panel--collapsible panel-container'
|
||||
: 'explore-panel panel-container';
|
||||
const iconClass = isOpen ? 'fa fa-caret-up' : 'fa fa-caret-down';
|
||||
const loaderClass = loading ? 'explore-panel__loader explore-panel__loader--active' : 'explore-panel__loader';
|
||||
return (
|
||||
<div className="explore-panel panel-container">
|
||||
<div className={panelClass}>
|
||||
<div className="explore-panel__header" onClick={this.onClickToggle}>
|
||||
<div className="explore-panel__header-buttons">
|
||||
<span className={iconClass} />
|
||||
|
@ -27,13 +27,9 @@ export class TableContainer extends PureComponent<TableContainerProps> {
|
||||
render() {
|
||||
const { loading, onClickCell, showingTable, tableResult } = this.props;
|
||||
|
||||
if (!tableResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Panel label="Table" loading={loading} isOpen={showingTable} onToggle={this.onClickTableButton}>
|
||||
<Table data={tableResult} loading={loading} onClickCell={onClickCell} />
|
||||
<Panel label="Table" loading={loading} collapsible isOpen={showingTable} onToggle={this.onClickTableButton}>
|
||||
{tableResult && <Table data={tableResult} loading={loading} onClickCell={onClickCell} />}
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
@ -204,10 +204,6 @@ export interface ToggleGraphPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface ToggleLogsPayload {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface UpdateUIStatePayload extends Partial<ExploreUIState> {
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
@ -412,11 +408,6 @@ export const toggleTableAction = actionCreatorFactory<ToggleTablePayload>('explo
|
||||
*/
|
||||
export const toggleGraphAction = actionCreatorFactory<ToggleGraphPayload>('explore/TOGGLE_GRAPH').create();
|
||||
|
||||
/**
|
||||
* Expand/collapse the logs result viewer. When collapsed, log queries won't be run.
|
||||
*/
|
||||
export const toggleLogsAction = actionCreatorFactory<ToggleLogsPayload>('explore/TOGGLE_LOGS').create();
|
||||
|
||||
/**
|
||||
* Updates datasource instance before datasouce loading has started
|
||||
*/
|
||||
|
@ -72,10 +72,8 @@ import {
|
||||
splitOpenAction,
|
||||
addQueryRowAction,
|
||||
toggleGraphAction,
|
||||
toggleLogsAction,
|
||||
toggleTableAction,
|
||||
ToggleGraphPayload,
|
||||
ToggleLogsPayload,
|
||||
ToggleTablePayload,
|
||||
updateUIStateAction,
|
||||
runQueriesAction,
|
||||
@ -517,7 +515,6 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false, replaceU
|
||||
const {
|
||||
datasourceInstance,
|
||||
queries,
|
||||
showingLogs,
|
||||
showingGraph,
|
||||
showingTable,
|
||||
datasourceError,
|
||||
@ -562,7 +559,7 @@ export function runQueries(exploreId: ExploreId, ignoreUIState = false, replaceU
|
||||
})
|
||||
);
|
||||
}
|
||||
if ((ignoreUIState || showingLogs) && mode === ExploreMode.Logs) {
|
||||
if (mode === ExploreMode.Logs) {
|
||||
dispatch(runQueriesForType(exploreId, 'Logs', { interval, format: 'logs' }));
|
||||
}
|
||||
|
||||
@ -700,7 +697,7 @@ export function stateSave(replaceUrl = false): ThunkResult<void> {
|
||||
range: toRawTimeRange(left.range),
|
||||
ui: {
|
||||
showingGraph: left.showingGraph,
|
||||
showingLogs: left.showingLogs,
|
||||
showingLogs: true,
|
||||
showingTable: left.showingTable,
|
||||
dedupStrategy: left.dedupStrategy,
|
||||
},
|
||||
@ -713,7 +710,7 @@ export function stateSave(replaceUrl = false): ThunkResult<void> {
|
||||
range: toRawTimeRange(right.range),
|
||||
ui: {
|
||||
showingGraph: right.showingGraph,
|
||||
showingLogs: right.showingLogs,
|
||||
showingLogs: true,
|
||||
showingTable: right.showingTable,
|
||||
dedupStrategy: right.dedupStrategy,
|
||||
},
|
||||
@ -731,10 +728,7 @@ export function stateSave(replaceUrl = false): ThunkResult<void> {
|
||||
* queries won't be run
|
||||
*/
|
||||
const togglePanelActionCreator = (
|
||||
actionCreator:
|
||||
| ActionCreator<ToggleGraphPayload>
|
||||
| ActionCreator<ToggleLogsPayload>
|
||||
| ActionCreator<ToggleTablePayload>
|
||||
actionCreator: ActionCreator<ToggleGraphPayload> | ActionCreator<ToggleTablePayload>
|
||||
) => (exploreId: ExploreId, isPanelVisible: boolean): ThunkResult<void> => {
|
||||
return dispatch => {
|
||||
let uiFragmentStateUpdate: Partial<ExploreUIState>;
|
||||
@ -744,9 +738,6 @@ const togglePanelActionCreator = (
|
||||
case toggleGraphAction.type:
|
||||
uiFragmentStateUpdate = { showingGraph: !isPanelVisible };
|
||||
break;
|
||||
case toggleLogsAction.type:
|
||||
uiFragmentStateUpdate = { showingLogs: !isPanelVisible };
|
||||
break;
|
||||
case toggleTableAction.type:
|
||||
uiFragmentStateUpdate = { showingTable: !isPanelVisible };
|
||||
break;
|
||||
@ -766,11 +757,6 @@ const togglePanelActionCreator = (
|
||||
*/
|
||||
export const toggleGraph = togglePanelActionCreator(toggleGraphAction);
|
||||
|
||||
/**
|
||||
* Expand/collapse the logs result viewer. When collapsed, log queries won't be run.
|
||||
*/
|
||||
export const toggleLogs = togglePanelActionCreator(toggleLogsAction);
|
||||
|
||||
/**
|
||||
* Expand/collapse the table result viewer. When collapsed, table queries won't be run.
|
||||
*/
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
ExploreItemState,
|
||||
ExploreUrlState,
|
||||
ExploreState,
|
||||
QueryTransaction,
|
||||
RangeScanner,
|
||||
ExploreMode,
|
||||
} from 'app/types/explore';
|
||||
@ -25,6 +24,7 @@ import {
|
||||
splitOpenAction,
|
||||
splitCloseAction,
|
||||
changeModeAction,
|
||||
runQueriesAction,
|
||||
} from './actionTypes';
|
||||
import { Reducer } from 'redux';
|
||||
import { ActionOf } from 'app/core/redux/actionCreatorFactory';
|
||||
@ -36,7 +36,7 @@ import { DataSourceApi, DataQuery } from '@grafana/ui';
|
||||
|
||||
describe('Explore item reducer', () => {
|
||||
describe('scanning', () => {
|
||||
test('should start scanning', () => {
|
||||
it('should start scanning', () => {
|
||||
const scanner = jest.fn();
|
||||
const initalState = {
|
||||
...makeExploreItemState(),
|
||||
@ -53,7 +53,7 @@ describe('Explore item reducer', () => {
|
||||
scanner,
|
||||
});
|
||||
});
|
||||
test('should stop scanning', () => {
|
||||
it('should stop scanning', () => {
|
||||
const scanner = jest.fn();
|
||||
const initalState = {
|
||||
...makeExploreItemState(),
|
||||
@ -96,7 +96,6 @@ describe('Explore item reducer', () => {
|
||||
describe('when testDataSourceFailureAction is dispatched', () => {
|
||||
it('then it should set correct state', () => {
|
||||
const error = 'some error';
|
||||
const queryTransactions: QueryTransaction[] = [];
|
||||
const initalState: Partial<ExploreItemState> = {
|
||||
datasourceError: null,
|
||||
graphResult: [],
|
||||
@ -111,7 +110,6 @@ describe('Explore item reducer', () => {
|
||||
};
|
||||
const expectedState = {
|
||||
datasourceError: error,
|
||||
queryTransactions,
|
||||
graphResult: undefined as any[],
|
||||
tableResult: undefined as TableModel,
|
||||
logsResult: undefined as LogsModel,
|
||||
@ -144,9 +142,9 @@ describe('Explore item reducer', () => {
|
||||
const StartPage = {};
|
||||
const datasourceInstance = {
|
||||
meta: {
|
||||
metrics: {},
|
||||
logs: {},
|
||||
tables: {},
|
||||
metrics: true,
|
||||
logs: true,
|
||||
tables: true,
|
||||
},
|
||||
components: {
|
||||
ExploreStartPage: StartPage,
|
||||
@ -175,6 +173,11 @@ describe('Explore item reducer', () => {
|
||||
queryKeys,
|
||||
supportedModes: [ExploreMode.Metrics, ExploreMode.Logs],
|
||||
mode: ExploreMode.Metrics,
|
||||
graphIsLoading: false,
|
||||
tableIsLoading: false,
|
||||
logIsLoading: false,
|
||||
latency: 0,
|
||||
queryErrors: [],
|
||||
};
|
||||
|
||||
reducerTester()
|
||||
@ -185,6 +188,28 @@ describe('Explore item reducer', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('run queries', () => {
|
||||
describe('when runQueriesAction is dispatched', () => {
|
||||
it('then it should set correct state', () => {
|
||||
const initalState: Partial<ExploreItemState> = {
|
||||
showingStartPage: true,
|
||||
};
|
||||
const expectedState = {
|
||||
queryIntervals: {
|
||||
interval: '1s',
|
||||
intervalMs: 1000,
|
||||
},
|
||||
showingStartPage: false,
|
||||
};
|
||||
|
||||
reducerTester()
|
||||
.givenReducer(itemReducer, initalState)
|
||||
.whenActionIsDispatched(runQueriesAction({ exploreId: ExploreId.left }))
|
||||
.thenStateShouldEqual(expectedState);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
export const setup = (urlStateOverrides?: any) => {
|
||||
@ -529,46 +554,8 @@ describe('Explore reducer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('and refreshInterval differs', () => {
|
||||
it('then it should return update refreshInterval', () => {
|
||||
const { initalState, serializedUrlState } = setup();
|
||||
const expectedState = {
|
||||
...initalState,
|
||||
left: {
|
||||
...initalState.left,
|
||||
update: {
|
||||
...initalState.left.update,
|
||||
refreshInterval: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const stateWithDifferentDataSource = {
|
||||
...initalState,
|
||||
left: {
|
||||
...initalState.left,
|
||||
urlState: {
|
||||
...initalState.left.urlState,
|
||||
refreshInterval: '5s',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
reducerTester()
|
||||
.givenReducer(exploreReducer, stateWithDifferentDataSource)
|
||||
.whenActionIsDispatched(
|
||||
updateLocation({
|
||||
query: {
|
||||
left: serializedUrlState,
|
||||
},
|
||||
path: '/explore',
|
||||
})
|
||||
)
|
||||
.thenStateShouldEqual(expectedState);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and nothing differs', () => {
|
||||
fit('then it should return update ui', () => {
|
||||
it('then it should return update ui', () => {
|
||||
const { initalState, serializedUrlState } = setup();
|
||||
const expectedState = { ...initalState };
|
||||
|
||||
|
@ -95,7 +95,6 @@ export const makeExploreItemState = (): ExploreItemState => ({
|
||||
scanning: false,
|
||||
scanRange: null,
|
||||
showingGraph: true,
|
||||
showingLogs: true,
|
||||
showingTable: true,
|
||||
graphIsLoading: false,
|
||||
logIsLoading: false,
|
||||
@ -351,7 +350,6 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
logsResult: resultType === 'Logs' ? null : state.logsResult,
|
||||
latency: 0,
|
||||
queryErrors,
|
||||
showingStartPage: false,
|
||||
graphIsLoading: resultType === 'Graph' ? false : state.graphIsLoading,
|
||||
logIsLoading: resultType === 'Logs' ? false : state.logIsLoading,
|
||||
tableIsLoading: resultType === 'Table' ? false : state.tableIsLoading,
|
||||
@ -371,7 +369,6 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
graphIsLoading: resultType === 'Graph' ? true : state.graphIsLoading,
|
||||
logIsLoading: resultType === 'Logs' ? true : state.logIsLoading,
|
||||
tableIsLoading: resultType === 'Table' ? true : state.tableIsLoading,
|
||||
showingStartPage: false,
|
||||
update: makeInitialUpdateState(),
|
||||
};
|
||||
},
|
||||
@ -392,7 +389,6 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
graphIsLoading: false,
|
||||
logIsLoading: false,
|
||||
tableIsLoading: false,
|
||||
showingStartPage: false,
|
||||
update: makeInitialUpdateState(),
|
||||
};
|
||||
},
|
||||
@ -543,6 +539,7 @@ export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemSta
|
||||
return {
|
||||
...state,
|
||||
queryIntervals,
|
||||
showingStartPage: false,
|
||||
};
|
||||
},
|
||||
})
|
||||
|
@ -3,10 +3,9 @@ import { ExploreItemState } from 'app/types';
|
||||
import { filterLogLevels, dedupLogRows } from 'app/core/logs_model';
|
||||
|
||||
export const exploreItemUIStateSelector = (itemState: ExploreItemState) => {
|
||||
const { showingGraph, showingLogs, showingTable, showingStartPage, dedupStrategy } = itemState;
|
||||
const { showingGraph, showingTable, showingStartPage, dedupStrategy } = itemState;
|
||||
return {
|
||||
showingGraph,
|
||||
showingLogs,
|
||||
showingTable,
|
||||
showingStartPage,
|
||||
dedupStrategy,
|
||||
|
@ -204,10 +204,6 @@ export interface ExploreItemState {
|
||||
* True if graph result viewer is expanded. Query runs will contain graph queries.
|
||||
*/
|
||||
showingGraph: boolean;
|
||||
/**
|
||||
* True if logs result viewer is expanded. Query runs will contain logs queries.
|
||||
*/
|
||||
showingLogs: boolean;
|
||||
/**
|
||||
* True StartPage needs to be shown. Typically set to `false` once queries have been run.
|
||||
*/
|
||||
|
@ -164,7 +164,7 @@
|
||||
.explore-panel__header {
|
||||
padding: $space-sm $space-md 0 $space-md;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
cursor: inherit;
|
||||
transition: all 0.1s linear;
|
||||
}
|
||||
|
||||
@ -176,9 +176,20 @@
|
||||
}
|
||||
|
||||
.explore-panel__header-buttons {
|
||||
margin-right: $space-sm;
|
||||
font-size: $font-size-lg;
|
||||
line-height: $font-size-h6;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.explore-panel--collapsible {
|
||||
.explore-panel__header {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.explore-panel__header-buttons {
|
||||
margin-right: $space-sm;
|
||||
font-size: $font-size-lg;
|
||||
line-height: $font-size-h6;
|
||||
display: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.time-series-disclaimer {
|
||||
|
Loading…
Reference in New Issue
Block a user