Просмотр исходного кода

Merge branch 'master' into storybook/valuemappingseditor

Peter Holmberg 6 лет назад
Родитель
Сommit
a4d35e2fbc

+ 37 - 15
README.md

@@ -25,49 +25,71 @@ the latest master builds [here](https://grafana.com/grafana/download)
 ### Dependencies
 ### Dependencies
 
 
 - Go (Latest Stable)
 - Go (Latest Stable)
+  - bra [`go get github.com/Unknwon/bra`]
 - Node.js LTS
 - Node.js LTS
+  - yarn [`npm install -g yarn`]
+
+### Get the project
+
+**The project located in the go-path will be your working directory.**
 
 
-### Building the backend
 ```bash
 ```bash
 go get github.com/grafana/grafana
 go get github.com/grafana/grafana
 cd $GOPATH/src/github.com/grafana/grafana
 cd $GOPATH/src/github.com/grafana/grafana
+```
+
+### Building
+
+#### The backend
+
+```bash
 go run build.go setup
 go run build.go setup
 go run build.go build
 go run build.go build
 ```
 ```
 
 
-### Building frontend assets
+#### Frontend assets
 
 
-For this you need Node.js (LTS version).
+*For this you need Node.js (LTS version).*
 
 
-To build the assets, rebuild on file change, and serve them by Grafana's webserver (http://localhost:3000):
 ```bash
 ```bash
-npm install -g yarn
 yarn install --pure-lockfile
 yarn install --pure-lockfile
+```
+
+### Run and rebuild on source change
+
+#### Backend
+
+To run the backend and rebuild on source change:
+
+```bash
+$GOPATH/bin/bra run
+```
+
+#### Frontend
+
+Rebuild on file change, and serve them by Grafana's webserver (http://localhost:3000):
+
+```bash
 yarn watch
 yarn watch
 ```
 ```
 
 
 Build the assets, rebuild on file change with Hot Module Replacement (HMR), and serve them by webpack-dev-server (http://localhost:3333):
 Build the assets, rebuild on file change with Hot Module Replacement (HMR), and serve them by webpack-dev-server (http://localhost:3333):
+
 ```bash
 ```bash
 yarn start
 yarn start
 # OR set a theme
 # OR set a theme
 env GRAFANA_THEME=light yarn start
 env GRAFANA_THEME=light yarn start
 ```
 ```
-Note: HMR for Angular is not supported. If you edit files in the Angular part of the app, the whole page will reload.
 
 
-Run tests
-```bash
-yarn jest
-```
+*Note: HMR for Angular is not supported. If you edit files in the Angular part of the app, the whole page will reload.*
 
 
-### Recompile backend on source change
+Run tests and rebuild on source change:
 
 
-To rebuild on source change.
 ```bash
 ```bash
-go get github.com/Unknwon/bra
-bra run
+yarn jest
 ```
 ```
 
 
-Open grafana in your browser (default: `http://localhost:3000`) and login with admin user (default: `user/pass = admin/admin`).
+**Open grafana in your browser (default: e.g. `http://localhost:3000`) and login with admin user (default: `user/pass = admin/admin`).**
 
 
 ### Building a Docker image
 ### Building a Docker image
 
 

+ 3 - 0
conf/defaults.ini

@@ -113,6 +113,9 @@ cache_mode = private
 # Login cookie name
 # Login cookie name
 cookie_name = grafana_session
 cookie_name = grafana_session
 
 
+# Login cookie same site setting. defaults to `lax`. can be set to "lax", "strict" and "none"
+cookie_samesite = lax
+
 # How many days an session can be unused before we inactivate it
 # How many days an session can be unused before we inactivate it
 login_remember_days = 7
 login_remember_days = 7
 
 

+ 3 - 0
conf/sample.ini

@@ -109,6 +109,9 @@ log_queries =
 # Login cookie name
 # Login cookie name
 ;cookie_name = grafana_session
 ;cookie_name = grafana_session
 
 
+# Login cookie same site setting. defaults to `lax`. can be set to "lax", "strict" and "none"
+;cookie_samesite = lax
+
 # How many days an session can be unused before we inactivate it
 # How many days an session can be unused before we inactivate it
 ;login_remember_days = 7
 ;login_remember_days = 7
 
 

+ 1 - 0
packages/grafana-ui/src/components/ColorPicker/_ColorPicker.scss

@@ -167,6 +167,7 @@ $arrowSize: 15px;
   color: inherit;
   color: inherit;
   padding: 0;
   padding: 0;
   border-radius: 10px;
   border-radius: 10px;
+  cursor: pointer;
 }
 }
 
 
 .sp-replacer:hover,
 .sp-replacer:hover,

+ 23 - 11
packages/grafana-ui/src/components/PanelOptionsGroup/PanelOptionsGroup.tsx

@@ -1,26 +1,38 @@
 // Libraries
 // Libraries
-import React, { SFC } from 'react';
+import React, { FunctionComponent } from 'react';
 
 
 interface Props {
 interface Props {
   title?: string;
   title?: string;
   onClose?: () => void;
   onClose?: () => void;
-  children: JSX.Element | JSX.Element[];
+  children: JSX.Element | JSX.Element[] | boolean;
+  onAdd?: () => void;
 }
 }
 
 
-export const PanelOptionsGroup: SFC<Props> = props => {
+export const PanelOptionsGroup: FunctionComponent<Props> = props => {
   return (
   return (
     <div className="panel-options-group">
     <div className="panel-options-group">
-      {props.title && (
+      {props.onAdd ? (
         <div className="panel-options-group__header">
         <div className="panel-options-group__header">
-          {props.title}
-          {props.onClose && (
-            <button className="btn btn-link" onClick={props.onClose}>
-              <i className="fa fa-remove" />
-            </button>
-          )}
+          <button className="panel-options-group__add-btn" onClick={props.onAdd}>
+            <div className="panel-options-group__add-circle">
+              <i className="fa fa-plus" />
+            </div>
+            <span className="panel-options-group__title">{props.title}</span>
+          </button>
         </div>
         </div>
+      ) : (
+        props.title && (
+          <div className="panel-options-group__header">
+            <span className="panel-options-group__title">{props.title}</span>
+            {props.onClose && (
+              <button className="btn btn-link" onClick={props.onClose}>
+                <i className="fa fa-remove" />
+              </button>
+            )}
+          </div>
+        )
       )}
       )}
-      <div className="panel-options-group__body">{props.children}</div>
+      {props.children && <div className="panel-options-group__body">{props.children}</div>}
     </div>
     </div>
   );
   );
 };
 };

+ 41 - 2
packages/grafana-ui/src/components/PanelOptionsGroup/_PanelOptionsGroup.scss

@@ -7,18 +7,57 @@
 
 
 .panel-options-group__header {
 .panel-options-group__header {
   padding: 4px 8px;
   padding: 4px 8px;
-  font-size: 1.1rem;
   background: $panel-options-group-header-bg;
   background: $panel-options-group-header-bg;
   position: relative;
   position: relative;
   border-radius: $border-radius $border-radius 0 0;
   border-radius: $border-radius $border-radius 0 0;
+  display: flex;
+  align-items: center;
 
 
   .btn {
   .btn {
     position: absolute;
     position: absolute;
     right: 0;
     right: 0;
-    top: 0px;
+    top: 0;
+  }
+}
+
+.panel-options-group__add-btn {
+  background: none;
+  border: none;
+  display: flex;
+  align-items: center;
+  padding: 0;
+
+  &:hover {
+    .panel-options-group__add-circle {
+      background-color: $btn-success-bg;
+      color: $text-color-strong;
+    }
+  }
+}
+
+.panel-options-group__add-circle {
+  @include gradientBar($btn-success-bg, $btn-success-bg-hl, $text-color);
+
+  border-radius: 50px;
+  width: 20px;
+  height: 20px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 6px;
+
+  i {
+    position: relative;
+    top: 1px;
   }
   }
 }
 }
 
 
+.panel-options-group__title {
+  font-size: 1.1rem;
+  position: relative;
+  top: 1px;
+}
+
 .panel-options-group__body {
 .panel-options-group__body {
   padding: 20px;
   padding: 20px;
 
 

+ 7 - 7
packages/grafana-ui/src/components/ThresholdsEditor/_ThresholdsEditor.scss

@@ -1,11 +1,11 @@
 .thresholds {
 .thresholds {
-  margin-bottom: 10px;
+  margin-bottom: 20px;
 }
 }
 
 
 .thresholds-row {
 .thresholds-row {
   display: flex;
   display: flex;
   flex-direction: row;
   flex-direction: row;
-  height: 70px;
+  height: 62px;
 }
 }
 
 
 .thresholds-row:first-child > .thresholds-row-color-indicator {
 .thresholds-row:first-child > .thresholds-row-color-indicator {
@@ -21,21 +21,21 @@
 }
 }
 
 
 .thresholds-row-add-button {
 .thresholds-row-add-button {
+  @include buttonBackground($btn-success-bg, $btn-success-bg-hl, $text-color);
+
   align-self: center;
   align-self: center;
   margin-right: 5px;
   margin-right: 5px;
-  color: $green;
   height: 24px;
   height: 24px;
   width: 24px;
   width: 24px;
-  background-color: $green;
   border-radius: 50%;
   border-radius: 50%;
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
   justify-content: center;
   justify-content: center;
   cursor: pointer;
   cursor: pointer;
-}
 
 
-.thresholds-row-add-button > i {
-  color: $white;
+  &:hover {
+    color: $text-color-strong;
+  }
 }
 }
 
 
 .thresholds-row-color-indicator {
 .thresholds-row-color-indicator {

+ 1 - 1
packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.test.tsx

@@ -2,7 +2,7 @@ import React from 'react';
 import { shallow } from 'enzyme';
 import { shallow } from 'enzyme';
 
 
 import { ValueMappingsEditor, Props } from './ValueMappingsEditor';
 import { ValueMappingsEditor, Props } from './ValueMappingsEditor';
-import { MappingType } from '../../types/panel';
+import { MappingType } from '../../types';
 
 
 const setup = (propOverrides?: object) => {
 const setup = (propOverrides?: object) => {
   const props: Props = {
   const props: Props = {

+ 3 - 11
packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx

@@ -1,8 +1,8 @@
 import React, { PureComponent } from 'react';
 import React, { PureComponent } from 'react';
 
 
 import MappingRow from './MappingRow';
 import MappingRow from './MappingRow';
-import { MappingType, ValueMapping } from '../../types/panel';
-import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup';
+import { MappingType, ValueMapping } from '../../types';
+import { PanelOptionsGroup } from '..';
 
 
 export interface Props {
 export interface Props {
   valueMappings: ValueMapping[];
   valueMappings: ValueMapping[];
@@ -81,8 +81,7 @@ export class ValueMappingsEditor extends PureComponent<Props, State> {
     const { valueMappings } = this.state;
     const { valueMappings } = this.state;
 
 
     return (
     return (
-      <PanelOptionsGroup title="Value Mappings">
-        <div>
+      <PanelOptionsGroup title="Add value mapping" onAdd={this.addMapping}>
           {valueMappings.length > 0 &&
           {valueMappings.length > 0 &&
             valueMappings.map((valueMapping, index) => (
             valueMappings.map((valueMapping, index) => (
               <MappingRow
               <MappingRow
@@ -92,13 +91,6 @@ export class ValueMappingsEditor extends PureComponent<Props, State> {
                 removeValueMapping={() => this.onRemoveMapping(valueMapping.id)}
                 removeValueMapping={() => this.onRemoveMapping(valueMapping.id)}
               />
               />
             ))}
             ))}
-        </div>
-        <div className="add-mapping-row" onClick={this.addMapping}>
-          <div className="add-mapping-row-icon">
-            <i className="fa fa-plus" />
-          </div>
-          <div className="add-mapping-row-label">Add mapping</div>
-        </div>
       </PanelOptionsGroup>
       </PanelOptionsGroup>
     );
     );
   }
   }

+ 29 - 47
packages/grafana-ui/src/components/ValueMappingsEditor/__snapshots__/ValueMappingsEditor.test.tsx.snap

@@ -2,55 +2,37 @@
 
 
 exports[`Render should render component 1`] = `
 exports[`Render should render component 1`] = `
 <Component
 <Component
-  title="Value Mappings"
+  onAdd={[Function]}
+  title="Add value mapping"
 >
 >
-  <div>
-    <MappingRow
-      key="Ok-0"
-      removeValueMapping={[Function]}
-      updateValueMapping={[Function]}
-      valueMapping={
-        Object {
-          "id": 1,
-          "operator": "",
-          "text": "Ok",
-          "type": 1,
-          "value": "20",
-        }
+  <MappingRow
+    key="Ok-0"
+    removeValueMapping={[Function]}
+    updateValueMapping={[Function]}
+    valueMapping={
+      Object {
+        "id": 1,
+        "operator": "",
+        "text": "Ok",
+        "type": 1,
+        "value": "20",
       }
       }
-    />
-    <MappingRow
-      key="Meh-1"
-      removeValueMapping={[Function]}
-      updateValueMapping={[Function]}
-      valueMapping={
-        Object {
-          "from": "21",
-          "id": 2,
-          "operator": "",
-          "text": "Meh",
-          "to": "30",
-          "type": 2,
-        }
+    }
+  />
+  <MappingRow
+    key="Meh-1"
+    removeValueMapping={[Function]}
+    updateValueMapping={[Function]}
+    valueMapping={
+      Object {
+        "from": "21",
+        "id": 2,
+        "operator": "",
+        "text": "Meh",
+        "to": "30",
+        "type": 2,
       }
       }
-    />
-  </div>
-  <div
-    className="add-mapping-row"
-    onClick={[Function]}
-  >
-    <div
-      className="add-mapping-row-icon"
-    >
-      <i
-        className="fa fa-plus"
-      />
-    </div>
-    <div
-      className="add-mapping-row-label"
-    >
-      Add mapping
-    </div>
-  </div>
+    }
+  />
 </Component>
 </Component>
 `;
 `;

+ 1 - 0
pkg/services/auth/auth_token.go

@@ -96,6 +96,7 @@ func (s *UserAuthTokenServiceImpl) writeSessionCookie(ctx *models.ReqContext, va
 		Path:     setting.AppSubUrl + "/",
 		Path:     setting.AppSubUrl + "/",
 		Secure:   s.Cfg.SecurityHTTPSCookies,
 		Secure:   s.Cfg.SecurityHTTPSCookies,
 		MaxAge:   maxAge,
 		MaxAge:   maxAge,
+		SameSite: s.Cfg.LoginCookieSameSite,
 	}
 	}
 
 
 	http.SetCookie(ctx.Resp, &cookie)
 	http.SetCookie(ctx.Resp, &cookie)

+ 16 - 0
pkg/setting/setting.go

@@ -6,6 +6,7 @@ package setting
 import (
 import (
 	"bytes"
 	"bytes"
 	"fmt"
 	"fmt"
+	"net/http"
 	"net/url"
 	"net/url"
 	"os"
 	"os"
 	"path"
 	"path"
@@ -227,6 +228,7 @@ type Cfg struct {
 	LoginCookieMaxDays                int
 	LoginCookieMaxDays                int
 	LoginCookieRotation               int
 	LoginCookieRotation               int
 	LoginDeleteExpiredTokensAfterDays int
 	LoginDeleteExpiredTokensAfterDays int
+	LoginCookieSameSite               http.SameSite
 
 
 	SecurityHTTPSCookies bool
 	SecurityHTTPSCookies bool
 }
 }
@@ -557,6 +559,20 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
 	cfg.LoginCookieName = login.Key("cookie_name").MustString("grafana_session")
 	cfg.LoginCookieName = login.Key("cookie_name").MustString("grafana_session")
 	cfg.LoginCookieMaxDays = login.Key("login_remember_days").MustInt(7)
 	cfg.LoginCookieMaxDays = login.Key("login_remember_days").MustInt(7)
 	cfg.LoginDeleteExpiredTokensAfterDays = login.Key("delete_expired_token_after_days").MustInt(30)
 	cfg.LoginDeleteExpiredTokensAfterDays = login.Key("delete_expired_token_after_days").MustInt(30)
+
+	samesiteString := login.Key("cookie_samesite").MustString("lax")
+	validSameSiteValues := map[string]http.SameSite{
+		"lax":    http.SameSiteLaxMode,
+		"strict": http.SameSiteStrictMode,
+		"none":   http.SameSiteDefaultMode,
+	}
+
+	if samesite, ok := validSameSiteValues[samesiteString]; ok {
+		cfg.LoginCookieSameSite = samesite
+	} else {
+		cfg.LoginCookieSameSite = http.SameSiteLaxMode
+	}
+
 	cfg.LoginCookieRotation = login.Key("rotate_token_minutes").MustInt(10)
 	cfg.LoginCookieRotation = login.Key("rotate_token_minutes").MustInt(10)
 	if cfg.LoginCookieRotation < 2 {
 	if cfg.LoginCookieRotation < 2 {
 		cfg.LoginCookieRotation = 2
 		cfg.LoginCookieRotation = 2

+ 1 - 1
public/app/core/services/keybindingSrv.ts

@@ -249,7 +249,7 @@ export class KeybindingSrv {
         if (panelInfo.panel.legend) {
         if (panelInfo.panel.legend) {
           const panelRef = dashboard.getPanelById(dashboard.meta.focusPanelId);
           const panelRef = dashboard.getPanelById(dashboard.meta.focusPanelId);
           panelRef.legend.show = !panelRef.legend.show;
           panelRef.legend.show = !panelRef.legend.show;
-          panelRef.refresh();
+          panelRef.render();
         }
         }
       }
       }
     });
     });

+ 2 - 2
public/app/features/datasources/DataSourcesListItem.tsx

@@ -16,12 +16,12 @@ export class DataSourcesListItem extends PureComponent<Props> {
           </div>
           </div>
           <div className="card-item-body">
           <div className="card-item-body">
             <figure className="card-item-figure">
             <figure className="card-item-figure">
-              <img src={dataSource.typeLogoUrl} />
+              <img src={dataSource.typeLogoUrl} alt={dataSource.name} />
             </figure>
             </figure>
             <div className="card-item-details">
             <div className="card-item-details">
               <div className="card-item-name">
               <div className="card-item-name">
                 {dataSource.name}
                 {dataSource.name}
-                {dataSource.isDefault && <span className="btn btn-secondary btn-mini">default</span>}
+                {dataSource.isDefault && <span className="btn btn-secondary btn-mini card-item-label">default</span>}
               </div>
               </div>
               <div className="card-item-sub-name">{dataSource.url}</div>
               <div className="card-item-sub-name">{dataSource.url}</div>
             </div>
             </div>

+ 1 - 0
public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap

@@ -24,6 +24,7 @@ exports[`Render should render component 1`] = `
         className="card-item-figure"
         className="card-item-figure"
       >
       >
         <img
         <img
+          alt="gdev-cloudwatch"
           src="public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png"
           src="public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png"
         />
         />
       </figure>
       </figure>

+ 1 - 1
public/app/plugins/panel/graph/module.ts

@@ -281,7 +281,7 @@ class GraphCtrl extends MetricsPanelCtrl {
 
 
   toggleLegend() {
   toggleLegend() {
     this.panel.legend.show = !this.panel.legend.show;
     this.panel.legend.show = !this.panel.legend.show;
-    this.refresh();
+    this.render();
   }
   }
 
 
   legendValuesOptionChanged() {
   legendValuesOptionChanged() {

+ 1 - 1
public/app/plugins/panel/graph/tab_legend.html

@@ -3,7 +3,7 @@
 		<h5 class="section-heading">Options</h5>
 		<h5 class="section-heading">Options</h5>
 		<gf-form-switch class="gf-form"
 		<gf-form-switch class="gf-form"
 			label="Show" label-class="width-7"
 			label="Show" label-class="width-7"
-			checked="ctrl.panel.legend.show" on-change="ctrl.refresh()">
+			checked="ctrl.panel.legend.show" on-change="ctrl.render()">
 		</gf-form-switch>
 		</gf-form-switch>
 		<gf-form-switch class="gf-form"
 		<gf-form-switch class="gf-form"
 			label="As Table" label-class="width-7"
 			label="As Table" label-class="width-7"

+ 4 - 0
public/sass/components/_cards.scss

@@ -109,6 +109,10 @@
   width: 100%;
   width: 100%;
 }
 }
 
 
+.card-item-label {
+    margin-left: 8px;
+}
+
 .card-item-sub-name {
 .card-item-sub-name {
   color: $text-color-weak;
   color: $text-color-weak;
   overflow: hidden;
   overflow: hidden;