소스 검색

Merge branch 'panel-edit-in-react' of https://github.com/grafana/grafana into panel-edit-in-react

Erik Sundell 7 년 전
부모
커밋
3395b1caf6
30개의 변경된 파일356개의 추가작업 그리고 174개의 파일을 삭제
  1. 11 2
      docs/sources/auth/overview.md
  2. 2 2
      public/app/core/utils/kbn.ts
  3. 1 1
      public/app/features/dashboard/dashgrid/AlertTab.tsx
  4. 21 12
      public/app/features/dashboard/dashgrid/EditorTabBody.tsx
  5. 1 7
      public/app/features/dashboard/dashgrid/GeneralTab.tsx
  6. 20 19
      public/app/features/dashboard/dashgrid/PanelEditor.tsx
  7. 1 1
      public/app/features/dashboard/dashgrid/QueriesTab.tsx
  8. 7 1
      public/app/features/dashboard/dashgrid/VisualizationTab.tsx
  9. 0 4
      public/app/features/dashboard/dashgrid/VizTypePicker.tsx
  10. 1 1
      public/app/features/explore/Typeahead.tsx
  11. 0 70
      public/app/features/panel/partials/metrics_tab.html
  12. 9 0
      public/app/plugins/datasource/logging/language_provider.test.ts
  13. 18 10
      public/app/plugins/datasource/logging/language_provider.ts
  14. 3 2
      public/app/plugins/datasource/prometheus/language_provider.ts
  15. 9 0
      public/img/panel-tabs/alert-selected.svg
  16. 9 0
      public/img/panel-tabs/alert.svg
  17. 31 0
      public/img/panel-tabs/general-selected.svg
  18. 8 0
      public/img/panel-tabs/general.svg
  19. 31 0
      public/img/panel-tabs/queries-selected.svg
  20. 8 0
      public/img/panel-tabs/queries.svg
  21. 34 0
      public/img/panel-tabs/visualization-selected.svg
  22. 7 0
      public/img/panel-tabs/visualization.svg
  23. 3 4
      public/sass/_variables.dark.scss
  24. 2 3
      public/sass/_variables.light.scss
  25. 2 1
      public/sass/components/_footer.scss
  26. 2 2
      public/sass/components/_gf-form.scss
  27. 75 19
      public/sass/components/_panel_editor.scss
  28. 1 1
      public/sass/components/_query_editor.scss
  29. 10 12
      public/sass/components/_toolbar.scss
  30. 29 0
      public/sass/layout/_page.scss

+ 11 - 2
docs/sources/auth/overview.md

@@ -78,8 +78,8 @@ disable_login_form = true
 
 ### Automatic OAuth login
 
-Set to true to attempt login with OAuth automatically, skipping the login screen. 
-This setting is ignored if multiple OAuth providers are configured. 
+Set to true to attempt login with OAuth automatically, skipping the login screen.
+This setting is ignored if multiple OAuth providers are configured.
 Defaults to `false`.
 
 ```bash
@@ -95,3 +95,12 @@ Set to the option detailed below to true to hide sign-out menu link. Useful if y
 [auth]
 disable_signout_menu = true
 ```
+
+### URL redirect after signing out
+
+URL to redirect the user to after signing out from Grafana. This can for example be used to enable signout from oauth provider.
+
+```bash
+[auth]
+signout_redirect_url =
+```

+ 2 - 2
public/app/core/utils/kbn.ts

@@ -584,8 +584,8 @@ kbn.valueFormats.flowcms = kbn.formatBuilders.fixedUnit('cms');
 kbn.valueFormats.flowcfs = kbn.formatBuilders.fixedUnit('cfs');
 kbn.valueFormats.flowcfm = kbn.formatBuilders.fixedUnit('cfm');
 kbn.valueFormats.litreh = kbn.formatBuilders.fixedUnit('l/h');
-kbn.valueFormats.flowlpm = kbn.formatBuilders.decimalSIPrefix('L');
-kbn.valueFormats.flowmlpm = kbn.formatBuilders.decimalSIPrefix('L', -1);
+kbn.valueFormats.flowlpm = kbn.formatBuilders.decimalSIPrefix('l/min');
+kbn.valueFormats.flowmlpm = kbn.formatBuilders.decimalSIPrefix('mL/min', -1);
 
 // Angle
 kbn.valueFormats.degree = kbn.formatBuilders.fixedUnit('°');

+ 1 - 1
public/app/features/dashboard/dashgrid/AlertTab.tsx

