diff --git a/.circleci/config.yml b/.circleci/config.yml index f351040fe2f..f1d161c3cac 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -88,6 +88,9 @@ jobs: - run: name: run linters command: 'gometalinter.v2 --enable-gc --vendor --deadline 10m --disable-all --enable=deadcode --enable=ineffassign --enable=structcheck --enable=unconvert --enable=varcheck ./...' + - run: + name: run go vet + command: 'go vet ./pkg/...' test-frontend: docker: diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f9cc951e34..0ebb038546e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * **Postgres/MySQL/MSSQL**: Use floor rounding in $__timeGroup macro function [#12460](https://github.com/grafana/grafana/issues/12460), thx [@svenklemm](https://github.com/svenklemm) * **MySQL/MSSQL**: Use datetime format instead of epoch for $__timeFilter, $__timeFrom and $__timeTo macros [#11618](https://github.com/grafana/grafana/issues/11618) [#11619](https://github.com/grafana/grafana/issues/11619), thx [@AustinWinstanley](https://github.com/AustinWinstanley) * **Github OAuth**: Allow changes of user info at Github to be synched to Grafana when signing in [#11818](https://github.com/grafana/grafana/issues/11818), thx [@rwaweber](https://github.com/rwaweber) +* **Alerting**: Fix diff and percent_diff reducers [#11563](https://github.com/grafana/grafana/issues/11563), thx [@jessetane](https://github.com/jessetane) # 5.2.2 (unreleased) diff --git a/package.json b/package.json index 4f2220abbae..c26438230cc 100644 --- a/package.json +++ b/package.json @@ -149,17 +149,17 @@ "classnames": "^2.2.5", "clipboard": "^1.7.1", "d3": "^4.11.0", - "d3-scale-chromatic": "^1.1.1", + "d3-scale-chromatic": "^1.3.0", "eventemitter3": "^2.0.3", "file-saver": "^1.3.3", "immutable": "^3.8.2", "jquery": "^3.2.1", - "lodash": "^4.17.4", + "lodash": "^4.17.10", "mini-css-extract-plugin": "^0.4.0", "mobx": "^3.4.1", "mobx-react": "^4.3.5", "mobx-state-tree": "^1.3.1", - "moment": "^2.18.1", + "moment": "^2.22.2", "mousetrap": "^1.6.0", "mousetrap-global-bind": "^1.1.0", "optimize-css-assets-webpack-plugin": "^4.0.2", diff --git a/pkg/services/alerting/conditions/reducer.go b/pkg/services/alerting/conditions/reducer.go index 0a61c13fa12..1e8ae792746 100644 --- a/pkg/services/alerting/conditions/reducer.go +++ b/pkg/services/alerting/conditions/reducer.go @@ -108,9 +108,9 @@ func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) null.Float { break } } - // get other points + // get the oldest point points = points[0:i] - for i := len(points) - 1; i >= 0; i-- { + for i := 0; i < len(points); i++ { if points[i][0].Valid { allNull = false value = first - points[i][0].Float64 @@ -131,9 +131,9 @@ func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) null.Float { break } } - // get other points + // get the oldest point points = points[0:i] - for i := len(points) - 1; i >= 0; i-- { + for i := 0; i < len(points); i++ { if points[i][0].Valid { allNull = false val := (first - points[i][0].Float64) / points[i][0].Float64 * 100 diff --git a/pkg/services/alerting/conditions/reducer_test.go b/pkg/services/alerting/conditions/reducer_test.go index 866b574f59f..9d4e1462690 100644 --- a/pkg/services/alerting/conditions/reducer_test.go +++ b/pkg/services/alerting/conditions/reducer_test.go @@ -110,16 +110,35 @@ func TestSimpleReducer(t *testing.T) { So(reducer.Reduce(series).Float64, ShouldEqual, float64(3)) }) - Convey("diff", func() { + Convey("diff one point", func() { + result := testReducer("diff", 30) + So(result, ShouldEqual, float64(0)) + }) + + Convey("diff two points", func() { result := testReducer("diff", 30, 40) So(result, ShouldEqual, float64(10)) }) - Convey("percent_diff", func() { + Convey("diff three points", func() { + result := testReducer("diff", 30, 40, 40) + So(result, ShouldEqual, float64(10)) + }) + + Convey("percent_diff one point", func() { + result := testReducer("percent_diff", 40) + So(result, ShouldEqual, float64(0)) + }) + + Convey("percent_diff two points", func() { result := testReducer("percent_diff", 30, 40) So(result, ShouldEqual, float64(33.33333333333333)) }) + Convey("percent_diff three points", func() { + result := testReducer("percent_diff", 30, 40, 40) + So(result, ShouldEqual, float64(33.33333333333333)) + }) }) } diff --git a/pkg/services/notifications/notifications.go b/pkg/services/notifications/notifications.go index fcefa91243d..769fdd06fd0 100644 --- a/pkg/services/notifications/notifications.go +++ b/pkg/services/notifications/notifications.go @@ -98,8 +98,6 @@ func (ns *NotificationService) Run(ctx context.Context) error { return ctx.Err() } } - - return nil } func (ns *NotificationService) SendWebhookSync(ctx context.Context, cmd *m.SendWebhookSync) error { diff --git a/pkg/services/rendering/phantomjs.go b/pkg/services/rendering/phantomjs.go index 8e06b5fed9d..87ccaf6b5d2 100644 --- a/pkg/services/rendering/phantomjs.go +++ b/pkg/services/rendering/phantomjs.go @@ -58,7 +58,9 @@ func (rs *RenderingService) renderViaPhantomJS(ctx context.Context, opts Opts) ( cmdArgs = append([]string{fmt.Sprintf("--output-encoding=%s", opts.Encoding)}, cmdArgs...) } - commandCtx, _ := context.WithTimeout(ctx, opts.Timeout+time.Second*2) + commandCtx, cancel := context.WithTimeout(ctx, opts.Timeout+time.Second*2) + defer cancel() + cmd := exec.CommandContext(commandCtx, binPath, cmdArgs...) cmd.Stderr = cmd.Stdout diff --git a/pkg/tsdb/elasticsearch/client/client.go b/pkg/tsdb/elasticsearch/client/client.go index efc3ed1bda2..dff626a79eb 100644 --- a/pkg/tsdb/elasticsearch/client/client.go +++ b/pkg/tsdb/elasticsearch/client/client.go @@ -218,7 +218,7 @@ func (c *baseClientImpl) ExecuteMultisearch(r *MultiSearchRequest) (*MultiSearch elapsed := time.Now().Sub(start) clientLog.Debug("Decoded multisearch json response", "took", elapsed) - msr.status = res.StatusCode + msr.Status = res.StatusCode return &msr, nil } diff --git a/pkg/tsdb/elasticsearch/client/models.go b/pkg/tsdb/elasticsearch/client/models.go index a5810a9b109..a0d257d01a6 100644 --- a/pkg/tsdb/elasticsearch/client/models.go +++ b/pkg/tsdb/elasticsearch/client/models.go @@ -74,7 +74,7 @@ type MultiSearchRequest struct { // MultiSearchResponse represents a multi search response type MultiSearchResponse struct { - status int `json:"status,omitempty"` + Status int `json:"status,omitempty"` Responses []*SearchResponse `json:"responses"` } diff --git a/public/app/containers/Explore/QueryField.tsx b/public/app/containers/Explore/QueryField.tsx index 53354584fea..bedb955b9b9 100644 --- a/public/app/containers/Explore/QueryField.tsx +++ b/public/app/containers/Explore/QueryField.tsx @@ -9,7 +9,7 @@ import { getNextCharacter, getPreviousCousin } from './utils/dom'; import BracesPlugin from './slate-plugins/braces'; import ClearPlugin from './slate-plugins/clear'; import NewlinePlugin from './slate-plugins/newline'; -import PluginPrism, { configurePrismMetricsTokens } from './slate-plugins/prism/index'; +import PluginPrism, { setPrismTokens } from './slate-plugins/prism/index'; import RunnerPlugin from './slate-plugins/runner'; import debounce from './utils/debounce'; import { processLabels, RATE_RANGES, cleanText } from './utils/prometheus'; @@ -17,13 +17,13 @@ import { processLabels, RATE_RANGES, cleanText } from './utils/prometheus'; import Typeahead from './Typeahead'; const EMPTY_METRIC = ''; -const TYPEAHEAD_DEBOUNCE = 300; +export const TYPEAHEAD_DEBOUNCE = 300; function flattenSuggestions(s) { return s ? s.reduce((acc, g) => acc.concat(g.items), []) : []; } -const getInitialValue = query => +export const getInitialValue = query => Value.fromJSON({ document: { nodes: [ @@ -45,12 +45,14 @@ const getInitialValue = query => }, }); -class Portal extends React.Component { +class Portal extends React.Component { node: any; + constructor(props) { super(props); + const { index = 0, prefix = 'query' } = props; this.node = document.createElement('div'); - this.node.classList.add('explore-typeahead', `explore-typeahead-${props.index}`); + this.node.classList.add(`slate-typeahead`, `slate-typeahead-${prefix}-${index}`); document.body.appendChild(this.node); } @@ -71,12 +73,14 @@ class QueryField extends React.Component { constructor(props, context) { super(props, context); + const { prismDefinition = {}, prismLanguage = 'promql' } = props; + this.plugins = [ BracesPlugin(), ClearPlugin(), RunnerPlugin({ handler: props.onPressEnter }), NewlinePlugin(), - PluginPrism(), + PluginPrism({ definition: prismDefinition, language: prismLanguage }), ]; this.state = { @@ -131,7 +135,8 @@ class QueryField extends React.Component { if (!this.state.metrics) { return; } - configurePrismMetricsTokens(this.state.metrics); + setPrismTokens(this.props.prismLanguage, 'metrics', this.state.metrics); + // Trigger re-render window.requestAnimationFrame(() => { // Bogus edit to trigger highlighting @@ -162,7 +167,7 @@ class QueryField extends React.Component { const selection = window.getSelection(); if (selection.anchorNode) { const wrapperNode = selection.anchorNode.parentElement; - const editorNode = wrapperNode.closest('.query-field'); + const editorNode = wrapperNode.closest('.slate-query-field'); if (!editorNode || this.state.value.isBlurred) { // Not inside this editor return; @@ -330,20 +335,30 @@ class QueryField extends React.Component { } onKeyDown = (event, change) => { - if (this.menuEl) { - const { typeaheadIndex, suggestions } = this.state; + const { typeaheadIndex, suggestions } = this.state; - switch (event.key) { - case 'Escape': { - if (this.menuEl) { - event.preventDefault(); - this.resetTypeahead(); - return true; - } - break; + switch (event.key) { + case 'Escape': { + if (this.menuEl) { + event.preventDefault(); + event.stopPropagation(); + this.resetTypeahead(); + return true; } + break; + } - case 'Tab': { + case ' ': { + if (event.ctrlKey) { + event.preventDefault(); + this.handleTypeahead(); + return true; + } + break; + } + + case 'Tab': { + if (this.menuEl) { // Dont blur input event.preventDefault(); if (!suggestions || suggestions.length === 0) { @@ -359,25 +374,30 @@ class QueryField extends React.Component { this.applyTypeahead(change, suggestion); return true; } + break; + } - case 'ArrowDown': { + case 'ArrowDown': { + if (this.menuEl) { // Select next suggestion event.preventDefault(); this.setState({ typeaheadIndex: typeaheadIndex + 1 }); - break; } + break; + } - case 'ArrowUp': { + case 'ArrowUp': { + if (this.menuEl) { // Select previous suggestion event.preventDefault(); this.setState({ typeaheadIndex: Math.max(0, typeaheadIndex - 1) }); - break; } + break; + } - default: { - // console.log('default key', event.key, event.which, event.charCode, event.locale, data.key); - break; - } + default: { + // console.log('default key', event.key, event.which, event.charCode, event.locale, data.key); + break; } } return undefined; @@ -502,10 +522,17 @@ class QueryField extends React.Component { // Align menu overlay to editor node if (node) { + // Read from DOM const rect = node.parentElement.getBoundingClientRect(); - menu.style.opacity = 1; - menu.style.top = `${rect.top + window.scrollY + rect.height + 4}px`; - menu.style.left = `${rect.left + window.scrollX - 2}px`; + const scrollX = window.scrollX; + const scrollY = window.scrollY; + + // Write DOM + requestAnimationFrame(() => { + menu.style.opacity = 1; + menu.style.top = `${rect.top + scrollY + rect.height + 4}px`; + menu.style.left = `${rect.left + scrollX - 2}px`; + }); } }; @@ -514,6 +541,7 @@ class QueryField extends React.Component { }; renderMenu = () => { + const { portalPrefix } = this.props; const { suggestions } = this.state; const hasSuggesstions = suggestions && suggestions.length > 0; if (!hasSuggesstions) { @@ -524,11 +552,13 @@ class QueryField extends React.Component { let selectedIndex = Math.max(this.state.typeaheadIndex, 0); const flattenedSuggestions = flattenSuggestions(suggestions); selectedIndex = selectedIndex % flattenedSuggestions.length || 0; - const selectedKeys = flattenedSuggestions.length > 0 ? [flattenedSuggestions[selectedIndex]] : []; + const selectedKeys = (flattenedSuggestions.length > 0 ? [flattenedSuggestions[selectedIndex]] : []).map( + i => (typeof i === 'object' ? i.text : i) + ); // Create typeahead in DOM root so we can later position it absolutely return ( - + { render() { return ( -
+
{this.renderMenu()} { @@ -55,12 +56,15 @@ class QueryRow extends PureComponent {
-
+
diff --git a/public/app/containers/Explore/Typeahead.tsx b/public/app/containers/Explore/Typeahead.tsx index 4943622fe4e..44fce7f8c7e 100644 --- a/public/app/containers/Explore/Typeahead.tsx +++ b/public/app/containers/Explore/Typeahead.tsx @@ -23,12 +23,13 @@ class TypeaheadItem extends React.PureComponent { }; render() { - const { isSelected, label, onClickItem } = this.props; + const { hint, isSelected, label, onClickItem } = this.props; const className = isSelected ? 'typeahead-item typeahead-item__selected' : 'typeahead-item'; const onClick = () => onClickItem(label); return (
  • {label} + {hint && isSelected ?
    {hint}
    : null}
  • ); } @@ -41,9 +42,19 @@ class TypeaheadGroup extends React.PureComponent {
  • {label}
      - {items.map(item => ( - -1} label={item} /> - ))} + {items.map(item => { + const text = typeof item === 'object' ? item.text : item; + const label = typeof item === 'object' ? item.display || item.text : item; + return ( + -1} + hint={item.hint} + label={label} + /> + ); + })}
  • ); diff --git a/public/app/containers/Explore/slate-plugins/prism/index.tsx b/public/app/containers/Explore/slate-plugins/prism/index.tsx index 7c3fa296d8e..d185518790f 100644 --- a/public/app/containers/Explore/slate-plugins/prism/index.tsx +++ b/public/app/containers/Explore/slate-plugins/prism/index.tsx @@ -1,16 +1,12 @@ import React from 'react'; import Prism from 'prismjs'; -import Promql from './promql'; - -Prism.languages.promql = Promql; - const TOKEN_MARK = 'prism-token'; -export function configurePrismMetricsTokens(metrics) { - Prism.languages.promql.metric = { - alias: 'variable', - pattern: new RegExp(`(?:^|\\s)(${metrics.join('|')})(?:$|\\s)`), +export function setPrismTokens(language, field, values, alias = 'variable') { + Prism.languages[language][field] = { + alias, + pattern: new RegExp(`(?:^|\\s)(${values.join('|')})(?:$|\\s)`), }; } @@ -21,7 +17,12 @@ export function configurePrismMetricsTokens(metrics) { * (Adapted to handle nested grammar definitions.) */ -export default function PrismPlugin() { +export default function PrismPlugin({ definition, language }) { + if (definition) { + // Don't override exising modified definitions + Prism.languages[language] = Prism.languages[language] || definition; + } + return { /** * Render a Slate mark with appropiate CSS class names @@ -54,7 +55,7 @@ export default function PrismPlugin() { const texts = node.getTexts().toArray(); const tstring = texts.map(t => t.text).join('\n'); - const grammar = Prism.languages.promql; + const grammar = Prism.languages[language]; const tokens = Prism.tokenize(tstring, grammar); const decorations = []; let startText = texts.shift(); diff --git a/public/app/plugins/datasource/cloudwatch/datasource.ts b/public/app/plugins/datasource/cloudwatch/datasource.ts index 4101759ec1d..391f65bd7ae 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.ts +++ b/public/app/plugins/datasource/cloudwatch/datasource.ts @@ -404,6 +404,7 @@ export default class CloudWatchDatasource { } expandTemplateVariable(targets, scopedVars, templateSrv) { + // Datasource and template srv logic uber-complected. This should be cleaned up. return _.chain(targets) .map(target => { var dimensionKey = _.findKey(target.dimensions, v => { diff --git a/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts b/public/app/plugins/datasource/cloudwatch/specs/datasource.jest.ts similarity index 59% rename from public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts rename to public/app/plugins/datasource/cloudwatch/specs/datasource.jest.ts index b2c4950d3ce..2dc6e57b1aa 100644 --- a/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/cloudwatch/specs/datasource.jest.ts @@ -1,32 +1,38 @@ import '../datasource'; -import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common'; -import helpers from 'test/specs/helpers'; import CloudWatchDatasource from '../datasource'; -import 'app/features/dashboard/time_srv'; +import * as dateMath from 'app/core/utils/datemath'; +import _ from 'lodash'; describe('CloudWatchDatasource', function() { - var ctx = new helpers.ServiceTestContext(); - var instanceSettings = { + let instanceSettings = { jsonData: { defaultRegion: 'us-east-1', access: 'proxy' }, }; - beforeEach(angularMocks.module('grafana.core')); - beforeEach(angularMocks.module('grafana.services')); - beforeEach(angularMocks.module('grafana.controllers')); - beforeEach(ctx.providePhase(['templateSrv', 'backendSrv'])); - beforeEach(ctx.createService('timeSrv')); + let templateSrv = { + data: {}, + templateSettings: { interpolate: /\[\[([\s\S]+?)\]\]/g }, + replace: text => _.template(text, templateSrv.templateSettings)(templateSrv.data), + variableExists: () => false, + }; - beforeEach( - angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) { - ctx.$q = $q; - ctx.$httpBackend = $httpBackend; - ctx.$rootScope = $rootScope; - ctx.ds = $injector.instantiate(CloudWatchDatasource, { - instanceSettings: instanceSettings, - }); - $httpBackend.when('GET', /\.html$/).respond(''); - }) - ); + let timeSrv = { + time: { from: 'now-1h', to: 'now' }, + timeRange: () => { + return { + from: dateMath.parse(timeSrv.time.from, false), + to: dateMath.parse(timeSrv.time.to, true), + }; + }, + }; + let backendSrv = {}; + let ctx = { + backendSrv, + templateSrv, + }; + + beforeEach(() => { + ctx.ds = new CloudWatchDatasource(instanceSettings, {}, backendSrv, templateSrv, timeSrv); + }); describe('When performing CloudWatch query', function() { var requestParams; @@ -67,24 +73,23 @@ describe('CloudWatchDatasource', function() { }, }; - beforeEach(function() { - ctx.backendSrv.datasourceRequest = function(params) { + beforeEach(() => { + ctx.backendSrv.datasourceRequest = jest.fn(params => { requestParams = params.data; - return ctx.$q.when({ data: response }); - }; + return Promise.resolve({ data: response }); + }); }); it('should generate the correct query', function(done) { ctx.ds.query(query).then(function() { var params = requestParams.queries[0]; - expect(params.namespace).to.be(query.targets[0].namespace); - expect(params.metricName).to.be(query.targets[0].metricName); - expect(params.dimensions['InstanceId']).to.be('i-12345678'); - expect(params.statistics).to.eql(query.targets[0].statistics); - expect(params.period).to.be(query.targets[0].period); + expect(params.namespace).toBe(query.targets[0].namespace); + expect(params.metricName).toBe(query.targets[0].metricName); + expect(params.dimensions['InstanceId']).toBe('i-12345678'); + expect(params.statistics).toEqual(query.targets[0].statistics); + expect(params.period).toBe(query.targets[0].period); done(); }); - ctx.$rootScope.$apply(); }); it('should generate the correct query with interval variable', function(done) { @@ -111,116 +116,17 @@ describe('CloudWatchDatasource', function() { ctx.ds.query(query).then(function() { var params = requestParams.queries[0]; - expect(params.period).to.be('600'); + expect(params.period).toBe('600'); done(); }); - ctx.$rootScope.$apply(); }); it('should return series list', function(done) { ctx.ds.query(query).then(function(result) { - expect(result.data[0].target).to.be(response.results.A.series[0].name); - expect(result.data[0].datapoints[0][0]).to.be(response.results.A.series[0].points[0][0]); + expect(result.data[0].target).toBe(response.results.A.series[0].name); + expect(result.data[0].datapoints[0][0]).toBe(response.results.A.series[0].points[0][0]); done(); }); - ctx.$rootScope.$apply(); - }); - - it('should generate the correct targets by expanding template variables', function() { - var templateSrv = { - variables: [ - { - name: 'instance_id', - options: [ - { text: 'i-23456789', value: 'i-23456789', selected: false }, - { text: 'i-34567890', value: 'i-34567890', selected: true }, - ], - current: { - text: 'i-34567890', - value: 'i-34567890', - }, - }, - ], - replace: function(target, scopedVars) { - if (target === '$instance_id' && scopedVars['instance_id']['text'] === 'i-34567890') { - return 'i-34567890'; - } else { - return ''; - } - }, - getVariableName: function(e) { - return 'instance_id'; - }, - variableExists: function(e) { - return true; - }, - containsVariable: function(str, variableName) { - return str.indexOf('$' + variableName) !== -1; - }, - }; - - var targets = [ - { - region: 'us-east-1', - namespace: 'AWS/EC2', - metricName: 'CPUUtilization', - dimensions: { - InstanceId: '$instance_id', - }, - statistics: ['Average'], - period: 300, - }, - ]; - - var result = ctx.ds.expandTemplateVariable(targets, {}, templateSrv); - expect(result[0].dimensions.InstanceId).to.be('i-34567890'); - }); - - it('should generate the correct targets by expanding template variables from url', function() { - var templateSrv = { - variables: [ - { - name: 'instance_id', - options: [ - { text: 'i-23456789', value: 'i-23456789', selected: false }, - { text: 'i-34567890', value: 'i-34567890', selected: false }, - ], - current: 'i-45678901', - }, - ], - replace: function(target, scopedVars) { - if (target === '$instance_id') { - return 'i-45678901'; - } else { - return ''; - } - }, - getVariableName: function(e) { - return 'instance_id'; - }, - variableExists: function(e) { - return true; - }, - containsVariable: function(str, variableName) { - return str.indexOf('$' + variableName) !== -1; - }, - }; - - var targets = [ - { - region: 'us-east-1', - namespace: 'AWS/EC2', - metricName: 'CPUUtilization', - dimensions: { - InstanceId: '$instance_id', - }, - statistics: ['Average'], - period: 300, - }, - ]; - - var result = ctx.ds.expandTemplateVariable(targets, {}, templateSrv); - expect(result[0].dimensions.InstanceId).to.be('i-45678901'); }); }); @@ -228,21 +134,21 @@ describe('CloudWatchDatasource', function() { it('should return the datasource region if empty or "default"', function() { var defaultRegion = instanceSettings.jsonData.defaultRegion; - expect(ctx.ds.getActualRegion()).to.be(defaultRegion); - expect(ctx.ds.getActualRegion('')).to.be(defaultRegion); - expect(ctx.ds.getActualRegion('default')).to.be(defaultRegion); + expect(ctx.ds.getActualRegion()).toBe(defaultRegion); + expect(ctx.ds.getActualRegion('')).toBe(defaultRegion); + expect(ctx.ds.getActualRegion('default')).toBe(defaultRegion); }); it('should return the specified region if specified', function() { - expect(ctx.ds.getActualRegion('some-fake-region-1')).to.be('some-fake-region-1'); + expect(ctx.ds.getActualRegion('some-fake-region-1')).toBe('some-fake-region-1'); }); var requestParams; beforeEach(function() { - ctx.ds.performTimeSeriesQuery = function(request) { + ctx.ds.performTimeSeriesQuery = jest.fn(request => { requestParams = request; - return ctx.$q.when({ data: {} }); - }; + return Promise.resolve({ data: {} }); + }); }); it('should query for the datasource region if empty or "default"', function(done) { @@ -264,10 +170,9 @@ describe('CloudWatchDatasource', function() { }; ctx.ds.query(query).then(function(result) { - expect(requestParams.queries[0].region).to.be(instanceSettings.jsonData.defaultRegion); + expect(requestParams.queries[0].region).toBe(instanceSettings.jsonData.defaultRegion); done(); }); - ctx.$rootScope.$apply(); }); }); @@ -311,18 +216,17 @@ describe('CloudWatchDatasource', function() { }; beforeEach(function() { - ctx.backendSrv.datasourceRequest = function(params) { - return ctx.$q.when({ data: response }); - }; + ctx.backendSrv.datasourceRequest = jest.fn(params => { + return Promise.resolve({ data: response }); + }); }); it('should return series list', function(done) { ctx.ds.query(query).then(function(result) { - expect(result.data[0].target).to.be(response.results.A.series[0].name); - expect(result.data[0].datapoints[0][0]).to.be(response.results.A.series[0].points[0][0]); + expect(result.data[0].target).toBe(response.results.A.series[0].name); + expect(result.data[0].datapoints[0][0]).toBe(response.results.A.series[0].points[0][0]); done(); }); - ctx.$rootScope.$apply(); }); }); @@ -332,14 +236,13 @@ describe('CloudWatchDatasource', function() { scenario.setup = setupCallback => { beforeEach(() => { setupCallback(); - ctx.backendSrv.datasourceRequest = args => { + ctx.backendSrv.datasourceRequest = jest.fn(args => { scenario.request = args.data; - return ctx.$q.when({ data: scenario.requestResponse }); - }; + return Promise.resolve({ data: scenario.requestResponse }); + }); ctx.ds.metricFindQuery(query).then(args => { scenario.result = args; }); - ctx.$rootScope.$apply(); }); }; @@ -359,9 +262,9 @@ describe('CloudWatchDatasource', function() { }); it('should call __GetRegions and return result', () => { - expect(scenario.result[0].text).to.contain('us-east-1'); - expect(scenario.request.queries[0].type).to.be('metricFindQuery'); - expect(scenario.request.queries[0].subtype).to.be('regions'); + expect(scenario.result[0].text).toContain('us-east-1'); + expect(scenario.request.queries[0].type).toBe('metricFindQuery'); + expect(scenario.request.queries[0].subtype).toBe('regions'); }); }); @@ -377,9 +280,9 @@ describe('CloudWatchDatasource', function() { }); it('should call __GetNamespaces and return result', () => { - expect(scenario.result[0].text).to.contain('AWS/EC2'); - expect(scenario.request.queries[0].type).to.be('metricFindQuery'); - expect(scenario.request.queries[0].subtype).to.be('namespaces'); + expect(scenario.result[0].text).toContain('AWS/EC2'); + expect(scenario.request.queries[0].type).toBe('metricFindQuery'); + expect(scenario.request.queries[0].subtype).toBe('namespaces'); }); }); @@ -395,9 +298,9 @@ describe('CloudWatchDatasource', function() { }); it('should call __GetMetrics and return result', () => { - expect(scenario.result[0].text).to.be('CPUUtilization'); - expect(scenario.request.queries[0].type).to.be('metricFindQuery'); - expect(scenario.request.queries[0].subtype).to.be('metrics'); + expect(scenario.result[0].text).toBe('CPUUtilization'); + expect(scenario.request.queries[0].type).toBe('metricFindQuery'); + expect(scenario.request.queries[0].subtype).toBe('metrics'); }); }); @@ -413,9 +316,9 @@ describe('CloudWatchDatasource', function() { }); it('should call __GetDimensions and return result', () => { - expect(scenario.result[0].text).to.be('InstanceId'); - expect(scenario.request.queries[0].type).to.be('metricFindQuery'); - expect(scenario.request.queries[0].subtype).to.be('dimension_keys'); + expect(scenario.result[0].text).toBe('InstanceId'); + expect(scenario.request.queries[0].type).toBe('metricFindQuery'); + expect(scenario.request.queries[0].subtype).toBe('dimension_keys'); }); }); @@ -431,9 +334,9 @@ describe('CloudWatchDatasource', function() { }); it('should call __ListMetrics and return result', () => { - expect(scenario.result[0].text).to.contain('i-12345678'); - expect(scenario.request.queries[0].type).to.be('metricFindQuery'); - expect(scenario.request.queries[0].subtype).to.be('dimension_values'); + expect(scenario.result[0].text).toContain('i-12345678'); + expect(scenario.request.queries[0].type).toBe('metricFindQuery'); + expect(scenario.request.queries[0].subtype).toBe('dimension_values'); }); }); @@ -449,9 +352,9 @@ describe('CloudWatchDatasource', function() { }); it('should call __ListMetrics and return result', () => { - expect(scenario.result[0].text).to.contain('i-12345678'); - expect(scenario.request.queries[0].type).to.be('metricFindQuery'); - expect(scenario.request.queries[0].subtype).to.be('dimension_values'); + expect(scenario.result[0].text).toContain('i-12345678'); + expect(scenario.request.queries[0].type).toBe('metricFindQuery'); + expect(scenario.request.queries[0].subtype).toBe('dimension_values'); }); }); @@ -544,7 +447,7 @@ describe('CloudWatchDatasource', function() { let now = new Date(options.range.from.valueOf() + t[2] * 1000); let expected = t[3]; let actual = ctx.ds.getPeriod(target, options, now); - expect(actual).to.be(expected); + expect(actual).toBe(expected); } }); }); diff --git a/public/app/plugins/datasource/mysql/specs/datasource_specs.ts b/public/app/plugins/datasource/mysql/specs/datasource.jest.ts similarity index 66% rename from public/app/plugins/datasource/mysql/specs/datasource_specs.ts rename to public/app/plugins/datasource/mysql/specs/datasource.jest.ts index 0373797f211..be33f5f8858 100644 --- a/public/app/plugins/datasource/mysql/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/mysql/specs/datasource.jest.ts @@ -1,28 +1,21 @@ -import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common'; import moment from 'moment'; -import helpers from 'test/specs/helpers'; import { MysqlDatasource } from '../datasource'; import { CustomVariable } from 'app/features/templating/custom_variable'; describe('MySQLDatasource', function() { - var ctx = new helpers.ServiceTestContext(); - var instanceSettings = { name: 'mysql' }; + let instanceSettings = { name: 'mysql' }; + let backendSrv = {}; + let templateSrv = { + replace: jest.fn(text => text), + }; - beforeEach(angularMocks.module('grafana.core')); - beforeEach(angularMocks.module('grafana.services')); - beforeEach(ctx.providePhase(['backendSrv'])); + let ctx = { + backendSrv, + }; - beforeEach( - angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) { - ctx.$q = $q; - ctx.$httpBackend = $httpBackend; - ctx.$rootScope = $rootScope; - ctx.ds = $injector.instantiate(MysqlDatasource, { - instanceSettings: instanceSettings, - }); - $httpBackend.when('GET', /\.html$/).respond(''); - }) - ); + beforeEach(() => { + ctx.ds = new MysqlDatasource(instanceSettings, backendSrv, {}, templateSrv); + }); describe('When performing annotationQuery', function() { let results; @@ -59,26 +52,25 @@ describe('MySQLDatasource', function() { }; beforeEach(function() { - ctx.backendSrv.datasourceRequest = function(options) { - return ctx.$q.when({ data: response, status: 200 }); - }; + ctx.backendSrv.datasourceRequest = jest.fn(options => { + return Promise.resolve({ data: response, status: 200 }); + }); ctx.ds.annotationQuery(options).then(function(data) { results = data; }); - ctx.$rootScope.$apply(); }); it('should return annotation list', function() { - expect(results.length).to.be(3); + expect(results.length).toBe(3); - expect(results[0].text).to.be('some text'); - expect(results[0].tags[0]).to.be('TagA'); - expect(results[0].tags[1]).to.be('TagB'); + expect(results[0].text).toBe('some text'); + expect(results[0].tags[0]).toBe('TagA'); + expect(results[0].tags[1]).toBe('TagB'); - expect(results[1].tags[0]).to.be('TagB'); - expect(results[1].tags[1]).to.be('TagC'); + expect(results[1].tags[0]).toBe('TagB'); + expect(results[1].tags[1]).toBe('TagC'); - expect(results[2].tags.length).to.be(0); + expect(results[2].tags.length).toBe(0); }); }); @@ -103,19 +95,18 @@ describe('MySQLDatasource', function() { }; beforeEach(function() { - ctx.backendSrv.datasourceRequest = function(options) { - return ctx.$q.when({ data: response, status: 200 }); - }; + ctx.backendSrv.datasourceRequest = jest.fn(options => { + return Promise.resolve({ data: response, status: 200 }); + }); ctx.ds.metricFindQuery(query).then(function(data) { results = data; }); - ctx.$rootScope.$apply(); }); it('should return list of all column values', function() { - expect(results.length).to.be(6); - expect(results[0].text).to.be('aTitle'); - expect(results[5].text).to.be('some text3'); + expect(results.length).toBe(6); + expect(results[0].text).toBe('aTitle'); + expect(results[5].text).toBe('some text3'); }); }); @@ -140,21 +131,20 @@ describe('MySQLDatasource', function() { }; beforeEach(function() { - ctx.backendSrv.datasourceRequest = function(options) { - return ctx.$q.when({ data: response, status: 200 }); - }; + ctx.backendSrv.datasourceRequest = jest.fn(options => { + return Promise.resolve({ data: response, status: 200 }); + }); ctx.ds.metricFindQuery(query).then(function(data) { results = data; }); - ctx.$rootScope.$apply(); }); it('should return list of as text, value', function() { - expect(results.length).to.be(3); - expect(results[0].text).to.be('aTitle'); - expect(results[0].value).to.be('value1'); - expect(results[2].text).to.be('aTitle3'); - expect(results[2].value).to.be('value3'); + expect(results.length).toBe(3); + expect(results[0].text).toBe('aTitle'); + expect(results[0].value).toBe('value1'); + expect(results[2].text).toBe('aTitle3'); + expect(results[2].value).toBe('value3'); }); }); @@ -179,19 +169,18 @@ describe('MySQLDatasource', function() { }; beforeEach(function() { - ctx.backendSrv.datasourceRequest = function(options) { - return ctx.$q.when({ data: response, status: 200 }); - }; + ctx.backendSrv.datasourceRequest = jest.fn(options => { + return Promise.resolve({ data: response, status: 200 }); + }); ctx.ds.metricFindQuery(query).then(function(data) { results = data; }); - ctx.$rootScope.$apply(); }); it('should return list of unique keys', function() { - expect(results.length).to.be(1); - expect(results[0].text).to.be('aTitle'); - expect(results[0].value).to.be('same'); + expect(results.length).toBe(1); + expect(results[0].text).toBe('aTitle'); + expect(results[0].value).toBe('same'); }); }); @@ -202,33 +191,33 @@ describe('MySQLDatasource', function() { describe('and value is a string', () => { it('should return an unquoted value', () => { - expect(ctx.ds.interpolateVariable('abc', ctx.variable)).to.eql('abc'); + expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual('abc'); }); }); describe('and value is a number', () => { it('should return an unquoted value', () => { - expect(ctx.ds.interpolateVariable(1000, ctx.variable)).to.eql(1000); + expect(ctx.ds.interpolateVariable(1000, ctx.variable)).toEqual(1000); }); }); describe('and value is an array of strings', () => { it('should return comma separated quoted values', () => { - expect(ctx.ds.interpolateVariable(['a', 'b', 'c'], ctx.variable)).to.eql("'a','b','c'"); + expect(ctx.ds.interpolateVariable(['a', 'b', 'c'], ctx.variable)).toEqual("'a','b','c'"); }); }); describe('and variable allows multi-value and value is a string', () => { it('should return a quoted value', () => { ctx.variable.multi = true; - expect(ctx.ds.interpolateVariable('abc', ctx.variable)).to.eql("'abc'"); + expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'"); }); }); describe('and variable allows all and value is a string', () => { it('should return a quoted value', () => { ctx.variable.includeAll = true; - expect(ctx.ds.interpolateVariable('abc', ctx.variable)).to.eql("'abc'"); + expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'"); }); }); }); diff --git a/public/app/plugins/datasource/postgres/specs/datasource_specs.ts b/public/app/plugins/datasource/postgres/specs/datasource.jest.ts similarity index 66% rename from public/app/plugins/datasource/postgres/specs/datasource_specs.ts rename to public/app/plugins/datasource/postgres/specs/datasource.jest.ts index f17ad019f26..107cd76e6c5 100644 --- a/public/app/plugins/datasource/postgres/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/postgres/specs/datasource.jest.ts @@ -1,28 +1,21 @@ -import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common'; import moment from 'moment'; -import helpers from 'test/specs/helpers'; import { PostgresDatasource } from '../datasource'; import { CustomVariable } from 'app/features/templating/custom_variable'; describe('PostgreSQLDatasource', function() { - var ctx = new helpers.ServiceTestContext(); - var instanceSettings = { name: 'postgresql' }; + let instanceSettings = { name: 'postgresql' }; - beforeEach(angularMocks.module('grafana.core')); - beforeEach(angularMocks.module('grafana.services')); - beforeEach(ctx.providePhase(['backendSrv'])); + let backendSrv = {}; + let templateSrv = { + replace: jest.fn(text => text), + }; + let ctx = { + backendSrv, + }; - beforeEach( - angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) { - ctx.$q = $q; - ctx.$httpBackend = $httpBackend; - ctx.$rootScope = $rootScope; - ctx.ds = $injector.instantiate(PostgresDatasource, { - instanceSettings: instanceSettings, - }); - $httpBackend.when('GET', /\.html$/).respond(''); - }) - ); + beforeEach(() => { + ctx.ds = new PostgresDatasource(instanceSettings, backendSrv, {}, templateSrv); + }); describe('When performing annotationQuery', function() { let results; @@ -59,26 +52,25 @@ describe('PostgreSQLDatasource', function() { }; beforeEach(function() { - ctx.backendSrv.datasourceRequest = function(options) { - return ctx.$q.when({ data: response, status: 200 }); - }; + ctx.backendSrv.datasourceRequest = jest.fn(options => { + return Promise.resolve({ data: response, status: 200 }); + }); ctx.ds.annotationQuery(options).then(function(data) { results = data; }); - ctx.$rootScope.$apply(); }); it('should return annotation list', function() { - expect(results.length).to.be(3); + expect(results.length).toBe(3); - expect(results[0].text).to.be('some text'); - expect(results[0].tags[0]).to.be('TagA'); - expect(results[0].tags[1]).to.be('TagB'); + expect(results[0].text).toBe('some text'); + expect(results[0].tags[0]).toBe('TagA'); + expect(results[0].tags[1]).toBe('TagB'); - expect(results[1].tags[0]).to.be('TagB'); - expect(results[1].tags[1]).to.be('TagC'); + expect(results[1].tags[0]).toBe('TagB'); + expect(results[1].tags[1]).toBe('TagC'); - expect(results[2].tags.length).to.be(0); + expect(results[2].tags.length).toBe(0); }); }); @@ -103,19 +95,18 @@ describe('PostgreSQLDatasource', function() { }; beforeEach(function() { - ctx.backendSrv.datasourceRequest = function(options) { - return ctx.$q.when({ data: response, status: 200 }); - }; + ctx.backendSrv.datasourceRequest = jest.fn(options => { + return Promise.resolve({ data: response, status: 200 }); + }); ctx.ds.metricFindQuery(query).then(function(data) { results = data; }); - ctx.$rootScope.$apply(); }); it('should return list of all column values', function() { - expect(results.length).to.be(6); - expect(results[0].text).to.be('aTitle'); - expect(results[5].text).to.be('some text3'); + expect(results.length).toBe(6); + expect(results[0].text).toBe('aTitle'); + expect(results[5].text).toBe('some text3'); }); }); @@ -140,21 +131,20 @@ describe('PostgreSQLDatasource', function() { }; beforeEach(function() { - ctx.backendSrv.datasourceRequest = function(options) { - return ctx.$q.when({ data: response, status: 200 }); - }; + ctx.backendSrv.datasourceRequest = jest.fn(options => { + return Promise.resolve({ data: response, status: 200 }); + }); ctx.ds.metricFindQuery(query).then(function(data) { results = data; }); - ctx.$rootScope.$apply(); }); it('should return list of as text, value', function() { - expect(results.length).to.be(3); - expect(results[0].text).to.be('aTitle'); - expect(results[0].value).to.be('value1'); - expect(results[2].text).to.be('aTitle3'); - expect(results[2].value).to.be('value3'); + expect(results.length).toBe(3); + expect(results[0].text).toBe('aTitle'); + expect(results[0].value).toBe('value1'); + expect(results[2].text).toBe('aTitle3'); + expect(results[2].value).toBe('value3'); }); }); @@ -178,20 +168,20 @@ describe('PostgreSQLDatasource', function() { }, }; - beforeEach(function() { - ctx.backendSrv.datasourceRequest = function(options) { - return ctx.$q.when({ data: response, status: 200 }); - }; + beforeEach(() => { + ctx.backendSrv.datasourceRequest = jest.fn(options => { + return Promise.resolve({ data: response, status: 200 }); + }); ctx.ds.metricFindQuery(query).then(function(data) { results = data; }); - ctx.$rootScope.$apply(); + //ctx.$rootScope.$apply(); }); it('should return list of unique keys', function() { - expect(results.length).to.be(1); - expect(results[0].text).to.be('aTitle'); - expect(results[0].value).to.be('same'); + expect(results.length).toBe(1); + expect(results[0].text).toBe('aTitle'); + expect(results[0].value).toBe('same'); }); }); @@ -202,33 +192,33 @@ describe('PostgreSQLDatasource', function() { describe('and value is a string', () => { it('should return an unquoted value', () => { - expect(ctx.ds.interpolateVariable('abc', ctx.variable)).to.eql('abc'); + expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual('abc'); }); }); describe('and value is a number', () => { it('should return an unquoted value', () => { - expect(ctx.ds.interpolateVariable(1000, ctx.variable)).to.eql(1000); + expect(ctx.ds.interpolateVariable(1000, ctx.variable)).toEqual(1000); }); }); describe('and value is an array of strings', () => { it('should return comma separated quoted values', () => { - expect(ctx.ds.interpolateVariable(['a', 'b', 'c'], ctx.variable)).to.eql("'a','b','c'"); + expect(ctx.ds.interpolateVariable(['a', 'b', 'c'], ctx.variable)).toEqual("'a','b','c'"); }); }); describe('and variable allows multi-value and is a string', () => { it('should return a quoted value', () => { ctx.variable.multi = true; - expect(ctx.ds.interpolateVariable('abc', ctx.variable)).to.eql("'abc'"); + expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'"); }); }); describe('and variable allows all and is a string', () => { it('should return a quoted value', () => { ctx.variable.includeAll = true; - expect(ctx.ds.interpolateVariable('abc', ctx.variable)).to.eql("'abc'"); + expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'"); }); }); }); diff --git a/public/app/plugins/panel/heatmap/color_scale.ts b/public/app/plugins/panel/heatmap/color_scale.ts index 40828639547..3550c981db2 100644 --- a/public/app/plugins/panel/heatmap/color_scale.ts +++ b/public/app/plugins/panel/heatmap/color_scale.ts @@ -3,7 +3,7 @@ import * as d3ScaleChromatic from 'd3-scale-chromatic'; export function getColorScale(colorScheme: any, lightTheme: boolean, maxValue: number, minValue = 0): (d: any) => any { let colorInterpolator = d3ScaleChromatic[colorScheme.value]; - let colorScaleInverted = colorScheme.invert === 'always' || (colorScheme.invert === 'dark' && !lightTheme); + let colorScaleInverted = colorScheme.invert === 'always' || colorScheme.invert === (lightTheme ? 'light' : 'dark'); let start = colorScaleInverted ? maxValue : minValue; let end = colorScaleInverted ? minValue : maxValue; diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 31a5afa630e..1d35ff2ea84 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -76,6 +76,13 @@ let colorSchemes = [ { name: 'Reds', value: 'interpolateReds', invert: 'dark' }, // Sequential (Multi-Hue) + { name: 'Viridis', value: 'interpolateViridis', invert: 'light' }, + { name: 'Magma', value: 'interpolateMagma', invert: 'light' }, + { name: 'Inferno', value: 'interpolateInferno', invert: 'light' }, + { name: 'Plasma', value: 'interpolatePlasma', invert: 'light' }, + { name: 'Warm', value: 'interpolateWarm', invert: 'light' }, + { name: 'Cool', value: 'interpolateCool', invert: 'light' }, + { name: 'Cubehelix', value: 'interpolateCubehelixDefault', invert: 'light' }, { name: 'BuGn', value: 'interpolateBuGn', invert: 'dark' }, { name: 'BuPu', value: 'interpolateBuPu', invert: 'dark' }, { name: 'GnBu', value: 'interpolateGnBu', invert: 'dark' }, @@ -87,7 +94,7 @@ let colorSchemes = [ { name: 'YlGnBu', value: 'interpolateYlGnBu', invert: 'dark' }, { name: 'YlGn', value: 'interpolateYlGn', invert: 'dark' }, { name: 'YlOrBr', value: 'interpolateYlOrBr', invert: 'dark' }, - { name: 'YlOrRd', value: 'interpolateYlOrRd', invert: 'darm' }, + { name: 'YlOrRd', value: 'interpolateYlOrRd', invert: 'dark' }, ]; const ds_support_histogram_sort = ['prometheus', 'elasticsearch']; diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index afc869f8b15..9e3bec267ed 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -67,6 +67,7 @@ @import 'components/filter-list'; @import 'components/filter-table'; @import 'components/old_stuff'; +@import 'components/slate_editor'; @import 'components/typeahead'; @import 'components/modals'; @import 'components/dropdown'; diff --git a/public/sass/components/_slate_editor.scss b/public/sass/components/_slate_editor.scss new file mode 100644 index 00000000000..de8a6e6d721 --- /dev/null +++ b/public/sass/components/_slate_editor.scss @@ -0,0 +1,151 @@ +.slate-query-field { + font-size: $font-size-root; + font-family: $font-family-monospace; + height: auto; +} + +.slate-query-field-wrapper { + position: relative; + display: inline-block; + padding: 6px 7px 4px; + width: 100%; + cursor: text; + line-height: $line-height-base; + color: $text-color-weak; + background-color: $panel-bg; + background-image: none; + border: $panel-border; + border-radius: $border-radius; + transition: all 0.3s; +} + +.slate-typeahead { + .typeahead { + position: absolute; + z-index: auto; + top: -10000px; + left: -10000px; + opacity: 0; + border-radius: $border-radius; + transition: opacity 0.75s; + border: $panel-border; + max-height: calc(66vh); + overflow-y: scroll; + max-width: calc(66%); + overflow-x: hidden; + outline: none; + list-style: none; + background: $panel-bg; + color: $text-color; + transition: opacity 0.4s ease-out; + box-shadow: $typeahead-shadow; + } + + .typeahead-group__title { + color: $text-color-weak; + font-size: $font-size-sm; + line-height: $line-height-base; + padding: $input-padding-y $input-padding-x; + } + + .typeahead-item { + height: auto; + font-family: $font-family-monospace; + padding: $input-padding-y $input-padding-x; + padding-left: $input-padding-x-lg; + font-size: $font-size-sm; + text-overflow: ellipsis; + overflow: hidden; + z-index: 1; + display: block; + white-space: nowrap; + cursor: pointer; + transition: color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), + background 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), padding 0.15s cubic-bezier(0.645, 0.045, 0.355, 1); + } + + .typeahead-item__selected { + background-color: $typeahead-selected-bg; + color: $typeahead-selected-color; + + .typeahead-item-hint { + font-size: $font-size-xs; + color: $text-color; + } + } +} + +/* SYNTAX */ + +.slate-query-field { + .token.comment, + .token.block-comment, + .token.prolog, + .token.doctype, + .token.cdata { + color: $text-color-weak; + } + + .token.punctuation { + color: $text-color-weak; + } + + .token.property, + .token.tag, + .token.boolean, + .token.number, + .token.function-name, + .token.constant, + .token.symbol, + .token.deleted { + color: $query-red; + } + + .token.selector, + .token.attr-name, + .token.string, + .token.char, + .token.function, + .token.builtin, + .token.inserted { + color: $query-green; + } + + .token.operator, + .token.entity, + .token.url, + .token.variable { + color: $query-purple; + } + + .token.atrule, + .token.attr-value, + .token.keyword, + .token.class-name { + color: $query-blue; + } + + .token.regex, + .token.important { + color: $query-orange; + } + + .token.important { + font-weight: normal; + } + + .token.bold { + font-weight: bold; + } + .token.italic { + font-style: italic; + } + + .token.entity { + cursor: help; + } + + .namespace { + opacity: 0.7; + } +} diff --git a/public/sass/pages/_explore.scss b/public/sass/pages/_explore.scss index 7dacccf6a87..876260c4f76 100644 --- a/public/sass/pages/_explore.scss +++ b/public/sass/pages/_explore.scss @@ -93,150 +93,3 @@ .query-row-tools { width: 4rem; } - -.query-field { - font-size: $font-size-root; - font-family: $font-family-monospace; - height: auto; -} - -.query-field-wrapper { - position: relative; - display: inline-block; - padding: 6px 7px 4px; - width: 100%; - cursor: text; - line-height: $line-height-base; - color: $text-color-weak; - background-color: $panel-bg; - background-image: none; - border: $panel-border; - border-radius: $border-radius; - transition: all 0.3s; -} - -.explore-typeahead { - .typeahead { - position: absolute; - z-index: auto; - top: -10000px; - left: -10000px; - opacity: 0; - border-radius: $border-radius; - transition: opacity 0.75s; - border: $panel-border; - max-height: calc(66vh); - overflow-y: scroll; - max-width: calc(66%); - overflow-x: hidden; - outline: none; - list-style: none; - background: $panel-bg; - color: $text-color; - transition: opacity 0.4s ease-out; - box-shadow: $typeahead-shadow; - } - - .typeahead-group__title { - color: $text-color-weak; - font-size: $font-size-sm; - line-height: $line-height-base; - padding: $input-padding-y $input-padding-x; - } - - .typeahead-item { - height: auto; - font-family: $font-family-monospace; - padding: $input-padding-y $input-padding-x; - padding-left: $input-padding-x-lg; - font-size: $font-size-sm; - text-overflow: ellipsis; - overflow: hidden; - z-index: 1; - display: block; - white-space: nowrap; - cursor: pointer; - transition: color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), border-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), - background 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), padding 0.15s cubic-bezier(0.645, 0.045, 0.355, 1); - } - - .typeahead-item__selected { - background-color: $typeahead-selected-bg; - color: $typeahead-selected-color; - } -} - -/* SYNTAX */ - -.explore { - .token.comment, - .token.block-comment, - .token.prolog, - .token.doctype, - .token.cdata { - color: $text-color-weak; - } - - .token.punctuation { - color: $text-color-weak; - } - - .token.property, - .token.tag, - .token.boolean, - .token.number, - .token.function-name, - .token.constant, - .token.symbol, - .token.deleted { - color: $query-red; - } - - .token.selector, - .token.attr-name, - .token.string, - .token.char, - .token.function, - .token.builtin, - .token.inserted { - color: $query-green; - } - - .token.operator, - .token.entity, - .token.url, - .token.variable { - color: $query-purple; - } - - .token.atrule, - .token.attr-value, - .token.keyword, - .token.class-name { - color: $query-blue; - } - - .token.regex, - .token.important { - color: $query-orange; - } - - .token.important { - font-weight: normal; - } - - .token.bold { - font-weight: bold; - } - .token.italic { - font-style: italic; - } - - .token.entity { - cursor: help; - } - - .namespace { - opacity: 0.7; - } -} diff --git a/scripts/circle-test-backend.sh b/scripts/circle-test-backend.sh index a63d6354fa6..4740ef99f1a 100755 --- a/scripts/circle-test-backend.sh +++ b/scripts/circle-test-backend.sh @@ -13,9 +13,6 @@ function exit_if_fail { echo "running go fmt" exit_if_fail test -z "$(gofmt -s -l ./pkg | tee /dev/stderr)" -echo "running go vet" -exit_if_fail test -z "$(go vet ./pkg/... | tee /dev/stderr)" - echo "building backend with install to cache pkgs" exit_if_fail time go install ./pkg/cmd/grafana-server diff --git a/tslint.json b/tslint.json index e7a51295701..22e123e0364 100644 --- a/tslint.json +++ b/tslint.json @@ -2,7 +2,7 @@ "rules": { "no-string-throw": true, "no-unused-expression": true, - "no-unused-variable": false, + "no-unused-variable": false, "no-use-before-declare": false, "no-duplicate-variable": true, "curly": true,