Commit adb2430a by David Kaltschmidt

Explore: collapsible result panels

- replace the Graph/Table buttons with toggle control in a wrapper panel
- moved toggle control to left to be close to the label
- removed panel styles from Logs and Graph viewer
- moved loader animation to panel
parent 634d71a6
...@@ -19,6 +19,7 @@ import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage'; ...@@ -19,6 +19,7 @@ import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model'; import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
import { DatasourceSrv } from 'app/features/plugins/datasource_srv'; import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
import Panel from './Panel';
import QueryRows from './QueryRows'; import QueryRows from './QueryRows';
import Graph from './Graph'; import Graph from './Graph';
import Logs from './Logs'; import Logs from './Logs';
...@@ -127,6 +128,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -127,6 +128,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
range: initialRange, range: initialRange,
showingGraph: true, showingGraph: true,
showingLogs: true, showingLogs: true,
showingStartPage: false,
showingTable: true, showingTable: true,
supportsGraph: null, supportsGraph: null,
supportsLogs: null, supportsLogs: null,
...@@ -238,6 +240,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -238,6 +240,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
datasourceLoading: false, datasourceLoading: false,
datasourceName: datasource.name, datasourceName: datasource.name,
queries: nextQueries, queries: nextQueries,
showingStartPage: Boolean(StartPage),
}, },
() => { () => {
if (datasourceError === null) { if (datasourceError === null) {
...@@ -329,10 +332,11 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -329,10 +332,11 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
onClickClear = () => { onClickClear = () => {
this.queryExpressions = ['']; this.queryExpressions = [''];
this.setState( this.setState(
{ prevState => ({
queries: ensureQueries(), queries: ensureQueries(),
queryTransactions: [], queryTransactions: [],
}, showingStartPage: Boolean(prevState.StartPage),
}),
this.saveState this.saveState
); );
}; };
...@@ -563,6 +567,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -563,6 +567,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
return { return {
queryTransactions: nextQueryTransactions, queryTransactions: nextQueryTransactions,
showingStartPage: false,
}; };
}); });
...@@ -789,16 +794,13 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -789,16 +794,13 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
range, range,
showingGraph, showingGraph,
showingLogs, showingLogs,
showingStartPage,
showingTable, showingTable,
supportsGraph, supportsGraph,
supportsLogs, supportsLogs,
supportsTable, supportsTable,
} = this.state; } = this.state;
const showingBoth = showingGraph && showingTable; const graphHeight = showingGraph && showingTable ? '200px' : '400px';
const graphHeight = showingBoth ? '200px' : '400px';
const graphButtonActive = showingBoth || showingGraph ? 'active' : '';
const logsButtonActive = showingLogs ? 'active' : '';
const tableButtonActive = showingBoth || showingTable ? 'active' : '';
const exploreClass = split ? 'explore explore-split' : 'explore'; const exploreClass = split ? 'explore explore-split' : 'explore';
const selectedDatasource = datasource ? exploreDatasources.find(d => d.label === datasource.name) : undefined; const selectedDatasource = datasource ? exploreDatasources.find(d => d.label === datasource.name) : undefined;
const graphRangeIntervals = getIntervals(graphRange, datasource, this.el ? this.el.offsetWidth : 0); const graphRangeIntervals = getIntervals(graphRange, datasource, this.el ? this.el.offsetWidth : 0);
...@@ -823,8 +825,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -823,8 +825,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
) )
: undefined; : undefined;
const loading = queryTransactions.some(qt => !qt.done); const loading = queryTransactions.some(qt => !qt.done);
const showStartPages = StartPage && queryTransactions.length === 0;
const viewModeCount = [supportsGraph, supportsLogs, supportsTable].filter(m => m).length;
return ( return (
<div className={exploreClass} ref={this.getRef}> <div className={exploreClass} ref={this.getRef}>
...@@ -913,55 +913,47 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -913,55 +913,47 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
/> />
<main className="m-t-2"> <main className="m-t-2">
<ErrorBoundary> <ErrorBoundary>
{showStartPages && <StartPage onClickQuery={this.onClickQuery} />} {showingStartPage && <StartPage onClickQuery={this.onClickQuery} />}
{!showStartPages && ( {!showingStartPage && (
<> <>
{viewModeCount > 1 && ( {supportsGraph && (
<div className="result-options"> <Panel
{supportsGraph ? ( label="Graph"
<button className={`btn toggle-btn ${graphButtonActive}`} onClick={this.onClickGraphButton}> isOpen={showingGraph}
Graph loading={graphLoading}
</button> onToggle={this.onClickGraphButton}
) : null} >
{supportsTable ? (
<button className={`btn toggle-btn ${tableButtonActive}`} onClick={this.onClickTableButton}>
Table
</button>
) : null}
{supportsLogs ? (
<button className={`btn toggle-btn ${logsButtonActive}`} onClick={this.onClickLogsButton}>
Logs
</button>
) : null}
</div>
)}
{supportsGraph &&
showingGraph && (
<Graph <Graph
data={graphResult} data={graphResult}
height={graphHeight} height={graphHeight}
loading={graphLoading}
id={`explore-graph-${position}`} id={`explore-graph-${position}`}
onChangeTime={this.onChangeTime} onChangeTime={this.onChangeTime}
range={graphRange} range={graphRange}
split={split} split={split}
/> />
)} </Panel>
{supportsTable && showingTable ? ( )}
<div className="panel-container m-t-2"> {supportsTable && (
<Panel
label="Table"
loading={tableLoading}
isOpen={showingTable}
onToggle={this.onClickTableButton}
>
<Table data={tableResult} loading={tableLoading} onClickCell={this.onClickTableCell} /> <Table data={tableResult} loading={tableLoading} onClickCell={this.onClickTableCell} />
</div> </Panel>
) : null} )}
{supportsLogs && showingLogs ? ( {supportsLogs && (
<Logs <Panel label="Logs" loading={logsLoading} isOpen={showingLogs} onToggle={this.onClickLogsButton}>
data={logsResult} <Logs
loading={logsLoading} data={logsResult}
position={position} loading={logsLoading}
onChangeTime={this.onChangeTime} position={position}
range={range} onChangeTime={this.onChangeTime}
/> range={range}
) : null} />
</Panel>
)}
</> </>
)} )}
</ErrorBoundary> </ErrorBoundary>
......
...@@ -77,7 +77,6 @@ interface GraphProps { ...@@ -77,7 +77,6 @@ interface GraphProps {
data: any[]; data: any[];
height?: string; // e.g., '200px' height?: string; // e.g., '200px'
id?: string; id?: string;
loading?: boolean;
range: RawTimeRange; range: RawTimeRange;
split?: boolean; split?: boolean;
size?: { width: number; height: number }; size?: { width: number; height: number };
...@@ -188,12 +187,11 @@ export class Graph extends PureComponent<GraphProps, GraphState> { ...@@ -188,12 +187,11 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
} }
render() { render() {
const { height = '100px', id = 'graph', loading = false } = this.props; const { height = '100px', id = 'graph' } = this.props;
const data = this.getGraphData(); const data = this.getGraphData();
return ( return (
<div className="panel-container"> <>
{loading && <div className="explore-panel__loader" />}
{this.props.data && {this.props.data &&
this.props.data.length > MAX_NUMBER_OF_TIME_SERIES && this.props.data.length > MAX_NUMBER_OF_TIME_SERIES &&
!this.state.showAllTimeSeries && ( !this.state.showAllTimeSeries && (
...@@ -207,7 +205,7 @@ export class Graph extends PureComponent<GraphProps, GraphState> { ...@@ -207,7 +205,7 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
)} )}
<div id={id} className="explore-graph" style={{ height }} /> <div id={id} className="explore-graph" style={{ height }} />
<Legend data={data} /> <Legend data={data} />
</div> </>
); );
} }
} }
......
...@@ -97,7 +97,7 @@ export default class Logs extends PureComponent<LogsProps, LogsState> { ...@@ -97,7 +97,7 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
/> />
</div> </div>
<div className="panel-container logs-options"> <div className="logs-options">
<div className="logs-controls"> <div className="logs-controls">
<Switch label="Timestamp" checked={showUtc} onChange={this.onChangeUtc} small /> <Switch label="Timestamp" checked={showUtc} onChange={this.onChangeUtc} small />
<Switch label="Local time" checked={showLocalTime} onChange={this.onChangeLocalTime} small /> <Switch label="Local time" checked={showLocalTime} onChange={this.onChangeLocalTime} small />
...@@ -116,33 +116,30 @@ export default class Logs extends PureComponent<LogsProps, LogsState> { ...@@ -116,33 +116,30 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
</div> </div>
</div> </div>
<div className="panel-container"> <div className="logs-entries" style={logEntriesStyle}>
{loading && <div className="explore-panel__loader" />} {hasData &&
<div className="logs-entries" style={logEntriesStyle}> data.rows.map(row => (
{hasData && <Fragment key={row.key}>
data.rows.map(row => ( <div className={row.logLevel ? `logs-row-level logs-row-level-${row.logLevel}` : ''} />
<Fragment key={row.key}> {showUtc && <div title={`Local: ${row.timeLocal} (${row.timeFromNow})`}>{row.timestamp}</div>}
<div className={row.logLevel ? `logs-row-level logs-row-level-${row.logLevel}` : ''} /> {showLocalTime && <div title={`${row.timestamp} (${row.timeFromNow})`}>{row.timeLocal}</div>}
{showUtc && <div title={`Local: ${row.timeLocal} (${row.timeFromNow})`}>{row.timestamp}</div>} {showLabels && (
{showLocalTime && <div title={`${row.timestamp} (${row.timeFromNow})`}>{row.timeLocal}</div>} <div className="max-width" title={row.labels}>
{showLabels && ( {row.labels}
<div className="max-width" title={row.labels}>
{row.labels}
</div>
)}
<div>
<Highlighter
textToHighlight={row.entry}
searchWords={row.searchWords}
findChunks={findHighlightChunksInText}
highlightClassName="logs-row-match-highlight"
/>
</div> </div>
</Fragment> )}
))} <div>
</div> <Highlighter
{!loading && !hasData && 'No data was returned.'} textToHighlight={row.entry}
searchWords={row.searchWords}
findChunks={findHighlightChunksInText}
highlightClassName="logs-row-match-highlight"
/>
</div>
</Fragment>
))}
</div> </div>
{!loading && !hasData && 'No data was returned.'}
</div> </div>
); );
} }
......
import React, { PureComponent } from 'react';
interface Props {
isOpen: boolean;
label: string;
loading?: boolean;
onToggle: (isOpen: boolean) => void;
}
export default class Panel extends PureComponent<Props> {
onClickToggle = () => this.props.onToggle(!this.props.isOpen);
render() {
const { isOpen, loading } = this.props;
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="explore-panel__header" onClick={this.onClickToggle}>
<div className="explore-panel__header-buttons">
<span className={iconClass} />
</div>
<div className="explore-panel__header-label">{this.props.label}</div>
</div>
{isOpen && (
<div className="explore-panel__body">
<div className={loaderClass} />
{this.props.children}
</div>
)}
</div>
);
}
}
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render should render component 1`] = ` exports[`Render should render component 1`] = `
<div <Fragment>
className="panel-container"
>
<div <div
className="explore-graph" className="explore-graph"
id="graph" id="graph"
...@@ -456,13 +454,11 @@ exports[`Render should render component 1`] = ` ...@@ -456,13 +454,11 @@ exports[`Render should render component 1`] = `
] ]
} }
/> />
</div> </Fragment>
`; `;
exports[`Render should render component with disclaimer 1`] = ` exports[`Render should render component with disclaimer 1`] = `
<div <Fragment>
className="panel-container"
>
<div <div
className="time-series-disclaimer" className="time-series-disclaimer"
> >
...@@ -952,13 +948,11 @@ exports[`Render should render component with disclaimer 1`] = ` ...@@ -952,13 +948,11 @@ exports[`Render should render component with disclaimer 1`] = `
] ]
} }
/> />
</div> </Fragment>
`; `;
exports[`Render should show query return no time series 1`] = ` exports[`Render should show query return no time series 1`] = `
<div <Fragment>
className="panel-container"
>
<div <div
className="explore-graph" className="explore-graph"
id="graph" id="graph"
...@@ -971,5 +965,5 @@ exports[`Render should show query return no time series 1`] = ` ...@@ -971,5 +965,5 @@ exports[`Render should show query return no time series 1`] = `
<Legend <Legend
data={Array []} data={Array []}
/> />
</div> </Fragment>
`; `;
...@@ -173,6 +173,7 @@ export interface ExploreState { ...@@ -173,6 +173,7 @@ export interface ExploreState {
range: RawTimeRange; range: RawTimeRange;
showingGraph: boolean; showingGraph: boolean;
showingLogs: boolean; showingLogs: boolean;
showingStartPage?: boolean;
showingTable: boolean; showingTable: boolean;
supportsGraph: boolean | null; supportsGraph: boolean | null;
supportsLogs: boolean | null; supportsLogs: boolean | null;
......
...@@ -18,10 +18,39 @@ ...@@ -18,10 +18,39 @@
margin-left: 15px; margin-left: 15px;
} }
// Graph panel needs a bit extra padding at top .explore-panel {
.panel-container { margin-top: $panel-margin;
}
.explore-panel__body {
padding: $panel-padding;
}
.explore-panel__header {
padding: $panel-padding; padding: $panel-padding;
padding-top: 10px; padding-top: 5px;
padding-bottom: 0;
display: flex;
cursor: pointer;
margin-bottom: 5px;
transition: all 0.1s linear;
}
.explore-panel__header:hover {
transform: translateY(-1px);
}
.explore-panel__header-label {
font-weight: 500;
margin-right: $panel-margin;
font-size: $font-size-h6;
box-shadow: $text-shadow-faint;
}
.explore-panel__header-buttons {
margin-right: $panel-margin;
font-size: $font-size-lg;
line-height: $font-size-h6;
} }
// Make sure wrap buttons around on small screens // Make sure wrap buttons around on small screens
...@@ -91,11 +120,16 @@ ...@@ -91,11 +120,16 @@
height: 2px; height: 2px;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
background: $text-color-faint; background: none;
margin: $panel-margin / 2; margin: $panel-margin / 2;
transition: background-color 1s ease;
} }
.explore-panel__loader:after { .explore-panel__loader--active {
background: $text-color-faint;
}
.explore-panel__loader--active:after {
content: ' '; content: ' ';
display: block; display: block;
width: 25%; width: 25%;
...@@ -221,17 +255,18 @@ ...@@ -221,17 +255,18 @@
.logs-controls { .logs-controls {
display: flex; display: flex;
background-color: $page-bg;
padding: $panel-padding;
padding-top: 10px;
border-radius: $border-radius;
margin: 2*$panel-margin 0;
border: $panel-border;
> * { > * {
margin-right: 1em; margin-right: 1em;
} }
} }
.logs-options,
.logs-graph {
margin-bottom: $panel-margin;
}
.logs-meta { .logs-meta {
flex: 1; flex: 1;
color: $text-color-weak; color: $text-color-weak;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment