Pārlūkot izejas kodu

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
David Kaltschmidt 7 gadi atpakaļ
vecāks
revīzija
adb2430a1b

+ 40 - 48
public/app/features/explore/Explore.tsx

@@ -19,6 +19,7 @@ import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
 import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
 import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
 
+import Panel from './Panel';
 import QueryRows from './QueryRows';
 import Graph from './Graph';
 import Logs from './Logs';
@@ -127,6 +128,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
         range: initialRange,
         showingGraph: true,
         showingLogs: true,
+        showingStartPage: false,
         showingTable: true,
         supportsGraph: null,
         supportsLogs: null,
@@ -238,6 +240,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
         datasourceLoading: false,
         datasourceName: datasource.name,
         queries: nextQueries,
+        showingStartPage: Boolean(StartPage),
       },
       () => {
         if (datasourceError === null) {
@@ -329,10 +332,11 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
   onClickClear = () => {
     this.queryExpressions = [''];
     this.setState(
-      {
+      prevState => ({
         queries: ensureQueries(),
         queryTransactions: [],
-      },
+        showingStartPage: Boolean(prevState.StartPage),
+      }),
       this.saveState
     );
   };
@@ -563,6 +567,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
 
       return {
         queryTransactions: nextQueryTransactions,
+        showingStartPage: false,
       };
     });
 
@@ -789,16 +794,13 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
       range,
       showingGraph,
       showingLogs,
+      showingStartPage,
       showingTable,
       supportsGraph,
       supportsLogs,
       supportsTable,
     } = this.state;
-    const showingBoth = showingGraph && showingTable;
-    const graphHeight = showingBoth ? '200px' : '400px';
-    const graphButtonActive = showingBoth || showingGraph ? 'active' : '';
-    const logsButtonActive = showingLogs ? 'active' : '';
-    const tableButtonActive = showingBoth || showingTable ? 'active' : '';
+    const graphHeight = showingGraph && showingTable ? '200px' : '400px';
     const exploreClass = split ? 'explore explore-split' : 'explore';
     const selectedDatasource = datasource ? exploreDatasources.find(d => d.label === datasource.name) : undefined;
     const graphRangeIntervals = getIntervals(graphRange, datasource, this.el ? this.el.offsetWidth : 0);
@@ -823,8 +825,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
           )
         : undefined;
     const loading = queryTransactions.some(qt => !qt.done);