@@ -64,7 +64,7 @@ export class AlertTab extends PureComponent<Props> {
 
   render() {
     return (
-      <EditorTabBody toolbarItems={[]}>
+      <EditorTabBody heading="Alert" toolbarItems={[]}>
         <div ref={element => (this.element = element)} />
       </EditorTabBody>
     );

+ 21 - 12
public/app/features/dashboard/dashgrid/EditorTabBody.tsx

@@ -4,6 +4,7 @@ import { FadeIn } from 'app/core/components/Animations/FadeIn';
 
 interface Props {
   children: JSX.Element;
+  heading: string;
   main?: EditorToolBarView;
   toolbarItems: EditorToolBarView[];
 }
@@ -19,6 +20,7 @@ export interface EditorToolBarView {
 
 interface State {
   openView?: EditorToolBarView;
+  fadeIn: boolean;
 }
 
 export class EditorTabBody extends PureComponent<Props, State> {
@@ -27,9 +29,14 @@ export class EditorTabBody extends PureComponent<Props, State> {
 
     this.state = {
       openView: null,
+      fadeIn: false,
     };
   }
 
+  componentDidMount() {
+    this.setState({ fadeIn: true });
+  }
+
   onToggleToolBarView = (item: EditorToolBarView) => {
     this.setState({
       openView: item === this.state.openView ? null : item,
@@ -94,24 +101,26 @@ export class EditorTabBody extends PureComponent<Props, State> {
   }
 
   render() {
-    const { children, toolbarItems, main } = this.props;
-    const { openView } = this.state;
+    const { children, toolbarItems, main, heading } = this.props;
+    const { openView, fadeIn } = this.state;
+
     return (
       <>
-        {main && (
-          <div className="toolbar">
-            {this.renderMainSelection(main)}
-            <div className="gf-form--grow" />
-            {toolbarItems.map(item => this.renderButton(item))}
-          </div>
-        )}
+        <div className="toolbar">
+          <div className="toolbar__heading">{heading}</div>
+          {main && this.renderMainSelection(main)}
+          <div className="gf-form--grow" />
+          {toolbarItems.map(item => this.renderButton(item))}
+        </div>
         <div className="panel-editor__scroll">
           <CustomScrollbar autoHide={false}>
+            <FadeIn in={openView !== null} duration={200}>
+              <div className="panel-editor__toolbar-view">{openView && this.renderOpenView(openView)}</div>
+            </FadeIn>
             <div className="panel-editor__content">
-              <FadeIn in={openView !== null} duration={200}>
-                {openView && this.renderOpenView(openView)}
+              <FadeIn in={fadeIn} duration={50}>
+                {children}
               </FadeIn>
-              {children}
             </div>
           </CustomScrollbar>
         </div>

+ 1 - 7
public/app/features/dashboard/dashgrid/GeneralTab.tsx

@@ -43,14 +43,8 @@ export class GeneralTab extends PureComponent<Props> {
   }
 
   render() {
-    const currentDataSource = {
-      title: 'ProductionDB',
-      imgSrc: 'public/app/plugins/datasource/prometheus/img/prometheus_logo.svg',
-      render: () => <h2>hello</h2>,
-    };
-
     return (
-      <EditorTabBody main={currentDataSource} toolbarItems={[]}>
+      <EditorTabBody heading="Basic Panel Options" toolbarItems={[]}>
         <div ref={element => (this.element = element)} />
       </EditorTabBody>
     );

+ 20 - 19
public/app/features/dashboard/dashgrid/PanelEditor.tsx

@@ -26,7 +26,6 @@ interface PanelEditorProps {
 interface PanelEditorTab {
   id: string;
   text: string;
-  icon: string;
 }
 
 export class PanelEditor extends PureComponent<PanelEditorProps> {
@@ -74,35 +73,37 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
     const activeTab = store.getState().location.query.tab || 'queries';
 
     const tabs = [
-      { id: 'general', text: 'General', icon: 'gicon gicon-preferences' },
-      { id: 'queries', text: 'Queries', icon: 'fa fa-database' },
-      { id: 'visualization', text: 'Visualization', icon: 'fa fa-line-chart' },
+      { id: 'queries', text: 'Queries' },
+      { id: 'visualization', text: 'Visualization' },
+      { id: 'general', text: 'General' },
     ];
 
     if (config.alertingEnabled && plugin.id === 'graph') {
       tabs.push({
         id: 'alert',
         text: 'Alert',
-        icon: 'gicon gicon-alert',
       });
     }
 
     return (
       <div className="panel-editor-container__editor">
-        <div className="panel-editor-resizer">
-          <div className="panel-editor-resizer__handle">
-            <div className="panel-editor-resizer__handle-dots" />
-          </div>
+        <div className="panel-editor__close">
+          <i className="fa fa-arrow-left" />
         </div>
+        {
+          // <div className="panel-editor-resizer">
+          //   <div className="panel-editor-resizer__handle">
+          //     <div className="panel-editor-resizer__handle-dots" />
+          //   </div>
+          // </div>
+        }
 
         <div className="panel-editor-tabs">
-          <ul className="gf-tabs">
-            {tabs.map(tab => {
-              return <TabItem tab={tab} activeTab={activeTab} onClick={this.onChangeTab} key={tab.id} />;
-            })}
-          </ul>
+          {tabs.map(tab => {
+            return <TabItem tab={tab} activeTab={activeTab} onClick={this.onChangeTab} key={tab.id} />;
+          })}
         </div>
-        {this.renderCurrentTab(activeTab)}
+        <div className="panel-editor__right">{this.renderCurrentTab(activeTab)}</div>
       </div>
     );
   }
@@ -116,15 +117,15 @@ interface TabItemParams {
 
 function TabItem({ tab, activeTab, onClick }: TabItemParams) {
   const tabClasses = classNames({
-    'gf-tabs-link': true,
+    'panel-editor-tabs__link': true,
     active: activeTab === tab.id,
   });
 
   return (
-    <li className="gf-tabs-item" onClick={() => onClick(tab)}>
+    <div className="panel-editor-tabs__item" onClick={() => onClick(tab)}>
       <a className={tabClasses}>
-        <i className={tab.icon} /> {tab.text}
+        <img src={`public/img/panel-tabs/${tab.id}${activeTab === tab.id ? '-selected' : ''}.svg`} />
       </a>
-    </li>
+    </div>
   );
 }

+ 1 - 1
public/app/features/dashboard/dashgrid/QueriesTab.tsx

@@ -238,7 +238,7 @@ export class QueriesTab extends PureComponent<Props, State> {
     };
 
     return (
-      <EditorTabBody main={dsInformation} toolbarItems={[queryInspector, dsHelp, options]}>
+      <EditorTabBody heading="Queries" main={dsInformation} toolbarItems={[options, queryInspector, dsHelp]}>
         <div ref={element => (this.element = element)} style={{ width: '100%' }} />
       </EditorTabBody>
     );

+ 7 - 1
public/app/features/dashboard/dashgrid/VisualizationTab.tsx

@@ -126,8 +126,14 @@ export class VisualizationTab extends PureComponent<Props> {
       },
     };
 
+    const panelHelp = {
+      title: '',
+      icon: 'fa fa-question',
+      render: () => <h2>Help</h2>,
+    };
+
     return (
-      <EditorTabBody main={panelSelection} toolbarItems={[]}>
+      <EditorTabBody heading="Visualization" main={panelSelection} toolbarItems={[panelHelp]}>
         {this.renderPanelOptions()}
       </EditorTabBody>
     );

+ 0 - 4
public/app/features/dashboard/dashgrid/VizTypePicker.tsx

@@ -67,10 +67,6 @@ export class VizTypePicker extends PureComponent<Props, State> {
           />
           <i className="gf-form-input-icon fa fa-search" />
         </label>
-        <div className="p-l-1">
-          <button className="btn toggle-btn gf-form-btn active">Basic Types</button>
-          <button className="btn toggle-btn gf-form-btn">Master Types</button>
-        </div>
       </>
     );
   }

+ 1 - 1
public/app/features/explore/Typeahead.tsx

@@ -42,7 +42,7 @@ class TypeaheadItem extends React.PureComponent<TypeaheadItemProps> {
   render() {
     const { isSelected, item, prefix } = this.props;
     const className = isSelected ? 'typeahead-item typeahead-item__selected' : 'typeahead-item';
-    const { label } = item;
+    const label = item.label || '';
     return (
       <li ref={this.getRef} className={className} onClick={this.onClick}>
         <Highlighter textToHighlight={label} searchWords={[prefix]} highlightClassName="typeahead-match" />

+ 0 - 70
public/app/features/panel/partials/metrics_tab.html

@@ -1,73 +1,3 @@
-<div class="gf-form-group">
-  <div class="gf-form-inline">
-    <div class="gf-form">
-      <label class="gf-form-label">Data Source</label>
-      <gf-form-dropdown model="ctrl.panelDsValue" css-class="gf-size-auto"
-                        lookup-text="true"
-                        get-options="ctrl.getOptions(true)"
-                        on-change="ctrl.datasourceChanged($option)">
-      </gf-form-dropdown>
-		</div>
-
-		<div class="gf-form gf-form--grow">
-			<label class="gf-form-label gf-form-label--grow"></label>
-		</div>
-		<div class="gf-form" ng-if="ctrl.queryOptions">
-			<a class="gf-form-label" ng-click="ctrl.toggleOptions()">
-				<i class="fa fa-fw fa-caret-right" ng-hide="ctrl.optionsOpen"></i><i class="fa fa-fw fa-caret-down" ng-show="ctrl.optionsOpen"></i>Options
-			</a>
-		</div>
-		<div class="gf-form" ng-if="ctrl.hasQueryHelp">
-			<button class="gf-form-label" ng-click="ctrl.toggleHelp()">
-				<i class="fa fa-fw fa-caret-right" ng-hide="ctrl.helpOpen"></i><i class="fa fa-fw fa-caret-down" ng-show="ctrl.helpOpen"></i>Help
-			</button>
-		</div>
-		<div class="gf-form">
-			<button class="gf-form-label" ng-click="ctrl.toggleQueryTroubleshooter()" bs-tooltip="'Display query request & response'">
-				<i class="fa fa-fw fa-caret-right" ng-hide="ctrl.queryTroubleshooterOpen"></i><i class="fa fa-fw fa-caret-down" ng-show="ctrl.queryTroubleshooterOpen"></i>Query Inspector
-			</button>
-		</div>
-	</div>
-
-	<div>
-		<div ng-if="ctrl.optionsOpen">
-			<div class="gf-form gf-form--flex-end" ng-if="ctrl.queryOptions.minInterval">
-				<label class="gf-form-label">Min time interval</label>
-				<input type="text" class="gf-form-input width-6" placeholder="{{ctrl.panelCtrl.interval}}" ng-model="ctrl.panel.interval" spellcheck="false" ng-model-onblur ng-change="ctrl.panelCtrl.refresh()" />
-				<info-popover mode="right-absolute">
-					A lower limit for the auto group by time interval. Recommended to be set to write frequency,
-					for example <code>1m</code> if your data is written every minute. Access auto interval via variable <code>$__interval</code> for time range
-					string and <code>$__interval_ms</code> for numeric variable that can be used in math expressions.
-				</info-popover>
-			</div>
-			<div class="gf-form gf-form--flex-end" ng-if="ctrl.queryOptions.cacheTimeout">
-				<label class="gf-form-label width-9">Cache timeout</label>
-				<input  type="text" class="gf-form-input width-6" placeholder="60" ng-model="ctrl.panel.cacheTimeout" ng-model-onblur ng-change="ctrl.panelCtrl.refresh()" spellcheck="false" />
-				<info-popover mode="right-absolute">
-					If your time series store has a query cache this option can override the default
-					cache timeout. Specify a numeric value in seconds.
-				</info-popover>
-			</div>
-			<div class="gf-form gf-form--flex-end" ng-if="ctrl.queryOptions.maxDataPoints">
-				<label class="gf-form-label width-9">Max data points</label>
-				<input type="text" class="gf-form-input width-6" placeholder="auto" ng-model-onblur ng-change="ctrl.panelCtrl.refresh()" ng-model="ctrl.panel.maxDataPoints" spellcheck="false"  />
-				<info-popover mode="right-absolute">
-					The maximum data points the query should return. For graphs this
-					is automatically set to one data point per pixel.
-				</info-popover>
-			</div>
-		</div>
-
-		<div class="grafana-info-box" ng-if="ctrl.helpOpen">
-			<div class="markdown-html" ng-bind-html="ctrl.helpHtml"></div>
-			<a class="grafana-info-box__close" ng-click="ctrl.toggleHelp()">
-				<i class="fa fa-chevron-up"></i>
-			</a>
-		</div>
-
-		<query-troubleshooter panel-ctrl="ctrl.panelCtrl" is-open="ctrl.queryTroubleshooterOpen"></query-troubleshooter>
-	</div>
-</div>
 
 <div class="query-editor-rows gf-form-group" ng-if="ctrl.datasourceInstance">
 	<div ng-repeat="target in ctrl.panel.targets" ng-class="{'gf-form-disabled': target.hide}">

+ 9 - 0
public/app/plugins/datasource/logging/language_provider.test.ts

@@ -95,5 +95,14 @@ describe('Query imports', () => {
       const result = await instance.importPrometheusQuery('metric{foo="bar",baz="42"}');
       expect(result).toEqual('{foo="bar"}');
     });
+
+    it('returns selector query from selector query with all labels if logging label list is empty', async () => {
+      const datasourceWithLabels = {
+        metadataRequest: url => (url === '/api/prom/label' ? { data: { data: [] } } : { data: { data: [] } }),
+      };
+      const instance = new LanguageProvider(datasourceWithLabels);
+      const result = await instance.importPrometheusQuery('metric{foo="bar",baz="42"}');
+      expect(result).toEqual('{baz="42",foo="bar"}');
+    });
   });
 });

+ 18 - 10
public/app/plugins/datasource/logging/language_provider.ts

@@ -97,9 +97,10 @@ export default class LoggingLanguageProvider extends LanguageProvider {
 
     if (history && history.length > 0) {
       const historyItems = _.chain(history)
-        .uniqBy('query.expr')
-        .take(HISTORY_ITEM_COUNT)
         .map(h => h.query.expr)
+        .filter()
+        .uniq()
+        .take(HISTORY_ITEM_COUNT)
         .map(wrapLabel)
         .map(item => addHistoryMetadata(item, history))
         .value();
@@ -194,17 +195,24 @@ export default class LoggingLanguageProvider extends LanguageProvider {
 
       // Keep only labels that exist on origin and target datasource
       await this.start(); // fetches all existing label keys
-      const commonLabels = {};
-      for (const key in labels) {
-        const existingKeys = this.labelKeys[EMPTY_SELECTOR];
-        if (existingKeys && existingKeys.indexOf(key) > -1) {
-          // Should we check for label value equality here?
-          commonLabels[key] = labels[key];
+      const existingKeys = this.labelKeys[EMPTY_SELECTOR];
+      let labelsToKeep = {};
+      if (existingKeys && existingKeys.length > 0) {
+        // Check for common labels
+        for (const key in labels) {
+          if (existingKeys && existingKeys.indexOf(key) > -1) {
+            // Should we check for label value equality here?
+            labelsToKeep[key] = labels[key];
+          }
         }
+      } else {
+        // Keep all labels by default
+        labelsToKeep = labels;
       }
-      const labelKeys = Object.keys(commonLabels).sort();
+
+      const labelKeys = Object.keys(labelsToKeep).sort();
       const cleanSelector = labelKeys
-        .map(key => `${key}${commonLabels[key].operator}${commonLabels[key].value}`)
+        .map(key => `${key}${labelsToKeep[key].operator}${labelsToKeep[key].value}`)
         .join(',');
 
       return ['{', cleanSelector, '}'].join('');

+ 3 - 2
public/app/plugins/datasource/prometheus/language_provider.ts

@@ -125,9 +125,10 @@ export default class PromQlLanguageProvider extends LanguageProvider {
 
     if (history && history.length > 0) {
       const historyItems = _.chain(history)
-        .uniqBy('query.expr')
-        .take(HISTORY_ITEM_COUNT)
         .map(h => h.query.expr)
+        .filter()
+        .uniq()
+        .take(HISTORY_ITEM_COUNT)
         .map(wrapLabel)
         .map(item => addHistoryMetadata(item, history))
         .value();

+ 9 - 0
public/img/panel-tabs/alert-selected.svg

@@ -0,0 +1,9 @@
+<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="22" cy="22" r="20" fill="#09090B" stroke="#8E8E8E" stroke-width="4"/>
+<line x1="12.5925" y1="28.1482" x2="31.4073" y2="28.1482" stroke="#8E8E8E" stroke-width="4" stroke-linecap="round"/>
+<ellipse cx="22.0001" cy="20.3704" rx="8.96296" ry="9.77778" fill="#8E8E8E"/>
+<circle cx="22.0001" cy="30.1482" r="2.44444" fill="#8E8E8E"/>
+<line x1="18.7407" y1="30.463" x2="25.2592" y2="30.463" stroke="#09090B"/>
+<rect x="13.0371" y="20.3704" width="17.9259" height="6.51852" fill="#8E8E8E"/>
+<ellipse cx="22.0001" cy="11.0001" rx="0.814815" ry="1.22222" fill="#8E8E8E"/>
+</svg>

+ 9 - 0
public/img/panel-tabs/alert.svg

@@ -0,0 +1,9 @@
+<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="22" cy="22" r="20" fill="#09090B" stroke="#8E8E8E" stroke-width="4"/>
+<line x1="12.5925" y1="28.1482" x2="31.4073" y2="28.1482" stroke="#8E8E8E" stroke-width="4" stroke-linecap="round"/>
+<ellipse cx="22.0001" cy="20.3704" rx="8.96296" ry="9.77778" fill="#8E8E8E"/>
+<circle cx="22.0001" cy="30.1482" r="2.44444" fill="#8E8E8E"/>
+<line x1="18.7407" y1="30.463" x2="25.2592" y2="30.463" stroke="#09090B"/>
+<rect x="13.0371" y="20.3704" width="17.9259" height="6.51852" fill="#8E8E8E"/>
+<ellipse cx="22.0001" cy="11.0001" rx="0.814815" ry="1.22222" fill="#8E8E8E"/>
+</svg>

+ 31 - 0
public/img/panel-tabs/general-selected.svg

@@ -0,0 +1,31 @@
+<svg width="65" height="54" viewBox="0 0 65 54" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="27" cy="27" r="25" fill="#09090B" stroke="url(#paint0_linear)" stroke-width="4"/>
+<ellipse cx="27" cy="33" rx="14" ry="6" fill="url(#paint1_linear)"/>
+<ellipse cx="27" cy="30" rx="14" ry="6" fill="#09090B"/>
+<ellipse cx="27" cy="27" rx="14" ry="6" fill="url(#paint2_linear)"/>
+<ellipse cx="27" cy="24" rx="14" ry="6" fill="#09090B"/>
+<ellipse cx="27" cy="21" rx="14" ry="6" fill="url(#paint3_linear)"/>
+<path d="M52 37L65 26.9999L52 17L52 37Z" fill="url(#paint4_linear)"/>
+<defs>
+<linearGradient id="paint0_linear" x1="27" y1="0" x2="27" y2="54" gradientUnits="userSpaceOnUse">
+<stop stop-color="#D44A3A"/>
+<stop offset="1" stop-color="#EB7B18"/>
+</linearGradient>
+<linearGradient id="paint1_linear" x1="27" y1="2.01165e-07" x2="27" y2="54" gradientUnits="userSpaceOnUse">
+<stop stop-color="#D44A3A"/>
+<stop offset="1" stop-color="#FEBC11"/>
+</linearGradient>
+<linearGradient id="paint2_linear" x1="27" y1="1.40816e-06" x2="27" y2="54" gradientUnits="userSpaceOnUse">
+<stop stop-color="#D44A3A"/>
+<stop offset="1" stop-color="#FEBC11"/>
+</linearGradient>
+<linearGradient id="paint3_linear" x1="27" y1="-6.03497e-07" x2="27" y2="54" gradientUnits="userSpaceOnUse">
+<stop stop-color="#D44A3A"/>
+<stop offset="1" stop-color="#FEBC11"/>
+</linearGradient>
+<linearGradient id="paint4_linear" x1="57" y1="54" x2="57" y2="7.55191e-06" gradientUnits="userSpaceOnUse">
+<stop stop-color="#EB7B18"/>
+<stop offset="1" stop-color="#D44A3A"/>
+</linearGradient>
+</defs>
+</svg>

+ 8 - 0
public/img/panel-tabs/general.svg

@@ -0,0 +1,8 @@
+<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="22" cy="22" r="20" fill="#09090B" stroke="#8E8E8E" stroke-width="4"/>
+<ellipse cx="21.9999" cy="26.8889" rx="11.4074" ry="4.88889" fill="#8E8E8E"/>
+<ellipse cx="21.9999" cy="24.4444" rx="11.4074" ry="4.88889" fill="#09090B"/>
+<ellipse cx="21.9999" cy="22" rx="11.4074" ry="4.88889" fill="#8E8E8E"/>
+<ellipse cx="21.9999" cy="19.5555" rx="11.4074" ry="4.88889" fill="#09090B"/>
+<ellipse cx="21.9999" cy="17.1111" rx="11.4074" ry="4.88889" fill="#8E8E8E"/>
+</svg>

+ 31 - 0
public/img/panel-tabs/queries-selected.svg

@@ -0,0 +1,31 @@
+<svg width="65" height="54" viewBox="0 0 65 54" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="27" cy="27" r="25" fill="#09090B" stroke="url(#paint0_linear)" stroke-width="4"/>
+<ellipse cx="27" cy="33" rx="14" ry="6" fill="url(#paint1_linear)"/>
+<ellipse cx="27" cy="30" rx="14" ry="6" fill="#09090B"/>
+<ellipse cx="27" cy="27" rx="14" ry="6" fill="url(#paint2_linear)"/>
+<ellipse cx="27" cy="24" rx="14" ry="6" fill="#09090B"/>
+<ellipse cx="27" cy="21" rx="14" ry="6" fill="url(#paint3_linear)"/>
+<path d="M52 37L65 26.9999L52 17L52 37Z" fill="url(#paint4_linear)"/>
+<defs>
+<linearGradient id="paint0_linear" x1="27" y1="0" x2="27" y2="54" gradientUnits="userSpaceOnUse">
+<stop stop-color="#D44A3A"/>
+<stop offset="1" stop-color="#EB7B18"/>
+</linearGradient>
+<linearGradient id="paint1_linear" x1="27" y1="2.01165e-07" x2="27" y2="54" gradientUnits="userSpaceOnUse">
+<stop stop-color="#D44A3A"/>
+<stop offset="1" stop-color="#FEBC11"/>
+</linearGradient>
+<linearGradient id="paint2_linear" x1="27" y1="1.40816e-06" x2="27" y2="54" gradientUnits="userSpaceOnUse">
+<stop stop-color="#D44A3A"/>
+<stop offset="1" stop-color="#FEBC11"/>
+</linearGradient>
+<linearGradient id="paint3_linear" x1="27" y1="-6.03497e-07" x2="27" y2="54" gradientUnits="userSpaceOnUse">
+<stop stop-color="#D44A3A"/>
+<stop offset="1" stop-color="#FEBC11"/>
+</linearGradient>
+<linearGradient id="paint4_linear" x1="57" y1="54" x2="57" y2="7.55191e-06" gradientUnits="userSpaceOnUse">
+<stop stop-color="#EB7B18"/>
+<stop offset="1" stop-color="#D44A3A"/>
+</linearGradient>
+</defs>
+</svg>

+ 8 - 0
public/img/panel-tabs/queries.svg

@@ -0,0 +1,8 @@
+<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="22" cy="22" r="20" fill="#09090B" stroke="#8E8E8E" stroke-width="4"/>
+<ellipse cx="21.9999" cy="26.8889" rx="11.4074" ry="4.88889" fill="#8E8E8E"/>
+<ellipse cx="21.9999" cy="24.4444" rx="11.4074" ry="4.88889" fill="#09090B"/>
+<ellipse cx="21.9999" cy="22" rx="11.4074" ry="4.88889" fill="#8E8E8E"/>
+<ellipse cx="21.9999" cy="19.5555" rx="11.4074" ry="4.88889" fill="#09090B"/>
+<ellipse cx="21.9999" cy="17.1111" rx="11.4074" ry="4.88889" fill="#8E8E8E"/>
+</svg>

+ 34 - 0
public/img/panel-tabs/visualization-selected.svg

@@ -0,0 +1,34 @@
+<svg width="65" height="54" viewBox="0 0 65 54" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="27" cy="27" r="25" fill="#09090B" stroke="url(#paint0_linear)" stroke-width="4"/>
+<path d="M39 21L35 17H39V21Z" fill="url(#paint1_linear)"/>
+<line x1="14" y1="15" x2="14" y2="39" stroke="url(#paint2_linear)" stroke-width="2"/>
+<line x1="41" y1="38" x2="14" y2="38" stroke="url(#paint3_linear)" stroke-width="2"/>
+<path d="M37 19L28 27L25 24L16 33" stroke="url(#paint4_linear)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M52 37L65 26.9999L52 17L52 37Z" fill="url(#paint5_linear)"/>
+<defs>
+<linearGradient id="paint0_linear" x1="27" y1="0" x2="27" y2="54" gradientUnits="userSpaceOnUse">
+<stop stop-color="#D44A3A"/>
+<stop offset="1" stop-color="#EB7B18"/>
+</linearGradient>
+<linearGradient id="paint1_linear" x1="37" y1="-9.62518e-07" x2="37" y2="54" gradientUnits="userSpaceOnUse">
+<stop stop-color="#D44A3A"/>
+<stop offset="1" stop-color="#EB7B18"/>
+</linearGradient>
+<linearGradient id="paint2_linear" x1="13" y1="53.5" x2="13" y2="1.5" gradientUnits="userSpaceOnUse">
+<stop stop-color="#EB7B18"/>
+<stop offset="1" stop-color="#D44A3A"/>
+</linearGradient>
+<linearGradient id="paint3_linear" x1="27" y1="7.21113e-07" x2="27.5" y2="54" gradientUnits="userSpaceOnUse">
+<stop stop-color="#D44A3A"/>
+<stop offset="1" stop-color="#EB7B18"/>
+</linearGradient>
+<linearGradient id="paint4_linear" x1="27" y1="-4.4462e-08" x2="27" y2="54" gradientUnits="userSpaceOnUse">
+<stop stop-color="#D44A3A"/>
+<stop offset="1" stop-color="#EB7B18"/>
+</linearGradient>
+<linearGradient id="paint5_linear" x1="57" y1="54" x2="57" y2="7.55191e-06" gradientUnits="userSpaceOnUse">
+<stop stop-color="#EB7B18"/>
+<stop offset="1" stop-color="#D44A3A"/>
+</linearGradient>
+</defs>
+</svg>

+ 7 - 0
public/img/panel-tabs/visualization.svg

@@ -0,0 +1,7 @@
+<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="22" cy="22" r="20" fill="#09090B" stroke="#8E8E8E" stroke-width="4"/>
+<path d="M31.7778 17.1111L28.5186 13.8518H31.7778V17.1111Z" fill="#8E8E8E"/>
+<line x1="11" y1="12" x2="11" y2="32" stroke="#8E8E8E" stroke-width="2"/>
+<line x1="33" y1="31" x2="11" y2="31" stroke="#8E8E8E" stroke-width="2"/>
+<path d="M30.1482 15.4814L22.8149 22L20.3704 19.5555L13.0371 26.8889" stroke="#8E8E8E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 3 - 4
public/sass/_variables.dark.scss

@@ -79,6 +79,7 @@ $brand-gradient: linear-gradient(
 );
 
 $page-gradient: linear-gradient(180deg, #222426 10px, rgb(22, 23, 25) 100px);
+$edit-gradient: linear-gradient(180deg, rgb(22, 23, 25) 00px, #090909 60%);
 
 // Links
 // -------------------------
@@ -268,9 +269,7 @@ $menu-dropdown-shadow: 5px 5px 20px -5px $black;
 $tab-border-color: $dark-4;
 
 // Toolbar
-$toolbar-bg: $page-header-bg;
-$toolbar-shadow: 0 0 20px black;
-$toolbar-tab-bg: $gray-blue;
+$toolbar-bg: $black;
 
 // Pagination
 // -------------------------
@@ -367,6 +366,6 @@ $switch-slider-shadow: 0 0 3px black;
 //Checkbox
 // -------------------------
 $checkbox-bg: $dark-1;
-$checkbox-border: 1px solid $gray-2;
+$checkbox-border: 1px solid $gray-1;
 $checkbox-checked-bg: linear-gradient(0deg, $orange, $red);
 $checkbox-color: $dark-1;

+ 2 - 3
public/sass/_variables.light.scss

@@ -76,6 +76,7 @@ $textShadow: none;
 // gradients
 $brand-gradient: linear-gradient(to right, rgba(255, 213, 0, 1) 0%, rgba(255, 68, 0, 1) 99%, rgba(255, 68, 0, 1) 100%);
 $page-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%);
+$edit-gradient: linear-gradient(-60deg, $gray-7, #f5f6f9 70%, $gray-7 98%);
 
 // Links
 // -------------------------
@@ -215,9 +216,7 @@ $menu-dropdown-shadow: 5px 5px 10px -5px $gray-1;
 $tab-border-color: $gray-5;
 
 // Toolbar
-$toolbar-bg: linear-gradient(90deg, #ffffff, #e6eef9);
-$toolbar-shadow: 1px 1px 3px #c7d0d8;
-$toolbar-tab-bg: $white;
+$toolbar-bg: white;
 
 // search
 $search-shadow: 0 5px 30px 0 $gray-4;

+ 2 - 1
public/sass/components/_footer.scss

@@ -4,7 +4,7 @@
 
 .footer {
   color: $footer-link-color;
-  padding: 5rem 0 1rem 0;
+  padding: 1rem 0 1rem 0;
   font-size: $font-size-sm;
   position: relative;
   width: 98%; /* was causing horiz scrollbars - need to examine */
@@ -38,6 +38,7 @@
   }
 }
 
+// Keeping footer inside the graphic on Login screen
 .login-page {
   .footer {
     bottom: $spacer;

+ 2 - 2
public/sass/components/_gf-form.scss

@@ -82,7 +82,7 @@ $input-border: 1px solid $input-border-color;
   align-content: flex-start;
 
   .gf-form + .gf-form {
-    margin-right: $gf-form-margin;
+    margin-left: $gf-form-margin;
   }
 }
 
@@ -163,7 +163,6 @@ $input-border: 1px solid $input-border-color;
   width: 100%;
   height: $gf-form-input-height;
   padding: $input-padding-y $input-padding-x;
-  margin-right: $gf-form-margin;
   font-size: $font-size-md;
   line-height: $input-line-height;
   color: $input-color;
@@ -181,6 +180,7 @@ $input-border: 1px solid $input-border-color;
   @at-root textarea#{&} {
     overflow: auto;
     white-space: pre-wrap;
+    height: auto;
   }
 
   // Unstyle the caret on `<select>`s in IE10+.

+ 75 - 19
public/sass/components/_panel_editor.scss

@@ -24,11 +24,40 @@
 .panel-editor-container__editor {
   margin-top: $panel-margin*2;
   display: flex;
-  flex-direction: column;
-  height: 65%;
+  flex-direction: row;
+  height: 60%;
   position: relative;
 }
 
+.panel-editor__right {
+  display: flex;
+  flex-direction: column;
+  flex-grow: 1;
+  background: $page-bg;
+  margin: 0 62px;
+  border-left: 2px solid #ac5224;
+  border-radius: 3px;
+  box-shadow: $popover-shadow;
+}
+
+.panel-editor__close {
+  @include buttonBackground($btn-inverse-bg, $btn-inverse-bg-hl);
+  position: absolute;
+  left: 11px;
+  top: 5px;
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+
+  i {
+    flex-grow: 1;
+    text-align: center;
+    font-size: 20px;
+  }
+}
+
 .panel-editor__scroll {
   flex-grow: 1;
   min-width: 0;
@@ -39,6 +68,11 @@
   padding: 40px 20px;
 }
 
+.panel-editor__toolbar-view {
+  background: $black;
+  padding: 20px;
+}
+
 .panel-in-fullscreen {
   .sidemenu {
     display: none;
@@ -106,9 +140,8 @@
 }
 
 .viz-picker__item {
-  background: $card-background;
-  box-shadow: $card-shadow;
-
+  background: $panel-bg;
+  border: $panel-border;
   border-radius: 3px;
   height: 90px;
   width: 150px;
@@ -149,22 +182,46 @@
 .panel-editor-tabs {
   position: relative;
   z-index: 2;
-  box-shadow: $page-header-shadow;
-  border-bottom: 1px solid $page-header-border-color;
-  padding: 0 $dashboard-padding;
+  display: flex;
+  flex-direction: column;
+  position: absolute;
+  top: 79px;
+  left: 9px;
+  align-items: flex-start;
+
+  &::before {
+    content: '';
+    display: block;
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 21px;
+    width: 2px;
+    background: #8e8e8e;
+  }
+}
 
-  @include clearfix();
+.panel-editor-tabs__item {
+  margin-bottom: 25px;
+  position: relative;
+  z-index: 1;
+  text-align: center;
 
-  .active.gf-tabs-link {
-    background: $toolbar-tab-bg;
+  &:last-child {
+    margin-bottom: 0;
   }
 }
 
-.panel-editor-tabs__close {
-  padding: 5px 9px;
-  border-radius: $border-radius;
-  float: right;
-  @include buttonBackground($btn-primary-bg, $btn-primary-bg-hl);
+.panel-editor-tabs__link {
+  display: inline-block;
+
+  &.active {
+    position: relative;
+  }
+
+  img {
+    height: 44px;
+  }
 }
 
 .ds-picker-list {
@@ -175,9 +232,8 @@
 }
 
 .ds-picker-list__item {
-  background: $card-background;
-  box-shadow: $card-shadow;
-
+  background: $panel-bg;
+  border: $panel-border;
   border-radius: 3px;
   display: flex;
   cursor: pointer;

+ 1 - 1
public/sass/components/_query_editor.scss

@@ -35,7 +35,7 @@
   }
 
   .gf-form + .gf-form {
-    margin-right: 0;
+    margin-left: 0;
   }
 }
 

+ 10 - 12
public/sass/components/_toolbar.scss

@@ -2,12 +2,18 @@
   display: flex;
   align-content: center;
   align-items: center;
-  background: $toolbar-bg;
-  box-shadow: $toolbar-shadow;
-  padding: 7px 20px 7px 20px;
+  padding: 3px 20px 3px 20px;
   position: relative;
   z-index: 1;
   flex: 0 0 auto;
+  background: $toolbar-bg;
+  border-radius: 3px;
+  height: 44px;
+}
+
+.toolbar__heading {
+  font-size: $font-size-lg;
+  padding-right: 20px;
 }
 
 .toolbar__main {
@@ -36,20 +42,12 @@
   height: 20px;
 }
 
-.toolbar-subview {
-  position: relative;
-  padding: 20px 20px;
-  background-color: $empty-list-cta-bg;
-  top: -45px;
-  margin: 0 30px 20px 0px;
-}
-
 .toolbar-subview__close {
   background: transparent;
   padding: 4px 8px 4px 9px;
   border: none;
   position: absolute;
-  right: 15px;
+  right: 25px;
   top: 20px;
   font-size: $font-size-md;
 

+ 29 - 0
public/sass/layout/_page.scss

@@ -14,6 +14,12 @@
   background: $page-gradient;
 }
 
+.panel-in-fullscreen {
+  .main-view {
+    background: $edit-gradient;
+  }
+}
+
 .page-container {
   margin-left: auto;
   margin-right: auto;
@@ -40,6 +46,29 @@
   &--dashboard {
     height: calc(100% - 56px);
   }
+
+  // Sticky footer
+  display: flex;
+  flex-direction: column;
+
+  > div {
+    flex-grow: 1;
+  }
+
+  > .footer {
+    flex-shrink: 0;
+  }
+
+  // Render in correct position even ng-view div is not rendered yet
+  > .footer:first-child {
+    flex-grow: 1;
+    display: flex;
+
+    > * {
+      width: 100%;
+      align-self: flex-end;
+    }
+  }
 }
 
 // fix for phantomjs