-    const showStartPages = StartPage && queryTransactions.length === 0;
-    const viewModeCount = [supportsGraph, supportsLogs, supportsTable].filter(m => m).length;
 
     return (
       <div className={exploreClass} ref={this.getRef}>
@@ -913,55 +913,47 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
             />
             <main className="m-t-2">
               <ErrorBoundary>
-                {showStartPages && <StartPage onClickQuery={this.onClickQuery} />}
-                {!showStartPages && (
+                {showingStartPage && <StartPage onClickQuery={this.onClickQuery} />}
+                {!showingStartPage && (
                   <>
-                    {viewModeCount > 1 && (
-                      <div className="result-options">
-                        {supportsGraph ? (
-                          <button className={`btn toggle-btn ${graphButtonActive}`} onClick={this.onClickGraphButton}>
-                            Graph
-                          </button>
-                        ) : 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 && (
+                    {supportsGraph && (
+                      <Panel
+                        label="Graph"
+                        isOpen={showingGraph}
+                        loading={graphLoading}
+                        onToggle={this.onClickGraphButton}
+                      >
                         <Graph
                           data={graphResult}
                           height={graphHeight}
-                          loading={graphLoading}
                           id={`explore-graph-${position}`}
                           onChangeTime={this.onChangeTime}
                           range={graphRange}
                           split={split}
                         />
-                      )}
-                    {supportsTable && showingTable ? (
-                      <div className="panel-container m-t-2">
+                      </Panel>
+                    )}
+                    {supportsTable && (
+                      <Panel
+                        label="Table"
+                        loading={tableLoading}
+                        isOpen={showingTable}
+                        onToggle={this.onClickTableButton}
+                      >
                         <Table data={tableResult} loading={tableLoading} onClickCell={this.onClickTableCell} />
-                      </div>
-                    ) : null}
-                    {supportsLogs && showingLogs ? (
-                      <Logs
-                        data={logsResult}
-                        loading={logsLoading}
-                        position={position}
-                        onChangeTime={this.onChangeTime}
-                        range={range}
-                      />
-                    ) : null}
+                      </Panel>
+                    )}
+                    {supportsLogs && (
+                      <Panel label="Logs" loading={logsLoading} isOpen={showingLogs} onToggle={this.onClickLogsButton}>
+                        <Logs
+                          data={logsResult}
+                          loading={logsLoading}
+                          position={position}
+                          onChangeTime={this.onChangeTime}
+                          range={range}
+                        />
+                      </Panel>
+                    )}
                   </>
                 )}
               </ErrorBoundary>

+ 3 - 5
public/app/features/explore/Graph.tsx

@@ -77,7 +77,6 @@ interface GraphProps {
   data: any[];
   height?: string; // e.g., '200px'
   id?: string;
-  loading?: boolean;
   range: RawTimeRange;
   split?: boolean;
   size?: { width: number; height: number };
@@ -188,12 +187,11 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
   }
 
   render() {
-    const { height = '100px', id = 'graph', loading = false } = this.props;
+    const { height = '100px', id = 'graph' } = this.props;
     const data = this.getGraphData();
 
     return (
-      <div className="panel-container">
-        {loading && <div className="explore-panel__loader" />}
+      <>
         {this.props.data &&
           this.props.data.length > MAX_NUMBER_OF_TIME_SERIES &&
           !this.state.showAllTimeSeries && (
@@ -207,7 +205,7 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
           )}
         <div id={id} className="explore-graph" style={{ height }} />
         <Legend data={data} />
-      </div>
+      </>
     );
   }
 }

+ 23 - 26
public/app/features/explore/Logs.tsx

@@ -97,7 +97,7 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
           />
         </div>
 
-        <div className="panel-container logs-options">
+        <div className="logs-options">
           <div className="logs-controls">
             <Switch label="Timestamp" checked={showUtc} onChange={this.onChangeUtc} small />
             <Switch label="Local time" checked={showLocalTime} onChange={this.onChangeLocalTime} small />
@@ -116,33 +116,30 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
           </div>
         </div>
 
-        <div className="panel-container">
-          {loading && <div className="explore-panel__loader" />}
-          <div className="logs-entries" style={logEntriesStyle}>
-            {hasData &&
-              data.rows.map(row => (
-                <Fragment key={row.key}>
-                  <div className={row.logLevel ? `logs-row-level logs-row-level-${row.logLevel}` : ''} />
-                  {showUtc && <div title={`Local: ${row.timeLocal} (${row.timeFromNow})`}>{row.timestamp}</div>}
-                  {showLocalTime && <div title={`${row.timestamp} (${row.timeFromNow})`}>{row.timeLocal}</div>}
-                  {showLabels && (
-                    <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 className="logs-entries" style={logEntriesStyle}>
+          {hasData &&
+            data.rows.map(row => (
+              <Fragment key={row.key}>
+                <div className={row.logLevel ? `logs-row-level logs-row-level-${row.logLevel}` : ''} />
+                {showUtc && <div title={`Local: ${row.timeLocal} (${row.timeFromNow})`}>{row.timestamp}</div>}
+                {showLocalTime && <div title={`${row.timestamp} (${row.timeFromNow})`}>{row.timeLocal}</div>}
+                {showLabels && (
+                  <div className="max-width" title={row.labels}>
+                    {row.labels}
                   </div>
-                </Fragment>
-              ))}
-          </div>
-          {!loading && !hasData && 'No data was returned.'}
+                )}
+                <div>
+                  <Highlighter
+                    textToHighlight={row.entry}
+                    searchWords={row.searchWords}
+                    findChunks={findHighlightChunksInText}
+                    highlightClassName="logs-row-match-highlight"
+                  />
+                </div>
+              </Fragment>
+            ))}
         </div>
+        {!loading && !hasData && 'No data was returned.'}
       </div>
     );
   }

+ 34 - 0
public/app/features/explore/Panel.tsx

@@ -0,0 +1,34 @@
+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>
+    );
+  }
+}

+ 6 - 12
public/app/features/explore/__snapshots__/Graph.test.tsx.snap

@@ -1,9 +1,7 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`Render should render component 1`] = `
-<div
-  className="panel-container"
->
+<Fragment>
   <div
     className="explore-graph"
     id="graph"
@@ -456,13 +454,11 @@ exports[`Render should render component 1`] = `
       ]
     }
   />
-</div>
+</Fragment>
 `;
 
 exports[`Render should render component with disclaimer 1`] = `
-<div
-  className="panel-container"
->
+<Fragment>
   <div
     className="time-series-disclaimer"
   >
@@ -952,13 +948,11 @@ exports[`Render should render component with disclaimer 1`] = `
       ]
     }
   />
-</div>
+</Fragment>
 `;
 
 exports[`Render should show query return no time series 1`] = `
-<div
-  className="panel-container"
->
+<Fragment>
   <div
     className="explore-graph"
     id="graph"
@@ -971,5 +965,5 @@ exports[`Render should show query return no time series 1`] = `
   <Legend
     data={Array []}
   />
-</div>
+</Fragment>
 `;

+ 1 - 0
public/app/types/explore.ts

@@ -173,6 +173,7 @@ export interface ExploreState {
   range: RawTimeRange;
   showingGraph: boolean;
   showingLogs: boolean;
+  showingStartPage?: boolean;
   showingTable: boolean;
   supportsGraph: boolean | null;
   supportsLogs: boolean | null;

+ 45 - 10
public/sass/pages/_explore.scss

@@ -18,10 +18,39 @@
     margin-left: 15px;
   }
 
-  // Graph panel needs a bit extra padding at top
-  .panel-container {
+  .explore-panel {
+    margin-top: $panel-margin;
+  }
+
+  .explore-panel__body {
+    padding: $panel-padding;
+  }
+
+  .explore-panel__header {
     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
@@ -91,11 +120,16 @@
     height: 2px;
     position: relative;
     overflow: hidden;
-    background: $text-color-faint;
+    background: none;
     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: ' ';
     display: block;
     width: 25%;
@@ -221,17 +255,18 @@
 
     .logs-controls {
       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;
       }
     }
 
-    .logs-options,
-    .logs-graph {
-      margin-bottom: $panel-margin;
-    }
-
     .logs-meta {
       flex: 1;
       color: $text-color-weak;